├── README.md ├── book_src ├── bib │ ├── ode.bib │ ├── ode.pub │ ├── papers.bib │ ├── papers.pub │ ├── papers.pub.bak │ └── venues.list ├── book │ ├── clean.sh │ ├── clean_all.sh │ ├── make_html.sh │ ├── make_ipynb.sh │ ├── make_pdf.sh │ ├── ode_book.do.txt │ ├── svmonodo.cls │ └── t4do.sty └── chapters │ ├── .gitignore │ ├── adaptive.do.txt │ ├── chapter1.do.txt │ ├── clean.sh │ ├── diffeq.do.txt │ ├── disease_modeling.do.txt │ ├── figs_appA │ ├── comic_root.pdf │ ├── comic_root.png │ ├── diffeq_integral_of_Gaussian.pdf │ ├── diffeq_integral_of_Gaussian.png │ ├── logistic_diffeq.py │ ├── logistic_growth1.pdf │ └── logistic_growth1.png │ ├── figs_ch1 │ ├── FE_n_10_20.pdf │ ├── FE_n_10_20.png │ ├── logistic_func_mpl.pdf │ ├── logistic_func_mpl.png │ ├── pendulum.png │ ├── pendulum_FE.pdf │ ├── pendulum_scipy.pdf │ └── pendulum_sketch.pptx │ ├── figs_ch2 │ └── ch2_simplecompare.pdf │ ├── figs_ch3 │ ├── stab_region_erk.pdf │ ├── stab_region_erk.png │ ├── stab_region_irk0.pdf │ ├── stab_region_radau.pdf │ ├── vanderpol1.pdf │ ├── vanderpol2.pdf │ ├── vanderpol_be.pdf │ └── vanderpol_irk.pdf │ ├── figs_ch4 │ ├── hh_adaptive_tr_bdf2.pdf │ └── hodgkinhuxley_AP.pdf │ ├── figs_ch5 │ ├── SEEIIR.pdf │ ├── SEEIIR.png │ ├── SEIR.pdf │ ├── SEIR.png │ ├── SIR0.pdf │ ├── SIR0.png │ ├── SIR1.pdf │ ├── SIR1.png │ ├── SIR_immunity_loss.pdf │ └── SIR_simple.pdf │ ├── mako │ ├── .dict4spell.txt │ ├── main_mako.do.txt │ ├── make.sh │ ├── make_all.sh │ ├── mako.do.txt │ └── src-mako │ │ └── mako_func.py │ ├── mako_code.txt │ ├── preface_ode.do.txt │ ├── rungekutta1.do.txt │ └── rungekutta2.do.txt └── docs ├── README.md ├── _config.yml ├── figs └── ode_cover.jpg ├── ipynb.zip ├── ipynb ├── ODESolver.py ├── adaptive.ipynb ├── diffeq.ipynb ├── disease_modeling.ipynb ├── ode_intro.ipynb ├── ode_systems.ipynb ├── rungekutta1.ipynb └── rungekutta2.ipynb ├── ode_book.pdf ├── src.zip └── src ├── appendix1 ├── Newton.py ├── fibonacci_diffeq.py ├── fortune_inflation.py ├── interest_v1.py ├── interest_v2.py ├── logistic_diffeq.py ├── lotke_volterra.py └── taylor_exp_diffeq.py ├── chapter1 ├── check_convergence_fe.py ├── forward_euler_class.py ├── forward_euler_class_scalar.py ├── forward_euler_fun.py ├── pendulum.py └── pendulum_scipy.py ├── chapter2 ├── ODESolver.py ├── check_convergence_erk.py └── simple_compare.py ├── chapter3 ├── ImplicitRK.py ├── ImplicitRK_v0.py ├── ODESolver.py ├── check_convergence_irk.py ├── vanderpol.py ├── vanderpol_euler_methods.py ├── vanderpol_figure_irk.py └── vanderpol_mu0_1_5.py ├── chapter4 ├── AdaptiveImplicitRK.py ├── AdaptiveODESolver.py ├── ImplicitRK.py ├── ODESolver.py ├── check_error_adaptive_hh.py └── hodgkinhuxley.py └── chapter5 ├── ODESolver.py ├── SEEIIR.py ├── SEIR.py ├── SIR_class_v1.py ├── SIR_class_v2.py └── SIR_fun.py /README.md: -------------------------------------------------------------------------------- 1 | # solving_odes_in_python 2 | Lecture notes used in the last part of the IN1900 course, on programming ODE solvers in Python. 3 | -------------------------------------------------------------------------------- /book_src/bib/ode.bib: -------------------------------------------------------------------------------- 1 | @Inbook{Greenwood2009, 2 | author="Greenwood, Priscilla E. 3 | and Gordillo, Luis F.", 4 | editor="Chowell, Gerardo 5 | and Hyman, James M. 6 | and Bettencourt, Lu{\'i}s M. A. 7 | and Castillo-Chavez, Carlos", 8 | title="Stochastic Epidemic Modeling", 9 | bookTitle="Mathematical and Statistical Estimation Approaches in Epidemiology", 10 | year="2009", 11 | publisher="Springer Netherlands", 12 | address="Dordrecht", 13 | pages="31--52", 14 | abstract="We review the topic of stochastic epidemic modeling with emphasis on compartmental stochastic models. A main theme is the usefulness of the correspondence between these and their large population deterministic limits, which describe dynamical systems. The dynamics of an ODE system informs us of the deterministic skeleton upon which the behavior of corresponding stochastic systems are built. In this chapter we present a number of examples, mostly in the context of susceptible-infected-removed (SIR) models, and point out how this way of thinking may be useful in understanding other stochastic models. In particular we discuss the distribution of final epidemic size, the effect of different patterns of infectiousness, and the quantification of stochastically sustained oscillations.", 15 | isbn="978-90-481-2313-1", 16 | doi="10.1007/978-90-481-2313-1_2", 17 | url="https://doi.org/10.1007/978-90-481-2313-1_2" 18 | } 19 | 20 | 21 | 22 | 23 | @article{wagner, 24 | author = {J. Wagner and J. Keizer}, 25 | title = {Effects of rapid buffers on $\textrm{Ca}^{2+}$ diffusion and 26 | $\textrm{Ca}^{2+}$ oscillations}, 27 | journal = {Biophysical Journal}, 28 | volume = {67}, 29 | pages = {447-456}, 30 | year = {1994} 31 | } 32 | 33 | @article{winslowODE, 34 | author = {R. L. Winslow and J. Rice and S. Jafri and E. Marban and B. O'Rourke}, 35 | title = {Mechanisms of Altered Excitation-Contraction Coupling in Canine 36 | Tachycardia-Induced Heart Failure, {II}. Model Studies}, 37 | journal = {Circulation Research}, 38 | year = {1999} 39 | } 40 | 41 | @book{ODEI, 42 | author = {E. Hairer and S. P. N\o rsett and G. Wanner}, 43 | title = {Solving Ordinary Differential Equations I, Nonstiff Problems}, 44 | publisher = {Springer-Verlag}, 45 | year = {1991} 46 | } 47 | 48 | @book{ODEII, 49 | author = {E. Hairer and G. Wanner}, 50 | title = {Solving Ordinary Differential Equations II, Stiff and 51 | Differential Algebraic Problems}, 52 | publisher = {Springer-Verlag}, 53 | year = {1991} 54 | } 55 | 56 | @techreport{DIRK, 57 | author = {A. {Kv\ae rn\o}}, 58 | title = {More, and to be hoped, better {DIRK} 59 | methods for the solution of stiff {ODEs}.}, 60 | institution = {Mathematical Sciences Div., 61 | Norwegian Institute of Technology, Trondheim}, 62 | year = {1992} 63 | } 64 | 65 | @phdthesis{HansO, 66 | author = {H. Olsson}, 67 | title = {Runge-Kutta Solution of Initial Value Problems}, 68 | school = {Department of Computer Science, 69 | Lund Institute of Technology, Lund University}, 70 | year = {1998} 71 | } 72 | 73 | @techreport{ODErep, 74 | author = {J. Sundnes and G. T. Lines and A. Tveito}, 75 | title = {An investigation of different solvers for stiff ODE systems}, 76 | institution = {Department of Informatics, 77 | Faculty of Mathematics and Natural Sciences, 78 | University of Oslo}, 79 | year = {2000} 80 | } 81 | 82 | @article{ODEart, 83 | author = {J. Sundnes and G. T. Lines and A. Tveito}, 84 | title = {Efficient solution of ordinary differential equations 85 | modeling electrical activity in cardiac cells}, 86 | journal = {Mathematical Biosciences}, 87 | year = {2001} 88 | } 89 | 90 | @article{HansO2, 91 | author = {H. Olsson}, 92 | title = {Stage value predictors and efficient Newton iterations in 93 | implicit Runge-Kutta methods}, 94 | journal = {SIAM Journal of Scientific Computing}, 95 | year = {1997} 96 | } 97 | 98 | @article{lopez, 99 | author = {L. Lopez}, 100 | title = {An explicit two-step method for solving stiff systems of 101 | ordinary differential equations}, 102 | journal = {International Journal of Computer Mathematics}, 103 | year = {1987} 104 | } 105 | 106 | @article{dahlquist, 107 | author = {G. Dahlquist}, 108 | title = {A special stability problem for linear multistep methods}, 109 | journal = {BIT}, 110 | year = {1963} 111 | } 112 | 113 | @Article{butcher, 114 | author = {J.C. Butcher}, 115 | title = {An introduction to "Almost {Runge}-{Kutta}" methods}, 116 | journal = {Applied Numerical Mathematics}, 117 | year = {1997}, 118 | volume = {24}, 119 | pages = {331-342} 120 | } 121 | 122 | @Article{hairwann99, 123 | author = {E. Hairer and G. Wanner}, 124 | title = {Stiff differential equations solved by Radau methods}, 125 | journal = {Journal of Computational and Applied Mathematics}, 126 | year = {1999}, 127 | volume = {111}, 128 | pages = {93-111} 129 | } 130 | 131 | @article{fehlberg, 132 | author = {E. Fehlberg}, 133 | title = {Klassische Runge-Kutta formeln f\"{u}nfter und siebenter 134 | ordnung mit scrittweitencontrolle}, 135 | journal = {Computing}, 136 | volume = {4}, 137 | pages = {93-106}, 138 | year = {1969} 139 | } 140 | 141 | @Article{dormand, 142 | author = {J. R. Dormand and P. J. Prince}, 143 | title = {A family of embedded Runge-Kutta formulae}, 144 | journal = {J. Comp. Appl. Math}, 145 | year = {1980}, 146 | volume = {6}, 147 | pages = {19-26} 148 | } 149 | 150 | @Article{Butcher64, 151 | author = {J. C. Butcher}, 152 | title = {Integration processes based on {Radau} quadrature formulas}, 153 | journal = {Mathematics of Computation}, 154 | year = {1964}, 155 | volume = {18}, 156 | pages = {233-244} 157 | } 158 | 159 | @Article{hosea, 160 | author = {M. E. Hosea and L. F. Shampine}, 161 | title = {Efficiency comparisons of methods for integrating {ODE}s}, 162 | journal = {Computers \& Mathematics with Applications}, 163 | year = {1994}, 164 | volume = {28}, 165 | pages = {45-55} 166 | } 167 | 168 | @Article{sandu1, 169 | author = {A. Sandu and J. G. Verwer and M. van Loon and G. R. Carmichael 170 | and F. A. Potra and D. Dabdub and J. H. Seinfeld}, 171 | title = {Benchmarking stiff {ODE} solvers for atmospheric chemistry problems 172 | - {I}. {I}mplicit vs explicit}, 173 | journal = {Athmospheric environment}, 174 | year = {1997}, 175 | volume = {31}, 176 | pages = {3151-3166} 177 | } 178 | 179 | @Article{sandu2, 180 | author = {A. Sandu and J. G. Verwer and J. G. Blom and E. J. Spee and G. R. Carmichael 181 | and F. A. Potra}, 182 | title = {Benchmarking stiff {ODE} solvers for atmospheric chemistry problems 183 | - {II}. {R}osenbrock solvers}, 184 | journal = {Athmospheric environment}, 185 | year = {1997}, 186 | volume = {31}, 187 | pages = {3459-3472} 188 | } 189 | 190 | 191 | 192 | @Article{Curtiss52, 193 | author = {C. F. Curtiss and J. O. Hirschfelder}, 194 | title = {Integration of stiff equations}, 195 | journal = {Proc. Nat. Acad. Sci.}, 196 | year = {1952}, 197 | volume = {38}, 198 | pages = {235-243} 199 | } 200 | 201 | 202 | @Book{AscherPetzold, 203 | author = {U. M. Ascher and L. R. Petzold}, 204 | title = {Computer methods for ordinary differential equations and 205 | differential-algebraic equtions}, 206 | publisher = {SIAM}, 207 | year = {1998} 208 | } 209 | 210 | @article{hosea1996analysis, 211 | title={Analysis and implementation of TR-BDF2}, 212 | author={Hosea, ME and Shampine, LF}, 213 | journal={Applied Numerical Mathematics}, 214 | volume={20}, 215 | number={1-2}, 216 | pages={21--37}, 217 | year={1996}, 218 | publisher={Elsevier} 219 | } 220 | 221 | 222 | @Article{Hodgkin, 223 | author = {A.L. Hodgkin and A. F. Huxley}, 224 | title = {A quantitative description of of membrane current and 225 | its aplication to conduction and excitation in nerve}, 226 | journal = {J Physiol}, 227 | year = {1952}, 228 | volume = {117}, 229 | pages = {500-544}, 230 | } 231 | 232 | @Article{RushLarsen, 233 | author = {S. Rush and H. Larsen}, 234 | title = {A practical algorithm for solving dynamic membrane equations}, 235 | journal = {IEEE Transactions on Biomedical Engineering}, 236 | year = {1978}, 237 | volume = {25}, 238 | number = {4}, 239 | pages = {389-392} 240 | } -------------------------------------------------------------------------------- /book_src/bib/ode.pub: -------------------------------------------------------------------------------- 1 | * articles 2 | ** A Quantitative Description of of Membrane Current and Its Aplication to Conduction and Excitation in Nerve 3 | key: Hodgkin 4 | author: A.L. Hodgkin, A. F. Huxley 5 | year: 1952 6 | journal: J Physiol 7 | volume: 117 8 | pages: 500-544 9 | status: published 10 | entrytype: article 11 | -------------------------------------------------------------------------------- /book_src/bib/papers.bib: -------------------------------------------------------------------------------- 1 | @article{wagner, 2 | title = {Effects of Rapid Buffers on $\textrm{Ca}^{2+}$ Diffusion and $\textrm{Ca}^{2+}$ Oscillations}, 3 | author = {J. Wagner and J. Keizer}, 4 | year = {1994}, 5 | journal = {Biophysical Journal}, 6 | volume = {67}, 7 | pages = {447-456}, 8 | } 9 | 10 | @article{winslowODE, 11 | title = {Mechanisms of Altered Excitation-Contraction Coupling in Canine Tachycardia-Induced Heart Failure, {II}. Model Studies}, 12 | author = {R. L. Winslow and J. Rice and S. Jafri and E. Marban and B. O'Rourke}, 13 | year = {1999}, 14 | journal = {Circulation Research}, 15 | } 16 | 17 | @article{ODEart, 18 | title = {Efficient Solution of Ordinary Differential Equations Modeling Electrical Activity in Cardiac Cells}, 19 | author = {J. Sundnes and G. T. Lines and A. Tveito}, 20 | year = {2001}, 21 | journal = {Mathematical Biosciences}, 22 | } 23 | 24 | @article{HansO2, 25 | title = {Stage Value Predictors and Efficient {N}ewton Iterations in Implicit Runge-Kutta Methods}, 26 | author = {H. Olsson}, 27 | year = {1997}, 28 | journal = {SIAM Journal on Scientific Computing}, 29 | } 30 | 31 | @article{lopez, 32 | title = {An Explicit Two-Step Method for Solving Stiff Systems of Ordinary Differential Equations}, 33 | author = {L. Lopez}, 34 | year = {1987}, 35 | journal = {International Journal of Computer Mathematics}, 36 | } 37 | 38 | @article{dahlquist, 39 | title = {A Special Stability Problem for Linear Multistep Methods}, 40 | author = {G. Dahlquist}, 41 | year = {1963}, 42 | journal = {BIT}, 43 | } 44 | 45 | @article{butcher, 46 | title = {An Introduction to "Almost {Runge}-{Kutta}" Methods}, 47 | author = {J.C. Butcher}, 48 | year = {1997}, 49 | journal = {Applied Numerical Mathematics}, 50 | volume = {24}, 51 | pages = {331-342}, 52 | } 53 | 54 | @article{hairwann99, 55 | title = {Stiff Differential Equations Solved by Radau Methods}, 56 | author = {E. Hairer and G. Wanner}, 57 | year = {1999}, 58 | journal = {Journal of Computational and Applied Mathematics}, 59 | volume = {111}, 60 | pages = {93-111}, 61 | } 62 | 63 | @article{fehlberg, 64 | title = {Klassische Runge-Kutta Formeln F\"{u}nfter Und Siebenter Ordnung Mit Scrittweitencontrolle}, 65 | author = {E. Fehlberg}, 66 | year = {1969}, 67 | journal = {Computing}, 68 | volume = {4}, 69 | pages = {93-106}, 70 | } 71 | 72 | @article{Butcher64, 73 | title = {Integration Processes Based on {Radau} Quadrature Formulas}, 74 | author = {J. C. Butcher}, 75 | year = {1964}, 76 | journal = {Mathematics of Computation}, 77 | volume = {18}, 78 | pages = {233-244}, 79 | } 80 | 81 | @article{hosea, 82 | title = {Efficiency Comparisons of Methods for Integrating {ODE}s}, 83 | author = {M. E. Hosea and L. F. Shampine}, 84 | year = {1994}, 85 | journal = {Computers \& Mathematics with Applications}, 86 | volume = {28}, 87 | pages = {45-55}, 88 | } 89 | 90 | @article{sandu1, 91 | title = {Benchmarking Stiff {ODE} Solvers for Atmospheric Chemistry Problems - {I}. {I}mplicit Vs Explicit}, 92 | author = {A. Sandu and J. G. Verwer and M. van Loon and G. R. Carmichael and F. A. Potra and D. Dabdub and J. H. Seinfeld}, 93 | year = {1997}, 94 | journal = {Athmospheric environment}, 95 | volume = {31}, 96 | pages = {3151-3166}, 97 | } 98 | 99 | @article{sandu2, 100 | title = {Benchmarking Stiff {ODE} Solvers for Atmospheric Chemistry Problems - {II}. {R}osenbrock Solvers}, 101 | author = {A. Sandu and J. G. Verwer and J. G. Blom and E. J. Spee and G. R. Carmichael and F. A. Potra}, 102 | year = {1997}, 103 | journal = {Athmospheric environment}, 104 | volume = {31}, 105 | pages = {3459-3472}, 106 | } 107 | 108 | @article{Curtiss52, 109 | title = {Integration of Stiff Equations}, 110 | author = {C. F. Curtiss and J. O. Hirschfelder}, 111 | year = {1952}, 112 | journal = {Proc. Nat. Acad. Sci.}, 113 | volume = {38}, 114 | pages = {235-243}, 115 | } 116 | 117 | @article{hosea1996analysis, 118 | title = {Analysis and Implementation of TR-BDF2}, 119 | author = {ME Hosea and LF Shampine}, 120 | year = {1996}, 121 | journal = {Applied Numerical Mathematics}, 122 | publisher = {Elsevier}, 123 | volume = {20}, 124 | number = {1-2}, 125 | pages = {21--37}, 126 | } 127 | 128 | @article{RushLarsen, 129 | title = {A Practical Algorithm for Solving Dynamic Membrane Equations}, 130 | author = {S. Rush and H. Larsen}, 131 | year = {1978}, 132 | journal = {IEEE Transactions on Biomedical Engineering}, 133 | volume = {25}, 134 | number = {4}, 135 | pages = {389-392}, 136 | } 137 | 138 | @article{Hodgkin, 139 | title = {A Quantitative Description of of Membrane Current and Its Aplication to Conduction and Excitation in Nerve}, 140 | author = {A.L. Hodgkin and A. F. Huxley}, 141 | year = {1952}, 142 | journal = {J Physiol}, 143 | volume = {117}, 144 | pages = {500-544}, 145 | } 146 | 147 | @article{SIR1, 148 | title = {Contributions to the Mathematical Theory of Epidemics-I. 1927}, 149 | author = {WO Kermack and AG McKendrick}, 150 | year = {1991}, 151 | journal = {Bulletin of mathematical biology}, 152 | volume = {53}, 153 | number = {1-2}, 154 | DOI = {10.1007/bf02464423}, 155 | ISSN = {0092-8240}, 156 | } 157 | 158 | @article{dormand, 159 | title = {A Family of Embedded Runge-Kutta Formulae}, 160 | author = {J. R. Dormand and P. J. Prince}, 161 | year = {1980}, 162 | journal = {J. Comput. Appl. Math.}, 163 | volume = {6}, 164 | pages = {19-26}, 165 | } 166 | 167 | @article{kvaerno2004singly, 168 | title = {Singly Diagonally Implicit Runge-Kutta Methods With an Explicit First Stage}, 169 | author = {Anne Kv{\ae}rn{\o}}, 170 | year = {2004}, 171 | journal = {BIT Numerical Mathematics}, 172 | publisher = {Springer}, 173 | volume = {44}, 174 | pages = {489--502}, 175 | } 176 | 177 | @article{gustafsson1990using, 178 | title = {Using Control Theory to Improve Stepsize Selection in Numerical Integration of {ODE}}, 179 | author = {Kjell Gustafsson}, 180 | year = {1990}, 181 | journal = {IFAC Proceedings Volumes}, 182 | publisher = {Elsevier}, 183 | volume = {23}, 184 | number = {8}, 185 | pages = {405--410}, 186 | } 187 | 188 | @article{fehlberg1970, 189 | title = {Klassische Runge-Kutta-Formeln Vierter Und Niedrigerer Ordnung Mit Schrittweiten-Kontrolle Und Ihre Anwendung Auf W{\"a}rmeleitungsprobleme}, 190 | author = {E. Fehlberg}, 191 | year = {1970}, 192 | journal = {Computing}, 193 | volume = {6}, 194 | number = {1}, 195 | pages = {61--71}, 196 | doi = {10.1007/BF02241732}, 197 | url = {https://doi.org/10.1007/BF02241732}, 198 | } 199 | 200 | @book{ODEI, 201 | title = {Solving Ordinary Differential Equations I, Nonstiff Problems}, 202 | author = {E. Hairer and S. P. N\o rsett and G. Wanner}, 203 | year = {1991}, 204 | publisher = {Springer}, 205 | } 206 | 207 | @book{ODEII, 208 | title = {Solving Ordinary Differential Equations II, Stiff and Differential Algebraic Problems}, 209 | author = {E. Hairer and G. Wanner}, 210 | year = {1991}, 211 | publisher = {Springer}, 212 | } 213 | 214 | @book{AscherPetzold, 215 | title = {Computer Methods for Ordinary Differential Equations and Differential-Algebraic Equtions}, 216 | author = {U. M. Ascher and L. R. Petzold}, 217 | year = {1998}, 218 | publisher = {SIAM}, 219 | } 220 | 221 | @book{ciaramella2022iterative, 222 | title = {Iterative Methods and Preconditioners for Systems of Linear Equations}, 223 | author = {Gabriele Ciaramella and Martin J. Gander}, 224 | year = {2022}, 225 | publisher = {SIAM}, 226 | } 227 | 228 | @book{KeenerSneyd, 229 | title = {Mathematical Physiology}, 230 | author = {J. Keener and J. Sneyd}, 231 | year = {2009}, 232 | publisher = {Springer}, 233 | } 234 | 235 | @book{langtangen2012primer, 236 | title = {A Primer on Scientific Programming With {P}ython}, 237 | author = {Hans Petter Langtangen and Hans Petter Langtangen}, 238 | year = {2012}, 239 | publisher = {Springer}, 240 | volume = {6}, 241 | } 242 | 243 | @book{sundnes2020introduction, 244 | title = {Introduction to Scientific Programming With {P}ython}, 245 | author = {Joakim Sundnes}, 246 | year = {2020}, 247 | publisher = {Springer}, 248 | } 249 | 250 | @book{deuflhard2012scientific, 251 | title = {Scientific Computing With Ordinary Differential Equations}, 252 | author = {Peter Deuflhard and Folkmar Bornemann}, 253 | year = {2012}, 254 | publisher = {Springer}, 255 | volume = {42}, 256 | } 257 | 258 | @inbook{Greenwood2009, 259 | title = {Stochastic Epidemic Modeling}, 260 | author = {Priscilla E. Greenwood and Luis F. Gordillo}, 261 | editor = {Gerardo Chowell and James M. Hyman and Lu{\'i}s M. A. Bettencourt and Carlos Castillo-Chavez}, 262 | year = {2009}, 263 | publisher = {Springer}, 264 | pages = {31--52}, 265 | bookTitle = {Mathematical and Statistical Estimation Approaches in Epidemiology}, 266 | } 267 | 268 | @techreport{DIRK, 269 | title = {More, and to Be Hoped, Better {DIRK} Methods for the Solution of Stiff {ODEs}.}, 270 | author = {A. {Kv\ae rn\o}}, 271 | year = {1992}, 272 | institution = {Mathematical Sciences Div., Norwegian Institute of Technology, Trondheim}, 273 | } 274 | 275 | @techreport{ODErep, 276 | title = {An Investigation of Different Solvers for Stiff {ODE} Systems}, 277 | author = {J. Sundnes and G. T. Lines and A. Tveito}, 278 | year = {2000}, 279 | institution = {Department of Informatics, Faculty of Mathematics and Natural Sciences, University of Oslo}, 280 | } 281 | 282 | @None{HansO, 283 | title = {Runge-Kutta Solution of Initial Value Problems}, 284 | author = {H. Olsson}, 285 | year = {1998}, 286 | school = {Department of Computer Science, Lund Institute of Technology, Lund University}, 287 | thesistype = {phd}, 288 | } 289 | -------------------------------------------------------------------------------- /book_src/bib/papers.pub: -------------------------------------------------------------------------------- 1 | * articles 2 | ** Effects of Rapid Buffers on $\textrm{Ca}^{2+}$ Diffusion and $\textrm{Ca}^{2+}$ Oscillations 3 | key: wagner 4 | author: J. Wagner, J. Keizer 5 | year: 1994 6 | journal: Biophysical Journal 7 | volume: 67 8 | pages: 447-456 9 | status: published 10 | entrytype: article 11 | ** Mechanisms of Altered Excitation-Contraction Coupling in Canine Tachycardia-Induced Heart Failure, {II}. Model Studies 12 | key: winslowODE 13 | author: R. L. Winslow, J. Rice, S. Jafri, E. Marban, B. O'Rourke 14 | year: 1999 15 | journal: Circulation Research 16 | status: published 17 | entrytype: article 18 | ** Efficient Solution of Ordinary Differential Equations Modeling Electrical Activity in Cardiac Cells 19 | key: ODEart 20 | author: J. Sundnes, G. T. Lines, A. Tveito 21 | year: 2001 22 | journal: Mathematical Biosciences 23 | status: published 24 | entrytype: article 25 | ** Stage Value Predictors and Efficient {N}ewton Iterations in Implicit Runge-Kutta Methods 26 | key: HansO2 27 | author: H. Olsson 28 | year: 1997 29 | journal: SIAM Journal on Scientific Computing 30 | status: published 31 | entrytype: article 32 | ** An Explicit Two-Step Method for Solving Stiff Systems of Ordinary Differential Equations 33 | key: lopez 34 | author: L. Lopez 35 | year: 1987 36 | journal: International Journal of Computer Mathematics 37 | status: published 38 | entrytype: article 39 | ** A Special Stability Problem for Linear Multistep Methods 40 | key: dahlquist 41 | author: G. Dahlquist 42 | year: 1963 43 | journal: BIT 44 | status: published 45 | entrytype: article 46 | ** An Introduction to "Almost {Runge}-{Kutta}" Methods 47 | key: butcher 48 | author: J.C. Butcher 49 | year: 1997 50 | journal: Applied Numerical Mathematics 51 | volume: 24 52 | pages: 331-342 53 | status: published 54 | entrytype: article 55 | ** Stiff Differential Equations Solved by Radau Methods 56 | key: hairwann99 57 | author: E. Hairer, G. Wanner 58 | year: 1999 59 | journal: Journal of Computational and Applied Mathematics 60 | volume: 111 61 | pages: 93-111 62 | status: published 63 | entrytype: article 64 | ** Klassische Runge-Kutta Formeln F\"{u}nfter Und Siebenter Ordnung Mit Scrittweitencontrolle 65 | key: fehlberg 66 | author: E. Fehlberg 67 | year: 1969 68 | journal: Computing 69 | volume: 4 70 | pages: 93-106 71 | status: published 72 | entrytype: article 73 | ** Integration Processes Based on {Radau} Quadrature Formulas 74 | key: Butcher64 75 | author: J. C. Butcher 76 | year: 1964 77 | journal: Mathematics of Computation 78 | volume: 18 79 | pages: 233-244 80 | status: published 81 | entrytype: article 82 | ** Efficiency Comparisons of Methods for Integrating {ODE}s 83 | key: hosea 84 | author: M. E. Hosea, L. F. Shampine 85 | year: 1994 86 | journal: Computers \& Mathematics with Applications 87 | volume: 28 88 | pages: 45-55 89 | status: published 90 | entrytype: article 91 | ** Benchmarking Stiff {ODE} Solvers for Atmospheric Chemistry Problems - {I}. {I}mplicit Vs Explicit 92 | key: sandu1 93 | author: A. Sandu, J. G. Verwer, M. van Loon, G. R. Carmichael, F. A. Potra, D. Dabdub, J. H. Seinfeld 94 | year: 1997 95 | journal: Athmospheric environment 96 | volume: 31 97 | pages: 3151-3166 98 | status: published 99 | entrytype: article 100 | ** Benchmarking Stiff {ODE} Solvers for Atmospheric Chemistry Problems - {II}. {R}osenbrock Solvers 101 | key: sandu2 102 | author: A. Sandu, J. G. Verwer, J. G. Blom, E. J. Spee, G. R. Carmichael, F. A. Potra 103 | year: 1997 104 | journal: Athmospheric environment 105 | volume: 31 106 | pages: 3459-3472 107 | status: published 108 | entrytype: article 109 | ** Integration of Stiff Equations 110 | key: Curtiss52 111 | author: C. F. Curtiss, J. O. Hirschfelder 112 | year: 1952 113 | journal: Proc. Nat. Acad. Sci. 114 | volume: 38 115 | pages: 235-243 116 | status: published 117 | entrytype: article 118 | ** Analysis and Implementation of TR-BDF2 119 | key: hosea1996analysis 120 | author: ME Hosea, LF Shampine 121 | year: 1996 122 | journal: Applied Numerical Mathematics 123 | publisher: Elsevier 124 | volume: 20 125 | number: 1-2 126 | pages: 21--37 127 | status: published 128 | entrytype: article 129 | ** A Practical Algorithm for Solving Dynamic Membrane Equations 130 | key: RushLarsen 131 | author: S. Rush, H. Larsen 132 | year: 1978 133 | journal: IEEE Transactions on Biomedical Engineering 134 | volume: 25 135 | number: 4 136 | pages: 389-392 137 | status: published 138 | entrytype: article 139 | ** A Quantitative Description of of Membrane Current and Its Aplication to Conduction and Excitation in Nerve 140 | key: Hodgkin 141 | author: A.L. Hodgkin, A. F. Huxley 142 | year: 1952 143 | journal: J Physiol 144 | volume: 117 145 | pages: 500-544 146 | status: published 147 | entrytype: article 148 | ** Contributions to the Mathematical Theory of Epidemics-I. 1927 149 | key: SIR1 150 | author: WO Kermack, AG McKendrick 151 | year: 1991 152 | journal: Bulletin of mathematical biology 153 | volume: 53 154 | number: 1-2 155 | status: published 156 | DOI: 10.1007/bf02464423 157 | ISSN: 0092-8240 158 | entrytype: article 159 | ** A Family of Embedded Runge-Kutta Formulae 160 | key: dormand 161 | author: J. R. Dormand, P. J. Prince 162 | year: 1980 163 | journal: J. Comput. Appl. Math. 164 | volume: 6 165 | pages: 19-26 166 | status: published 167 | entrytype: article 168 | ** Singly Diagonally Implicit Runge-Kutta Methods With an Explicit First Stage 169 | key: kvaerno2004singly 170 | author: Anne Kv{\ae}rn{\o} 171 | year: 2004 172 | journal: BIT Numerical Mathematics 173 | publisher: Springer 174 | volume: 44 175 | pages: 489--502 176 | status: published 177 | entrytype: article 178 | ** Using Control Theory to Improve Stepsize Selection in Numerical Integration of {ODE} 179 | key: gustafsson1990using 180 | author: Kjell Gustafsson 181 | year: 1990 182 | journal: IFAC Proceedings Volumes 183 | publisher: Elsevier 184 | volume: 23 185 | number: 8 186 | pages: 405--410 187 | status: published 188 | entrytype: article 189 | ** Klassische Runge-Kutta-Formeln Vierter Und Niedrigerer Ordnung Mit Schrittweiten-Kontrolle Und Ihre Anwendung Auf W{\"a}rmeleitungsprobleme 190 | key: fehlberg1970 191 | author: E. Fehlberg 192 | year: 1970 193 | journal: Computing 194 | volume: 6 195 | number: 1 196 | pages: 61--71 197 | doi: 10.1007/BF02241732 198 | url: https://doi.org/10.1007/BF02241732 199 | status: published 200 | entrytype: article 201 | * books 202 | ** Solving Ordinary Differential Equations I, Nonstiff Problems 203 | key: ODEI 204 | author: E. Hairer, S. P. N\o rsett, G. Wanner 205 | year: 1991 206 | publisher: Springer 207 | status: published 208 | entrytype: book 209 | ** Solving Ordinary Differential Equations II, Stiff and Differential Algebraic Problems 210 | key: ODEII 211 | author: E. Hairer, G. Wanner 212 | year: 1991 213 | publisher: Springer 214 | status: published 215 | entrytype: book 216 | ** Computer Methods for Ordinary Differential Equations and Differential-Algebraic Equtions 217 | key: AscherPetzold 218 | author: U. M. Ascher, L. R. Petzold 219 | year: 1998 220 | publisher: SIAM 221 | status: published 222 | entrytype: book 223 | ** Iterative Methods and Preconditioners for Systems of Linear Equations 224 | key: ciaramella2022iterative 225 | author: Gabriele Ciaramella, Martin J. Gander 226 | year: 2022 227 | publisher: SIAM 228 | status: published 229 | entrytype: book 230 | ** Mathematical Physiology 231 | key: KeenerSneyd 232 | author: J. Keener, J. Sneyd 233 | year: 2009 234 | publisher: Springer 235 | status: published 236 | entrytype: book 237 | ** A Primer on Scientific Programming With {P}ython 238 | key: langtangen2012primer 239 | author: Hans Petter Langtangen, Hans Petter Langtangen 240 | year: 2012 241 | publisher: Springer 242 | volume: 6 243 | status: published 244 | entrytype: book 245 | ** Introduction to Scientific Programming With {P}ython 246 | key: sundnes2020introduction 247 | author: Joakim Sundnes 248 | year: 2020 249 | publisher: Springer 250 | status: published 251 | entrytype: book 252 | ** Scientific Computing With Ordinary Differential Equations 253 | key: deuflhard2012scientific 254 | author: Peter Deuflhard, Folkmar Bornemann 255 | year: 2012 256 | publisher: Springer 257 | volume: 42 258 | status: published 259 | entrytype: book 260 | * chapters 261 | ** Stochastic Epidemic Modeling 262 | key: Greenwood2009 263 | author: Priscilla E. Greenwood, Luis F. Gordillo 264 | editor: Gerardo Chowell, James M. Hyman, Lu{\'i}s M. A. Bettencourt, Carlos Castillo-Chavez 265 | year: 2009 266 | publisher: Springer 267 | pages: 31--52 268 | status: published 269 | bookTitle: Mathematical and Statistical Estimation Approaches in Epidemiology 270 | entrytype: inbook 271 | * reports 272 | ** More, and to Be Hoped, Better {DIRK} Methods for the Solution of Stiff {ODEs}. 273 | key: DIRK 274 | author: A. {Kv\ae rn\o} 275 | year: 1992 276 | institution: Mathematical Sciences Div., Norwegian Institute of Technology, Trondheim 277 | status: published 278 | entrytype: techreport 279 | ** An Investigation of Different Solvers for Stiff {ODE} Systems 280 | key: ODErep 281 | author: J. Sundnes, G. T. Lines, A. Tveito 282 | year: 2000 283 | institution: Department of Informatics, Faculty of Mathematics and Natural Sciences, University of Oslo 284 | status: published 285 | entrytype: techreport 286 | * theses 287 | ** Runge-Kutta Solution of Initial Value Problems 288 | key: HansO 289 | author: H. Olsson 290 | year: 1998 291 | status: published 292 | entrytype: phdthesis 293 | school: Department of Computer Science, Lund Institute of Technology, Lund University 294 | thesistype: phd 295 | -------------------------------------------------------------------------------- /book_src/bib/papers.pub.bak: -------------------------------------------------------------------------------- 1 | * articles 2 | ** Effects of Rapid Buffers on $\textrm{Ca}^{2+}$ Diffusion and $\textrm{Ca}^{2+}$ Oscillations 3 | key: wagner 4 | author: J. Wagner, J. Keizer 5 | year: 1994 6 | journal: Biophysical Journal 7 | volume: 67 8 | pages: 447-456 9 | status: published 10 | entrytype: article 11 | ** Mechanisms of Altered Excitation-Contraction Coupling in Canine Tachycardia-Induced Heart Failure, {II}. Model Studies 12 | key: winslowODE 13 | author: R. L. Winslow, J. Rice, S. Jafri, E. Marban, B. O'Rourke 14 | year: 1999 15 | journal: Circulation Research 16 | status: published 17 | entrytype: article 18 | ** Efficient Solution of Ordinary Differential Equations Modeling Electrical Activity in Cardiac Cells 19 | key: ODEart 20 | author: J. Sundnes, G. T. Lines, A. Tveito 21 | year: 2001 22 | journal: Mathematical Biosciences 23 | status: published 24 | entrytype: article 25 | ** Stage Value Predictors and Efficient {N}ewton Iterations in Implicit Runge-Kutta Methods 26 | key: HansO2 27 | author: H. Olsson 28 | year: 1997 29 | journal: SIAM Journal on Scientific Computing 30 | status: published 31 | entrytype: article 32 | ** An Explicit Two-Step Method for Solving Stiff Systems of Ordinary Differential Equations 33 | key: lopez 34 | author: L. Lopez 35 | year: 1987 36 | journal: International Journal of Computer Mathematics 37 | status: published 38 | entrytype: article 39 | ** A Special Stability Problem for Linear Multistep Methods 40 | key: dahlquist 41 | author: G. Dahlquist 42 | year: 1963 43 | journal: BIT 44 | status: published 45 | entrytype: article 46 | ** An Introduction to "Almost {Runge}-{Kutta}" Methods 47 | key: butcher 48 | author: J.C. Butcher 49 | year: 1997 50 | journal: Applied Numerical Mathematics 51 | volume: 24 52 | pages: 331-342 53 | status: published 54 | entrytype: article 55 | ** Stiff Differential Equations Solved by Radau Methods 56 | key: hairwann99 57 | author: E. Hairer, G. Wanner 58 | year: 1999 59 | journal: Journal of Computational and Applied Mathematics 60 | volume: 111 61 | pages: 93-111 62 | status: published 63 | entrytype: article 64 | ** Klassische Runge-Kutta Formeln F\"{u}nfter Und Siebenter Ordnung Mit Scrittweitencontrolle 65 | key: fehlberg 66 | author: E. Fehlberg 67 | year: 1969 68 | journal: Computing 69 | volume: 4 70 | pages: 93-106 71 | status: published 72 | entrytype: article 73 | ** Integration Processes Based on {Radau} Quadrature Formulas 74 | key: Butcher64 75 | author: J. C. Butcher 76 | year: 1964 77 | journal: Mathematics of Computation 78 | volume: 18 79 | pages: 233-244 80 | status: published 81 | entrytype: article 82 | ** Efficiency Comparisons of Methods for Integrating {ODE}s 83 | key: hosea 84 | author: M. E. Hosea, L. F. Shampine 85 | year: 1994 86 | journal: Computers \& Mathematics with Applications 87 | volume: 28 88 | pages: 45-55 89 | status: published 90 | entrytype: article 91 | ** Benchmarking Stiff {ODE} Solvers for Atmospheric Chemistry Problems - {I}. {I}mplicit Vs Explicit 92 | key: sandu1 93 | author: A. Sandu, J. G. Verwer, M. van Loon, G. R. Carmichael, F. A. Potra, D. Dabdub, J. H. Seinfeld 94 | year: 1997 95 | journal: Athmospheric environment 96 | volume: 31 97 | pages: 3151-3166 98 | status: published 99 | entrytype: article 100 | ** Benchmarking Stiff {ODE} Solvers for Atmospheric Chemistry Problems - {II}. {R}osenbrock Solvers 101 | key: sandu2 102 | author: A. Sandu, J. G. Verwer, J. G. Blom, E. J. Spee, G. R. Carmichael, F. A. Potra 103 | year: 1997 104 | journal: Athmospheric environment 105 | volume: 31 106 | pages: 3459-3472 107 | status: published 108 | entrytype: article 109 | ** Integration of Stiff Equations 110 | key: Curtiss52 111 | author: C. F. Curtiss, J. O. Hirschfelder 112 | year: 1952 113 | journal: Proc. Nat. Acad. Sci. 114 | volume: 38 115 | pages: 235-243 116 | status: published 117 | entrytype: article 118 | ** Analysis and Implementation of TR-BDF2 119 | key: hosea1996analysis 120 | author: ME Hosea, LF Shampine 121 | year: 1996 122 | journal: Applied Numerical Mathematics 123 | publisher: Elsevier 124 | volume: 20 125 | number: 1-2 126 | pages: 21--37 127 | status: published 128 | entrytype: article 129 | ** A Practical Algorithm for Solving Dynamic Membrane Equations 130 | key: RushLarsen 131 | author: S. Rush, H. Larsen 132 | year: 1978 133 | journal: IEEE Transactions on Biomedical Engineering 134 | volume: 25 135 | number: 4 136 | pages: 389-392 137 | status: published 138 | entrytype: article 139 | ** A Quantitative Description of of Membrane Current and Its Aplication to Conduction and Excitation in Nerve 140 | key: Hodgkin 141 | author: A.L. Hodgkin, A. F. Huxley 142 | year: 1952 143 | journal: J Physiol 144 | volume: 117 145 | pages: 500-544 146 | status: published 147 | entrytype: article 148 | ** Contributions to the Mathematical Theory of Epidemics-I. 1927 149 | key: SIR1 150 | author: WO Kermack, AG McKendrick 151 | year: 1991 152 | journal: Bulletin of mathematical biology 153 | volume: 53 154 | number: 1-2 155 | status: published 156 | DOI: 10.1007/bf02464423 157 | ISSN: 0092-8240 158 | entrytype: article 159 | ** A Family of Embedded Runge-Kutta Formulae 160 | key: dormand 161 | author: J. R. Dormand, P. J. Prince 162 | year: 1980 163 | journal: J. Comput. Appl. Math. 164 | volume: 6 165 | pages: 19-26 166 | status: published 167 | entrytype: article 168 | ** Singly Diagonally Implicit Runge-Kutta Methods With an Explicit First Stage 169 | key: kvaerno2004singly 170 | author: Anne Kv{\ae}rn{\o} 171 | year: 2004 172 | journal: BIT Numerical Mathematics 173 | publisher: Springer 174 | volume: 44 175 | pages: 489--502 176 | status: published 177 | entrytype: article 178 | ** Using Control Theory to Improve Stepsize Selection in Numerical Integration of {ODE} 179 | key: gustafsson1990using 180 | author: Kjell Gustafsson 181 | year: 1990 182 | journal: IFAC Proceedings Volumes 183 | publisher: Elsevier 184 | volume: 23 185 | number: 8 186 | pages: 405--410 187 | status: published 188 | entrytype: article 189 | * books 190 | ** Solving Ordinary Differential Equations I, Nonstiff Problems 191 | key: ODEI 192 | author: E. Hairer, S. P. N\o rsett, G. Wanner 193 | year: 1991 194 | publisher: Springer 195 | status: published 196 | entrytype: book 197 | ** Solving Ordinary Differential Equations II, Stiff and Differential Algebraic Problems 198 | key: ODEII 199 | author: E. Hairer, G. Wanner 200 | year: 1991 201 | publisher: Springer 202 | status: published 203 | entrytype: book 204 | ** Computer Methods for Ordinary Differential Equations and Differential-Algebraic Equtions 205 | key: AscherPetzold 206 | author: U. M. Ascher, L. R. Petzold 207 | year: 1998 208 | publisher: SIAM 209 | status: published 210 | entrytype: book 211 | ** Iterative Methods and Preconditioners for Systems of Linear Equations 212 | key: ciaramella2022iterative 213 | author: Gabriele Ciaramella, Martin J. Gander 214 | year: 2022 215 | publisher: SIAM 216 | status: published 217 | entrytype: book 218 | ** Mathematical Physiology 219 | key: KeenerSneyd 220 | author: J. Keener, J. Sneyd 221 | year: 2009 222 | publisher: Springer 223 | status: published 224 | entrytype: book 225 | ** A Primer on Scientific Programming With {P}ython 226 | key: langtangen2012primer 227 | author: Hans Petter Langtangen, Hans Petter Langtangen 228 | year: 2012 229 | publisher: Springer 230 | volume: 6 231 | status: published 232 | entrytype: book 233 | ** Introduction to Scientific Programming With {P}ython 234 | key: sundnes2020introduction 235 | author: Joakim Sundnes 236 | year: 2020 237 | publisher: Springer 238 | status: published 239 | entrytype: book 240 | ** Scientific Computing With Ordinary Differential Equations 241 | key: deuflhard2012scientific 242 | author: Peter Deuflhard, Folkmar Bornemann 243 | year: 2012 244 | publisher: Springer 245 | volume: 42 246 | status: published 247 | entrytype: book 248 | * chapters 249 | ** Stochastic Epidemic Modeling 250 | key: Greenwood2009 251 | author: Priscilla E. Greenwood, Luis F. Gordillo 252 | editor: Gerardo Chowell, James M. Hyman, Lu{\'i}s M. A. Bettencourt, Carlos Castillo-Chavez 253 | year: 2009 254 | publisher: Springer 255 | pages: 31--52 256 | status: published 257 | bookTitle: Mathematical and Statistical Estimation Approaches in Epidemiology 258 | entrytype: inbook 259 | * reports 260 | ** More, and to Be Hoped, Better {DIRK} Methods for the Solution of Stiff {ODEs}. 261 | key: DIRK 262 | author: A. {Kv\ae rn\o} 263 | year: 1992 264 | institution: Mathematical Sciences Div., Norwegian Institute of Technology, Trondheim 265 | status: published 266 | entrytype: techreport 267 | ** An Investigation of Different Solvers for Stiff {ODE} Systems 268 | key: ODErep 269 | author: J. Sundnes, G. T. Lines, A. Tveito 270 | year: 2000 271 | institution: Department of Informatics, Faculty of Mathematics and Natural Sciences, University of Oslo 272 | status: published 273 | entrytype: techreport 274 | * theses 275 | ** Runge-Kutta Solution of Initial Value Problems 276 | key: HansO 277 | author: H. Olsson 278 | year: 1998 279 | status: published 280 | entrytype: phdthesis 281 | school: Department of Computer Science, Lund Institute of Technology, Lund University 282 | thesistype: phd 283 | -------------------------------------------------------------------------------- /book_src/bib/venues.list: -------------------------------------------------------------------------------- 1 | journal: Circulation Research 2 | institution: Mathematical Sciences Div., Norwegian Institute of Technology, Trondheim 3 | school: Department of Computer Science, Lund Institute of Technology, Lund University 4 | institution: Department of Informatics, Faculty of Mathematics and Natural Sciences, University of Oslo 5 | journal: Computing 6 | journal: Athmospheric environment 7 | journal: Proc. Nat. Acad. Sci. 8 | journal: Applied Numerical Mathematics 9 | journal: J Physiol 10 | journal: Bulletin of mathematical biology 11 | journal: IFAC Proceedings Volumes 12 | -------------------------------------------------------------------------------- /book_src/book/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Run from subdirectory (chapter directory) 3 | doconce clean 4 | rm -rf *.html *.pdf *.tex reveal.js deck.js automake* newcommands* Trash sphinx 5 | -------------------------------------------------------------------------------- /book_src/book/clean_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | python -c 'import scripts; scripts.clean()' 3 | rm -rf runestone sphinx-* *.pyc automake* 4 | -------------------------------------------------------------------------------- /book_src/book/make_html.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -x 3 | 4 | name=book 5 | CHAPTER=chapter 6 | BOOK=book 7 | APPENDIX=appendix 8 | 9 | function system { 10 | "$@" 11 | if [ $? -ne 0 ]; then 12 | echo "make.sh: unsuccessful command $@" 13 | echo "abort!" 14 | exit 1 15 | fi 16 | } 17 | 18 | preprocess -DFORMAT=html ../chapters/newcommands.p.tex > newcommands_keep.tex 19 | 20 | opt="CHAPTER=$CHAPTER BOOK=$BOOK APPENDIX=$APPENDIX" 21 | opt="$opt --exercise_numbering=chapter" 22 | 23 | # Compile HTML Bootstrap book 24 | system doconce format html $name $opt --html_style=bootswatch_journal --html_code_style=inherit --html_output=$name 25 | system doconce split_html $name.html 26 | 27 | # Compile standard sphinx 28 | theme=uio 29 | system doconce format sphinx $name $opt --sphinx_keep_splits 30 | system doconce split_rst $name 31 | system doconce sphinx_dir theme=$theme dirname=sphinx-${theme} $name 32 | # Change logo 33 | doconce replace _static/uio_logo.png https://raw.githubusercontent.com/CINPLA/logo/master/brain/cinpla_logo_transparent.png sphinx-${theme}/_themes/uio/layout.html 34 | system python automake_sphinx.py 35 | 36 | # Generate and compile RunestoneInteractive book 37 | # (temporarily not available after their setup changed) 38 | # see bin/doconce and update generation of automake_sphinx.py 39 | #system doconce format sphinx $name --runestone $opt --sphinx_keep_splits -DRUNESTONE 40 | #system doconce split_rst $name 41 | #system doconce sphinx_dir theme=cbc dirname=runestone $name 42 | #system python automake_sphinx.py --runestone 43 | 44 | # Publish 45 | dest=../../pub 46 | cp $name.html ._${name}*.html $dest 47 | figdirs="fig-* mov-*" 48 | for figdir in $figdirs; do 49 | # slash important for copying files in links to dirs 50 | if [ -d $figdir/ ]; then 51 | cp -r $figdir/ $dest 52 | fi 53 | done 54 | 55 | rm -rf $dest/sphinx 56 | #rm -rf $dest/sphinx-runestone 57 | cp -r sphinx-${theme}/_build/html $dest/sphinx 58 | #cp -r runestone/RunestoneTools/build $dest/sphinx-runestone 59 | 60 | cd $dest 61 | git add sphinx-* 62 | -------------------------------------------------------------------------------- /book_src/book/make_ipynb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | # Compile the book to LaTeX/PDF. 3 | # 4 | # Usage: make.sh [nospell] 5 | # 6 | # With nospell, spellchecking is skipped. 7 | 8 | set -x 9 | 10 | name=ode_book 11 | #name=test 12 | encoding="--encoding=utf-8" 13 | 14 | CHAPTER=chapter 15 | BOOK=book 16 | APPENDIX=appendix 17 | 18 | function system { 19 | "$@" 20 | if [ $? -ne 0 ]; then 21 | echo "make.sh: unsuccessful command $@" 22 | echo "abort!" 23 | exit 1 24 | fi 25 | } 26 | 27 | rm tmp_* 28 | 29 | if [ $# -ge 1 ]; then 30 | spellcheck=$1 31 | else 32 | spellcheck=spell 33 | fi 34 | 35 | cd ../chapters 36 | for name in ode_intro rungekutta1 rungekutta2 adaptive disease_modeling diffeq 37 | do 38 | cp $name.do.txt tmp.do.txt 39 | #doconce subst 'FIGURE: +\[fig-(.+?)/(.+?),' 'FIGURE: [https://raw.githubusercontent.com/hplgit/scipro-primer/master/slides/\g<1>/html/fig-\g<1>/\g<2>.png,' tmp.do.txt 40 | doconce replace "../chapters/" "./" tmp.do.txt 41 | #hacks to fix references to other chapters 42 | doconce replace ref{ch:ode_intro} 1 tmp.do.txt 43 | doconce replace ref{ch:runge_kutta} 2 tmp.do.txt 44 | doconce replace ref{ch:stiff} 3 tmp.do.txt 45 | doconce replace ref{ch:adaptive} 4 tmp.do.txt 46 | doconce replace ref{ch:disease_models} 5 tmp.do.txt 47 | doconce replace ref{ch:diff_eq} A tmp.do.txt 48 | 49 | doconce replace ref{sec:ode_sys} 1.4 tmp.do.txt 50 | 51 | #more hacks to remove footnote labels, not supported by notebook format 52 | #footnote bodies should be removed by preprocessor if statements 53 | system doconce subst "\[\^.*\]" "" tmp.do.txt 54 | system doconce format ipynb tmp --allow_refs_to_external_docs #$opt 55 | mv -f tmp.ipynb ../../docs/ipynb/$name.ipynb 56 | done 57 | 58 | #system makeindex $name 59 | #system pdflatex $name 60 | 61 | # Publish 62 | #cp book.pdf ../../pub 63 | 64 | # index file for book and all chapters 65 | #cd ../chapters 66 | #cp index_files.do.txt index.do.txt 67 | #system doconce format html index --html_style=bootstrap --html_links_in_new_window --html_bootstrap_navbar=off 68 | #cp index.html ../../pub 69 | #rm -f index.* 70 | #cd - 71 | 72 | # Report typical problems with the book (too long lines, 73 | # undefined labels, etc.). Here we report lines that are more than 10pt 74 | # too long. 75 | #doconce latex_problems $name.log 10 76 | 77 | # Check grammar in MS Word: 78 | # doconce spellcheck tmp_mako__book.do.txt 79 | # load tmp_stripped_book.do.txt into Word 80 | -------------------------------------------------------------------------------- /book_src/book/make_pdf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | # Compile the book to LaTeX/PDF. 3 | # 4 | # Usage: make.sh [nospell] 5 | # 6 | # With nospell, spellchecking is skipped. 7 | 8 | set -x 9 | 10 | name=ode_book 11 | #name=test 12 | #encoding="--encoding=utf-8" 13 | 14 | CHAPTER=chapter 15 | BOOK=book 16 | APPENDIX=appendix 17 | 18 | function system { 19 | "$@" 20 | if [ $? -ne 0 ]; then 21 | echo "make.sh: unsuccessful command $@" 22 | echo "abort!" 23 | exit 1 24 | fi 25 | } 26 | 27 | #rm tmp_* 28 | 29 | if [ $# -ge 1 ]; then 30 | spellcheck=$1 31 | else 32 | spellcheck=spell 33 | fi 34 | 35 | #preprocess -DFORMAT=pdflatex ../chapters/newcommands.p.tex > newcommands_keep.tex 36 | 37 | opt="CHAPTER=$CHAPTER BOOK=$BOOK APPENDIX=$APPENDIX" 38 | 39 | system doconce format pdflatex $name $opt --device=paper --exercise_numbering=chapter --latex_style=Springer_sv --latex_title_layout=std --latex_list_of_exercises=none --latex_admon=mdfbox --latex_admon_color=1,1,1 --latex_table_format=left --latex_admon_title_no_period --latex_no_program_footnotelink --allow_refs_to_external_docs "--latex_code_style=default:lst[style=blue1]@pypro:lst[style=blue1bar]@dat:lst[style=gray]@sys:vrb[frame=lines,label=\\fbox{{\tiny Terminal}},framesep=2.5mm,framerule=0.7pt]" #--latex_index_in_margin 40 | 41 | # Auto edits 42 | #doconce replace 'linecolor=black,' 'linecolor=darkblue,' $name.tex 43 | #doconce subst 'frametitlebackgroundcolor=.*?,' 'frametitlebackgroundcolor=blue!5,' $name.tex 44 | 45 | rm -rf $name.aux $name.ind $name.idx $name.bbl $name.toc $name.loe 46 | 47 | system pdflatex $name 48 | system bibtex $name 49 | system makeindex $name 50 | system pdflatex $name 51 | system pdflatex $name 52 | -------------------------------------------------------------------------------- /book_src/book/ode_book.do.txt: -------------------------------------------------------------------------------- 1 | # Note on the Springer T4 style: here we use the modifications 2 | # introduced in t4do.sty and svmonodo.sty (both are bundled with DocOnce). 3 | 4 | TITLE: Solving Ordinary Differential Equations in Python 5 | AUTHOR: Joakim Sundnes at Simula Research Laboratory 6 | DATE: today 7 | 8 | ## Handy mako variables and functions for the preprocessing step 9 | # #include "../chapters/mako_code.txt" 10 | 11 | ## Externaldocuments: ../chapters/fake/main_fake 12 | 13 | 14 | ========= Preface ========= 15 | label{ch:preface} 16 | # #include "../chapters/preface_ode.do.txt" 17 | 18 | TOC: on 19 | 20 | !split 21 | ========= Programming a simple ODE solver ========= 22 | label{ch:ode_intro} 23 | # #include "../chapters/chapter1.do.txt" 24 | 25 | !split 26 | ========= Improving the accuracy ========= 27 | label{ch:runge_kutta} 28 | # #include "../chapters/rungekutta1.do.txt" 29 | 30 | 31 | 32 | !split 33 | ========= Stable solvers for stiff ODE systems ========= 34 | label{ch:stiff} 35 | # #include "../chapters/rungekutta2.do.txt" 36 | 37 | !split 38 | ========= Adaptive time step methods ========= 39 | label{ch:adaptive} 40 | # #include "../chapters/adaptive.do.txt" 41 | 42 | 43 | !split 44 | ========= Modeling infectious diseases ========= 45 | label{ch:disease_models} 46 | # #include "../chapters/disease_modeling.do.txt" 47 | 48 | !split 49 | ========= Appendix: Programming of difference equations ========= 50 | label{ch:diff_eq} 51 | # #include "../chapters/diffeq.do.txt" 52 | 53 | !split 54 | ========= References ========= 55 | BIBFILE: ../bib/papers.pub 56 | 57 | -------------------------------------------------------------------------------- /book_src/book/t4do.sty: -------------------------------------------------------------------------------- 1 | % This is the Springer T4 style, based on the T2 style, but 2 | % different page/text size. All length measures are multiplied by 3 | % 0.92 compared to t2do.sty. 4 | 5 | % Modifications by Hans Petter Langtangen for Doconce-generated LaTeX books: 6 | % 7 | % * redefinition of section, figure, table counters 8 | % * utf8 is loaded instead of latin1 9 | % * no itshape, but larger font in subsection 10 | % * paragraph has sf bold font (originally, t2 relies on svmono paragrah, 11 | % but a significantly modified svmono paragraph is used here in this t2) 12 | % * definition of \subex for headings in Doconce subexercises 13 | % * no makefntext as that construction interferes with 14 | % mdframed frames, but using the footmisc package instead 15 | % (search for hpl to see the modifications compared to standard t2.sty) 16 | % 17 | % This version was used for the 4th ed. of the Springer book TCSE6. 18 | 19 | %\usepackage{graphicx} % define this in .tex file 20 | %\usepackage[latin1]{inputenc} 21 | \usepackage{amsmath,amsfonts,amssymb} 22 | %\usepackage{slogo} 23 | \usepackage{caption} 24 | %\usepackage{mathptmx} 25 | %\usepackagep{mathfsb} 26 | \usepackage{tabularx} 27 | \usepackage{multicol} 28 | \usepackage[T1]{fontenc} 29 | 30 | 31 | \newcolumntype{P}{>{\rightskip0pt plus12mm\hangindent12pt\hangafter1}X} 32 | 33 | 34 | \paperwidth178mm 35 | \paperheight254mm 36 | \topmargin7mm 37 | % NOTE: \evensidemargin and \oddsidemargin have no effect here if 38 | % \mymainmatter is used! 39 | \evensidemargin48.3mm 40 | \oddsidemargin13.3mm 41 | \textwidth117mm 42 | \textheight190mm 43 | \columnwidth76mm 44 | \columnsep4mm 45 | \headheight3.5mm 46 | \headsep3.5mm 47 | 48 | \hoffset-1in 49 | \voffset-1in 50 | 51 | \parskip0pt 52 | \parindent12pt 53 | 54 | \normalbaselineskip12.5pt 55 | \baselineskip12.5pt 56 | \def\normalsize{\fontsize{11pt}{13.5pt}\selectfont} 57 | \def\small{\fontsize{8.5pt}{10pt}\selectfont} 58 | \def\qut#1{{``}#1{''}} 59 | \def\squt#1{{`}#1{'}} 60 | 61 | \def\fullcolor{\color[gray]{0.85}} 62 | %\def\shadedcolor{\color[cmyk]{0.19,0.1,0,0}} 63 | 64 | \newcommand\mymainmatter{% 65 | \mainmatter 66 | % These are appropriate for printed book 67 | %\evensidemargin48.3mm 68 | %\oddsidemargin13.3mm 69 | % These look better in PDF and on paper 70 | \evensidemargin34mm 71 | \oddsidemargin23.3mm 72 | \textwidth117mm 73 | \pagestyle{headings} 74 | \def\l@section{\vskip6pt\@dottedtocline{1}{0pt}{\tocsecnum}}% 75 | \def\l@subsection{\@dottedtocline{2}{\tocsecnum}{\tocsubsecnum}}% 76 | } 77 | 78 | \newdimen\@headskip 79 | \def\@makechapterhead#1{% 80 | \vbox to15\baselineskip{% 81 | \setbox0\vbox{% 82 | \begin{minipage}[t]{110.6mm} 83 | \hyphenpenalty \@M 84 | \interlinepenalty\@M 85 | \raggedright 86 | \color{black}% 87 | \fontsize{16pt}{18pt}\sffamily\bfseries\selectfont#1 88 | \expandafter\ifx\csname ch@psubtitle\endcsname\relax 89 | \else 90 | \vskip14pt 91 | \fontsize{14pt}{16pt}\sffamily\mdseries\selectfont\ch@psubtitle 92 | \fi 93 | \vskip22pt 94 | \expandafter\ifx\csname ch@pauthor\endcsname\relax 95 | \fontsize{12pt}{14pt}\sffamily\mdseries\selectfont\strut\par 96 | \else 97 | \fontsize{12pt}{14pt}\sffamily\mdseries\selectfont\ch@pauthor 98 | \fi 99 | \end{minipage}}% 100 | \@headskip38mm 101 | \advance\@headskip-\ht0 102 | \advance\@headskip-\dp0 103 | \advance\@headskip-24pt% 104 | \advance\@headskip22pt% 105 | \ifdim\@headskip<22pt 106 | \@headskip22pt 107 | \fi 108 | \setbox0\vbox{% 109 | %hpl: Reduce 90mm if chapter no is placed too far to the right 110 | \begin{minipage}[t]{90mm} 111 | \hyphenpenalty \@M 112 | \interlinepenalty\@M 113 | \raggedright 114 | \color{black}% 115 | \fontsize{16pt}{18pt}\sffamily\bfseries\selectfont#1\par 116 | \expandafter\ifx\csname ch@psubtitle\endcsname\relax 117 | \else 118 | \vskip14pt 119 | \fontsize{14pt}{16pt}\sffamily\mdseries\selectfont\ch@psubtitle\par 120 | \fi 121 | \vskip\@headskip 122 | \vskip22pt% 123 | \expandafter\ifx\csname ch@pauthor\endcsname\relax 124 | \fontsize{12pt}{14pt}\sffamily\mdseries\selectfont\strut\par 125 | \else 126 | \fontsize{12pt}{14pt}\sffamily\mdseries\selectfont\ch@pauthor\par 127 | \fi 128 | \end{minipage}% 129 | % \hskip4.2mm% 130 | \begin{minipage}[t]{12.5mm} 131 | \color{black}% 132 | \hbox to 12.5mm{\hfill\fontsize{36pt}{36pt}\sffamily\bfseries\selectfont \thechapter} 133 | \end{minipage} 134 | }% 135 | \fullcolor 136 | \rlap{\hbox{\vrule width165mm height2pt depth0pt}}% 137 | \vskip2pt 138 | \noindent\strut\kern35mm% 139 | \hbox{% 140 | \dimen0=\ht0% 141 | \dimen2=\dp0 142 | \advance\dimen0 12pt% 143 | \advance\dimen2 12pt% 144 | \rlap{\hbox{\vrule width130mm height\dimen0 depth\dimen2}}% 145 | \kern12pt\box0}% 146 | \global\let\ch@psubtitle=\undefined 147 | \global\let\ch@pauthor=\undefined 148 | \vfill 149 | }} 150 | 151 | \def\@makeschapterhead#1{% 152 | \if@mainmatter\else\moveleft38mm\fi 153 | \vbox to15\baselineskip{% 154 | \setbox0\vbox{% 155 | \begin{minipage}[t]{101mm} 156 | \hyphenpenalty \@M 157 | \interlinepenalty\@M 158 | \raggedright 159 | \color{black}% 160 | \fontsize{16pt}{18pt}\sffamily\bfseries\selectfont#1\par 161 | % \expandafter\ifx\csname ch@pauthor\endcsname\relax 162 | % \fontsize{12pt}{14pt}\sffamily\mdseries\selectfont\strut\par 163 | % \else 164 | % \fontsize{12pt}{14pt}\sffamily\mdseries\selectfont\ch@pauthor\par 165 | % \fi 166 | \end{minipage}}% 167 | \fullcolor 168 | \rlap{\hbox{\vrule width165mm height2pt depth0pt}}% 169 | \vskip2pt 170 | \noindent\strut\kern35mm% 171 | \hbox{% 172 | \dimen0=\ht0% 173 | \dimen2=\dp0 174 | \advance\dimen0 12pt% 175 | \advance\dimen2 12pt% 176 | \rlap{\hbox{\vrule width130mm height\dimen0 depth\dimen2}}% 177 | \kern12pt\box0}% 178 | \global\let\ch@psubtitle=\undefined 179 | \global\let\ch@pauthor=\undefined 180 | \vfill 181 | }} 182 | 183 | \renewcommand\section{\@startsection{section}{1}{\z@}% 184 | {-30\p@ \@plus -4\p@ \@minus -4\p@}% 185 | {16\p@ \@plus 4\p@ \@minus 4\p@}% 186 | {\sffamily\fontsize{12pt}{14.5pt}\selectfont\bfseries 187 | \rightskip=\z@ \@plus 8em\pretolerance=10000 }} 188 | % hpl redefinition, larger font: 189 | \renewcommand\section{\@startsection{section}{1}{\z@}% 190 | {-30\p@ \@plus -4\p@ \@minus -4\p@}% 191 | {16\p@ \@plus 4\p@ \@minus 4\p@}% 192 | {\sffamily\fontsize{13pt}{15.5pt}\selectfont\bfseries 193 | \rightskip=\z@ \@plus 8em\pretolerance=10000 }} 194 | % original 195 | \renewcommand\subsection{\@startsection{subsection}{2}{\z@}% 196 | {-30\p@ \@plus -4\p@ \@minus -4\p@}% 197 | {8\p@ \@plus 2\p@ \@minus 2\p@}% 198 | {\sffamily\fontsize{10pt}{12pt}\selectfont\bfseries\itshape 199 | \rightskip=\z@ \@plus 8em\pretolerance=10000 }} 200 | % hpl redefinition, no itshape, larger font: 201 | \renewcommand\subsection{\@startsection{subsection}{2}{\z@}% 202 | {-30\p@ \@plus -4\p@ \@minus -4\p@}% 203 | {8\p@ \@plus 2\p@ \@minus 2\p@}% 204 | {\sffamily\fontsize{12pt}{14pt}\selectfont\bfseries 205 | \rightskip=\z@ \@plus 8em\pretolerance=10000 }} 206 | 207 | % hpl redefinition, no itshape, almost original font: 208 | \renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}% 209 | {-24\p@ \@plus -3\p@ \@minus -3\p@}% 210 | {11.5\p@ \@plus 3\p@ \@minus 3\p@}% 211 | {\sffamily\fontsize{10pt}{12.5pt}\selectfont\bfseries 212 | \rightskip=\z@ \@plus 8em\pretolerance=10000 }} 213 | % hpl redefinition, with boldface and smaller spacing (for TCSE6, 4th ed.): 214 | % (taken from svmondo.cls, but sffamily font replaces bfseries for t2) 215 | \renewcommand\paragraph{\@startsection{paragraph}{4}{\z@}% 216 | {-6\p@ \@plus -4\p@ \@minus -4\p@}% 217 | {-0.5em \@plus -0.22em \@minus -0.1em}% 218 | {\sffamily\normalsize\bfseries}} 219 | % hpl: special definition of Doconce subexercise heading 220 | \newcommand\subex{\@startsection{paragraph}{4}{\z@}% 221 | {-6\p@ \@plus -4\p@ \@minus -4\p@}% 222 | {-0.5em \@plus -0.22em \@minus -0.1em}% 223 | {\sffamily\normalsize\bfseries}} 224 | 225 | % original lines, leading to arabic numbering also in appendices: 226 | %\def\thesection{\arabic{chapter}.\arabic{section}} 227 | %\def\thefigure{\arabic{chapter}.\arabic{figure}} 228 | %\def\thetable{\arabic{chapter}.\arabic{table}} 229 | % hpl corrected lines (from Thomas in le-tex): 230 | \def\thesection{\thechapter.\arabic{section}} 231 | \def\thefigure{\thechapter.\arabic{figure}} 232 | \def\thetable{\thechapter.\arabic{table}} 233 | 234 | \leftmargini14pt 235 | \def\enumerate{% 236 | \ifnum \@enumdepth >\thr@@\@toodeep\else 237 | \advance\@enumdepth\@ne 238 | \edef\@enumctr{enum\romannumeral\the\@enumdepth}% 239 | \expandafter 240 | \list 241 | \csname label\@enumctr\endcsname 242 | {\labelwidth\leftmargini\labelsep0pt\topsep4pt\partopsep4pt\itemsep0pt\usecounter\@enumctr\def\makelabel##1{\rlap{##1}\hss}}% 243 | \fi} 244 | 245 | \def\itemize{% 246 | \ifnum \@itemdepth >\thr@@\@toodeep\else 247 | \advance\@itemdepth\@ne 248 | \edef\@itemitem{labelitem\romannumeral\the\@itemdepth}% 249 | \expandafter 250 | \list 251 | \csname\@itemitem\endcsname 252 | {\topsep4pt\partopsep4pt\def\makelabel##1{\hss\llap{##1}}}% 253 | \fi} 254 | 255 | \def\floatlegendstyle{\sffamily\bfseries\small\selectfont}%\fullcolor} 256 | 257 | \long\def\@makecaption#1#2{% 258 | \captionstyle\hsize118mm 259 | \ifx\@captype\fig@type 260 | \vskip0pt 261 | \fi 262 | \setbox\@tempboxa\hbox{{\floatlegendstyle #1\floatcounterend}% 263 | #2}% 264 | \ifdim \wd\@tempboxa >\hsize 265 | {\floatlegendstyle #1\floatcounterend} #2\par 266 | \else 267 | \hbox to\hsize{\leftlegendglue\unhbox\@tempboxa\hfil}% 268 | \fi 269 | \ifx\@captype\fig@type\else 270 | \vskip\tabcapgap 271 | \fi} 272 | 273 | \long\def\@makesplitcaption#1#2{% 274 | \captionstyle 275 | % \ifx\@captype\fig@type 276 | % \vskip0pt 277 | % \fi 278 | \setbox\@tempboxa\hbox{{\floatlegendstyle #1\floatcounterend}#2}% 279 | \ifdim \wd\@tempboxa >\hsize 280 | \setbox\@tempboxa\vbox{\hsize\columnwidth{\floatlegendstyle #1\floatcounterend} #2\par}% 281 | \ifdim\ht\@tempboxa>33pt% 282 | \vspace*{-2mm}% 283 | \begin{multicols}{2} 284 | {\floatlegendstyle #1\floatcounterend} #2% 285 | \end{multicols}% 286 | \else 287 | {\floatlegendstyle #1\floatcounterend} #2\par 288 | \fi 289 | \else 290 | \hbox to\hsize{\leftlegendglue\unhbox\@tempboxa\hfil}% 291 | \fi 292 | \ifx\@captype\fig@type\else 293 | \vskip\tabcapgap 294 | \fi} 295 | 296 | \setlength\textfloatsep{25pt plus10pt} 297 | \setlength\dbltextfloatsep{25pt} 298 | 299 | \def\endhline{% 300 | \noalign{\ifnum0=`}\fi\vskip3pt{\fullcolor\hrule \@height 1pt} \futurelet 301 | \reserved@a\@xhline} 302 | 303 | \def\hline{% 304 | \noalign{\ifnum0=`}\fi\vskip3pt{\fullcolor\hrule \@height 1pt} \vskip3pt\futurelet 305 | \reserved@a\@xhline} 306 | 307 | \def\svhline{% 308 | \noalign{\ifnum0=`}\fi\vskip3pt{\fullcolor\hrule \@height 1.5pt} \vskip3pt\futurelet 309 | \reserved@a\@xhline} 310 | 311 | \flushbottom 312 | 313 | \def\@chapter[#1]#2{\if@chapnum % war mal \ifnum \c@secnumdepth >\m@ne 314 | \refstepcounter{chapter}% 315 | \if@mainmatter 316 | \typeout{\@chapapp\space\thechapter.}% 317 | \addcontentsline{toc}{chapter}{\protect 318 | \numberline{\thechapter\thechapterend}#1}% 319 | \else 320 | \addcontentsline{toc}{chapter}{#1}% 321 | \fi 322 | \else 323 | \addcontentsline{toc}{chapter}{#1}% 324 | \fi 325 | \chaptermark{#1}% 326 | \addtocontents{lof}{\protect\addvspace{10\p@}}% 327 | \addtocontents{lot}{\protect\addvspace{10\p@}}% 328 | \if@twocolumn 329 | \@topnewpage[\@makechapterhead{#2}]\@afterheading% 330 | \else 331 | \@makechapterhead{#2}% 332 | \@afterheading 333 | \fi} 334 | 335 | 336 | \def\markleft#1{% 337 | \begingroup 338 | \let\label\relax \let\index\relax \let\glossary\relax 339 | \expandafter\@markleft\@themark {#1}% 340 | \@temptokena \expandafter{\@themark}% 341 | \mark{\the\@temptokena}% 342 | \endgroup 343 | \if@nobreak\ifvmode\nobreak\fi\fi} 344 | \def\@markleft#1#2#3{\@temptokena {#1}% 345 | \unrestored@protected@xdef\@themark{{#3}{\the\@temptokena}}} 346 | 347 | \def\ps@headings{\let\@mkboth\markboth 348 | \def\@evenhead{\hspace*{-0.5mm}\vbox to\headheight{\hsize117mm\hbox 349 | to\hsize{\sffamily\fontsize{8.5pt}{8.5pt}\selectfont\rlap{\thepage}\hfil 350 | \leftmark}\vss\fullcolor\hrule\@height 1pt\normalcolor}\hss} 351 | \def\@oddhead{\if@mainmatter\else\kern-38mm\fi\vbox to\headheight{\hsize117.0mm\hbox to\hsize{\sffamily\fontsize{8.5pt}{8.5pt}\selectfont\rightmark\hfil 352 | \llap{\thepage}}\vss\fullcolor\hrule\@height 1pt\normalcolor}} 353 | \def\chaptermark##1{\markboth{{\if@chapnum %\ifnum\c@secnumdepth>\m@ne 354 | \thechapter\thechapterend\hskip\betweenumberspace\fi ##1}}{{\if@chapnum %\ifnum\c@secnumdepth>\m@ne 355 | \thechapter\thechapterend\hskip\betweenumberspace\fi ##1}}}%!!! 356 | \def\sectionmark##1{\markright{{\ifnum\c@secnumdepth>\z@ 357 | \thesection\seccounterend\hskip\betweenumberspace\fi ##1}}}} 358 | 359 | \def\ps@myheadings{\let\@mkboth\@gobbletwo 360 | \let\@oddfoot\@empty\let\@evenfoot\@empty 361 | \def\@evenhead{\runheadsize\runheadstyle\rlap{\thepage}\hfil 362 | \leftmark} 363 | \def\@oddhead{\runheadsize\runheadstyle\rightmark\hfil 364 | \llap{\thepage}} 365 | \let\chaptermark\@gobble 366 | \let\sectionmark\@gobble 367 | \let\subsectionmark\@gobble} 368 | 369 | 370 | \long\def\split@caption#1[#2]#3{\par\addcontentsline{\csname 371 | ext@#1\endcsname}{#1}{\protect\numberline{\csname 372 | the#1\endcsname}{\ignorespaces #2}}\begingroup 373 | \@parboxrestore\if@minipage\@setminipage\fi 374 | \@makesplitcaption{\csname fnum@#1\endcsname}{\ignorespaces #3}\par 375 | \endgroup} 376 | 377 | \newif\if@splitcap 378 | \def\sidec@ption[#1]#2\caption{% 379 | \setbox\bildb@x=\hbox{\ignorespaces#2\unskip}% 380 | \if@twocolumn 381 | \ifdim\hsize<\textwidth 382 | \else 383 | \ifdim\wd\bildb@x<\columnwidth 384 | \typeout{Double column float fits into single column - 385 | ^^Jyou'd better switch the environment. }% 386 | \fi 387 | \fi 388 | \fi 389 | \if@twocolumn 390 | \ifdim\hsize=\textwidth 391 | \ifdim\wd\bildb@x>120mm 392 | \@splitcaptrue 393 | \else 394 | \@splitcapfalse 395 | \fi 396 | \fi 397 | \fi 398 | \instindent=\ht\bildb@x 399 | \advance\instindent by\dp\bildb@x 400 | \if t#1 401 | \else 402 | \instindent=-\instindent 403 | \fi 404 | \@tempdimb=\hsize 405 | \advance\@tempdimb by-\figgap 406 | \advance\@tempdimb by-\wd\bildb@x 407 | \ifdim\@tempdimb<3.6cm 408 | \ClassWarning{SVMult}{\string\sidecaption: No sufficient room for the legend; 409 | ^^Jusing normal \string\caption}% 410 | \unhbox\bildb@x 411 | \if@splitcap 412 | \let\@capcommand=\split@caption 413 | \else 414 | \let\@capcommand=\@caption 415 | \fi 416 | \else 417 | \toks@\expandafter{\captionstyle\sloppy 418 | \rightskip=\z@\@plus6mm\relax}% 419 | \def\captionstyle{\the\toks@}% 420 | \let\@capcommand=\@sidecaption 421 | \fi 422 | \refstepcounter\@captype 423 | \@dblarg{\@capcommand\@captype}} 424 | \long\def\@sidecaption#1[#2]#3{\addcontentsline{\csname 425 | ext@#1\endcsname}{#1}{\protect\numberline{\csname 426 | the#1\endcsname}{\ignorespaces #2}}\begingroup 427 | \@parboxrestore 428 | \@makesidecaption{\csname fnum@#1\endcsname}{\ignorespaces #3}% 429 | \hfill 430 | \unhbox\bildb@x 431 | \par 432 | \endgroup 433 | } 434 | 435 | \newdimen\mydimen 436 | \newdimen\m@dimen 437 | \def\set@to@baselines#1{% 438 | \m@dimen12.5pt 439 | \divide#1 by\m@dimen 440 | \@tempcnta#1 441 | #1=\m@dimen 442 | \multiply#1\@tempcnta} 443 | 444 | \long\def\fitToLineGrid#1{% 445 | \boxmaxdepth0pt% 446 | \setbox0\vbox{#1\par}% 447 | \mydimen\ht0 448 | \advance\mydimen\dp0 449 | \set@to@baselines\mydimen 450 | \advance\mydimen12.5pt 451 | \advance\mydimen-\ht0 452 | \setbox0\vbox{\unvbox0\vskip\mydimen}% 453 | \box0 454 | } 455 | 456 | \emergencystretch2em 457 | 458 | %\def\c@minitocdepth{\relax} 459 | %\let\c@minitocdepth\relax 460 | %\usepackage{minitoc} 461 | %\mtcsetdepth{minitoc}{2} 462 | %\nomtcrule 463 | %\def\beforeminitoc{} 464 | %\def\mtifont{\sffamily\fontsize{12pt}{14pt}\selectfont\bfseries\fullcolor} 465 | %\mtcsetfont{minitoc}{section}{\small} 466 | %\mtcsetfont{minitoc}{subsection}{\small} 467 | %\mtcindent0pt 468 | 469 | 470 | \renewcommand\frontmatter{\startnewpage 471 | %\oddsidemargin52mm 472 | \oddsidemargin27mm 473 | \textwidth109mm 474 | \linewidth109mm 475 | \hsize118mm 476 | \@mainmatterfalse\pagenumbering{roman} 477 | \pagestyle{fheadings} 478 | } 479 | 480 | 481 | \def\locentry#1#2{% 482 | \noindent\textbf{#1}\kern6pt#2\vskip6.25pt} 483 | 484 | 485 | 486 | \usepackage{marvosym} 487 | 488 | \@addtoreset{section}{chapter} 489 | \@addtoreset{figure}{chapter} 490 | \@addtoreset{table}{chapter} 491 | \@addtoreset{equation}{chapter} 492 | \@addtoreset{footnote}{chapter} 493 | 494 | \tolerance=1000 495 | \tabcolsep5pt 496 | 497 | 498 | \RequirePackage{textcomp} 499 | \def\umu{\ensuremath{{\text{\textmu}}}} 500 | 501 | 502 | \let\texteuro\EURtm 503 | 504 | 505 | \setlength\footnotesep{7.7pt} 506 | \renewcommand\footnoterule{% 507 | \kern-3\p@ 508 | \hrule\@width 50\p@ 509 | \kern2.6\p@} 510 | %\newdimen\foot@parindent 511 | %\foot@parindent 10.83\p@ 512 | %\long\def\@makefntext#1{\@setpar{\@@par\@tempdima 118mm%\hsize 513 | % \advance\@tempdima-\foot@parindent\parshape\@ne\foot@parindent 514 | % \@tempdima}\par 515 | % \parindent \foot@parindent\noindent \hbox to \z@{% 516 | % \hss\hss$^{\mathrm{\@thefnmark}}$ }#1} 517 | % hpl modification: removed makefntext and used footmisc package 518 | % instead since it works with the mdframed package. 519 | % The result is approx the same spacing as that produced by makefntext above. 520 | \usepackage[ragged]{footmisc} 521 | \renewcommand{\footnotemargin}{8pt} 522 | \renewcommand{\footnotelayout}{\hspace{1pt}} 523 | % NOTE: If there are footnotes with verbatim content, the combination of 524 | % footmisc and \VerbatimFootnotes leads to strange typesetting (the 525 | % text in the footnote appears *under* the number). One remedy is to 526 | % comment out the use of footmisc or to comment out \VerbatimFootnotes 527 | % in the .tex file (but then we need to ensure that all verbatim text 528 | % in footnotes can be handled by \texttt. 529 | 530 | 531 | 532 | \xdef\@pnumwidth{8mm} 533 | 534 | 535 | \def\tableofcontents{\chapter*{\contentsname}% 536 | \markboth{{\contentsname}}{{\contentsname}}% 537 | \def\authcount##1{\setcounter{auco}{##1}\setcounter{@auth}{1}} 538 | \def\lastand{\ifnum\value{auco}=2\relax 539 | \unskip{} \andname\ 540 | \else 541 | \unskip \lastandname\ 542 | \fi}% 543 | \def\and{\stepcounter{@auth}\relax 544 | \ifnum\value{@auth}=\value{auco}% 545 | \lastand 546 | \else 547 | \unskip, 548 | \fi}% 549 | \@starttoc{toc}\if@restonecol\twocolumn\fi} 550 | 551 | \def\ps@vheadings{\let\@mkboth\markboth 552 | \let\@oddfoot\@empty\let\@evenfoot\@empty 553 | \def\@evenhead{\vbox to\headheight{\hsize144mm\hbox 554 | to\hsize{\sffamily\fontsize{8.5pt}{8.5pt}\selectfont\rlap{\thepage}\hfil 555 | \leftmark}\vss\fullcolor\hrule\@height 1pt\normalcolor}} 556 | \def\@oddhead{\if@mainmatter\else\kern-35mm\fi\vbox to\headheight{\hsize144mm\hbox to\hsize{\sffamily\fontsize{8.5pt}{8.5pt}\selectfont\rightmark\hfil 557 | \llap{\thepage}}\vss\fullcolor\hrule\@height 1pt\normalcolor}} 558 | \def\chaptermark##1{\markboth{{\ifnum\c@secnumdepth>\m@ne 559 | \thechapter\thechapterend\hskip\betweenumberspace\fi ##1}}{{\ifnum %!!! 560 | \c@secnumdepth>\m@ne \thechapter\thechapterend\hskip\betweenumberspace\fi ##1}}}%!!! 561 | \def\authormark##1{\markleft{##1}}% 562 | \def\sectionmark##1{}} 563 | 564 | \def\ps@fheadings{\let\@mkboth\markboth 565 | % \let\@oddfoot\@empty\let\@evenfoot\@empty 566 | \def\@evenhead{\vbox to\headheight{\hsize156mm\hbox 567 | to\hsize{\sffamily\fontsize{8.5pt}{8.5pt}\selectfont\rlap{\thepage}\hfil 568 | \leftmark}\vss\fullcolor\hrule\@height 1pt\normalcolor}} 569 | \def\@oddhead{\if@mainmatter\else\kern-35mm\fi\vbox to\headheight{\hsize156mm\hbox to\hsize{\sffamily\fontsize{8.5pt}{8.5pt}\selectfont\rightmark\hfil 570 | \llap{\thepage}}\vss\fullcolor\hrule\@height 1pt\normalcolor}} 571 | \def\chaptermark##1{\markboth{{\ifnum\c@secnumdepth>\m@ne 572 | \thechapter\thechapterend\hskip\betweenumberspace\fi ##1}}{{\ifnum %!!! 573 | \c@secnumdepth>\m@ne \thechapter\thechapterend\hskip\betweenumberspace\fi ##1}}}%!!! 574 | \def\authormark##1{\markleft{##1}}% 575 | \def\sectionmark##1{\markleft{\thesection\hskip\betweenumberspace##1}}} 576 | -------------------------------------------------------------------------------- /book_src/chapters/.gitignore: -------------------------------------------------------------------------------- 1 | cut_text/ -------------------------------------------------------------------------------- /book_src/chapters/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Run from subdirectory (chapter directory) 3 | doconce clean 4 | rm -rf *.html *.pdf *.tex reveal.js deck.js automake* newcommands* Trash sphinx 5 | -------------------------------------------------------------------------------- /book_src/chapters/figs_appA/comic_root.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_appA/comic_root.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_appA/comic_root.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_appA/comic_root.png -------------------------------------------------------------------------------- /book_src/chapters/figs_appA/diffeq_integral_of_Gaussian.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_appA/diffeq_integral_of_Gaussian.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_appA/diffeq_integral_of_Gaussian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_appA/diffeq_integral_of_Gaussian.png -------------------------------------------------------------------------------- /book_src/chapters/figs_appA/logistic_diffeq.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | x0 = 100 # initial population 4 | rho = 5 # growth rate in % 5 | M = 500 # max population (carrying capacity) 6 | N = 200 # number of years 7 | 8 | index_set = range(N+1) 9 | x = np.zeros(len(index_set)) 10 | 11 | x[0] = x0 12 | for n in index_set[1:]: 13 | x[n] = x[n-1] + (rho/100) *x[n-1]*(1 - x[n-1]/float(M)) 14 | 15 | plt.plot(index_set, x) 16 | plt.xlabel('Time units') 17 | plt.ylabel('Population') 18 | plt.savefig('logistic_growth1.pdf') 19 | plt.savefig('logistic_growth1.png') 20 | plt.show() 21 | -------------------------------------------------------------------------------- /book_src/chapters/figs_appA/logistic_growth1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_appA/logistic_growth1.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_appA/logistic_growth1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_appA/logistic_growth1.png -------------------------------------------------------------------------------- /book_src/chapters/figs_ch1/FE_n_10_20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch1/FE_n_10_20.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch1/FE_n_10_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch1/FE_n_10_20.png -------------------------------------------------------------------------------- /book_src/chapters/figs_ch1/logistic_func_mpl.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch1/logistic_func_mpl.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch1/logistic_func_mpl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch1/logistic_func_mpl.png -------------------------------------------------------------------------------- /book_src/chapters/figs_ch1/pendulum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch1/pendulum.png -------------------------------------------------------------------------------- /book_src/chapters/figs_ch1/pendulum_FE.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch1/pendulum_FE.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch1/pendulum_scipy.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch1/pendulum_scipy.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch1/pendulum_sketch.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch1/pendulum_sketch.pptx -------------------------------------------------------------------------------- /book_src/chapters/figs_ch2/ch2_simplecompare.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch2/ch2_simplecompare.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch3/stab_region_erk.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch3/stab_region_erk.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch3/stab_region_erk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch3/stab_region_erk.png -------------------------------------------------------------------------------- /book_src/chapters/figs_ch3/stab_region_irk0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch3/stab_region_irk0.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch3/stab_region_radau.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch3/stab_region_radau.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch3/vanderpol1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch3/vanderpol1.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch3/vanderpol2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch3/vanderpol2.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch3/vanderpol_be.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch3/vanderpol_be.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch3/vanderpol_irk.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch3/vanderpol_irk.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch4/hh_adaptive_tr_bdf2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch4/hh_adaptive_tr_bdf2.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch4/hodgkinhuxley_AP.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch4/hodgkinhuxley_AP.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch5/SEEIIR.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch5/SEEIIR.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch5/SEEIIR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch5/SEEIIR.png -------------------------------------------------------------------------------- /book_src/chapters/figs_ch5/SEIR.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch5/SEIR.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch5/SEIR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch5/SEIR.png -------------------------------------------------------------------------------- /book_src/chapters/figs_ch5/SIR0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch5/SIR0.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch5/SIR0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch5/SIR0.png -------------------------------------------------------------------------------- /book_src/chapters/figs_ch5/SIR1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch5/SIR1.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch5/SIR1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch5/SIR1.png -------------------------------------------------------------------------------- /book_src/chapters/figs_ch5/SIR_immunity_loss.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch5/SIR_immunity_loss.pdf -------------------------------------------------------------------------------- /book_src/chapters/figs_ch5/SIR_simple.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/book_src/chapters/figs_ch5/SIR_simple.pdf -------------------------------------------------------------------------------- /book_src/chapters/mako/.dict4spell.txt: -------------------------------------------------------------------------------- 1 | DocOnce 2 | Informatics 3 | Langtangen 4 | Linge 5 | Mako 6 | Mako's 7 | Matlab 8 | Preprocess 9 | Simula 10 | co 11 | hardcoded 12 | inline 13 | -------------------------------------------------------------------------------- /book_src/chapters/mako/main_mako.do.txt: -------------------------------------------------------------------------------- 1 | TITLE: Use of Mako to aid book writing 2 | AUTHOR: Hans Petter Langtangen at Center for Biomedical Computing, Simula Research Laboratory & Department of Informatics, University of Oslo 3 | DATE: today 4 | 5 | TOC: on 6 | 7 | 8 | # No common Mako variables and functions here since we cannot run mako 9 | ### #include "../mako_code.txt" 10 | 11 | 12 | # Running text 13 | # #include "mako.do.txt" 14 | 15 | ======= References ======= 16 | 17 | BIBFILE: ../papers.pub 18 | -------------------------------------------------------------------------------- /book_src/chapters/mako/make.sh: -------------------------------------------------------------------------------- 1 | # Cannot use ../make.sh because of the # INCLUDE construction 2 | #bash -x ../make.sh main_mako --no_mako 3 | 4 | # Must do a hack because of # #include examples in the file 5 | # (preprocess will try to include...so we write # INCLUDE instead 6 | # and edit here) 7 | 8 | mainname=$1 9 | shift 10 | args="$@" 11 | 12 | # Strip off main_ in $mainname to get the nickname 13 | nickname=`echo $mainname | sed 's/main_//'` 14 | 15 | # Individual chapter documents will have formulations like 16 | # "In this ${BOOK}" or "in this ${CHAPTER}" to be transformed 17 | # to "In this document" when the chapter stands on its own, while 18 | # for a book we want "In this book" and "in this chapter". 19 | CHAPTER=document 20 | BOOK=document 21 | APPENDIX=document 22 | 23 | # Function for running operating system commands. The script aborts 24 | # if the execution is unsuccessful. All doconce, latex, etc. commands 25 | # in this script are run with the system function such that the script 26 | # stops when the first error is encountered. 27 | function system { 28 | "$@" 29 | if [ $? -ne 0 ]; then 30 | echo "make.sh: unsuccessful command $@" 31 | echo "abort!" 32 | exit 1 33 | fi 34 | } 35 | 36 | rm -fr tmp_* 37 | 38 | # Perform spell checking 39 | system doconce spellcheck -d .dict4spell.txt *.do.txt 40 | 41 | # Copy common newcommands 42 | system preprocess -DFORMAT=pdflatex ../newcommands.p.tex > newcommands_keep.tex 43 | # Copy ptex2tex configuration file if not using the newer --latex_code_style=... 44 | #cp ../.ptex2tex.cfg . 45 | 46 | opt="CHAPTER=$CHAPTER BOOK=$BOOK APPENDIX=$APPENDIX" 47 | 48 | # Paper version (--device=paper) 49 | system doconce format pdflatex ${mainname} $opt --device=paper --latex_admon_color=1,1,1 --latex_admon=mdfbox $args --latex_list_of_exercises=toc --latex_table_format=left "--latex_code_style=default:lst[style=blue1]@pypro:lst[style=blue1bar]@dat:lst[style=gray]@sys:vrb[frame=lines,label=\\fbox{{\tiny Terminal}},framesep=2.5mm,framerule=0.7pt]" 50 | # code style: blue boxes, darker-color frame for complete boxs, and terminal 51 | # style for sys 52 | # alternative code style: blue boxes with plain verbatim for all code, special 53 | # terminal style for sys (gives larger colored framed than the lst-style above) 54 | #"--latex_code_style=default:vrb-blue1@sys:vrb[frame=lines,label=\\fbox{{\tiny Terminal}},framesep=2.5mm,framerule=0.7pt]" 55 | 56 | # Auto-editing of .tex file (tailored adjustments) 57 | doconce replace 'linecolor=black,' 'linecolor=darkblue,' ${mainname}.tex 58 | doconce subst 'frametitlebackgroundcolor=.*?,' 'frametitlebackgroundcolor=blue!5,' ${mainname}.tex 59 | doconce replace '# INCLUDE' '# #include' ${mainname}.tex 60 | # Special fix for unwanted footnote in URLs in tex demo code 61 | doconce subst 'sampler\.py\}\\footnote.+' 'sampler.py}}' ${mainname}.tex 62 | doconce subst 'sampler\.m\}\\footnote.+' 'sampler.m}}' ${mainname}.tex 63 | 64 | rm -rf ${mainname}.aux ${mainname}.ind ${mainname}.idx ${mainname}.bbl ${mainname}.toc ${mainname}.loe 65 | system pdflatex ${mainname} 66 | bibtex ${mainname} 67 | makeindex ${mainname} 68 | system pdflatex ${mainname} 69 | system pdflatex ${mainname} 70 | mv -f ${mainname}.pdf ${nickname}-4print.pdf # drop main_ prefix in PDF 71 | 72 | # Electronic version 73 | system doconce format pdflatex ${mainname} $opt --device=screen --latex_admon_color=1,1,1 --latex_admon=mdfbox $args --latex_list_of_exercises=toc --latex_table_format=left "--latex_code_style=default:vrb-blue1@sys:vrb[frame=lines,label=\\fbox{{\tiny Terminal}},framesep=2.5mm,framerule=0.7pt]" 74 | # Auto-editing of .tex file (tailored adjustments) 75 | doconce replace 'linecolor=black,' 'linecolor=darkblue,' ${mainname}.tex 76 | doconce subst 'frametitlebackgroundcolor=.*?,' 'frametitlebackgroundcolor=blue!5,' ${mainname}.tex 77 | system pdflatex ${mainname} 78 | bibtex ${mainname} 79 | makeindex ${mainname} 80 | system pdflatex ${mainname} 81 | system pdflatex ${mainname} 82 | mv -f ${mainname}.pdf ${nickname}.pdf # drop main_ prefix in PDF 83 | 84 | # Publish 85 | dest=/some/repo/some/where 86 | dest=../../../pub 87 | if [ ! -d $dest ]; then 88 | exit 0 # drop publishing 89 | fi 90 | dest=$dest/$nickname 91 | if [ ! -d $dest ]; then 92 | mkdir $dest 93 | mkdir $dest/pdf 94 | mkdir $dest/html 95 | fi 96 | cp ${nickname}*.pdf $dest/pdf/ 97 | 98 | # If published in an external repo and the current writing repo is 99 | # private, all the source files for programs need to be copied to 100 | # the publishing repo as well. 101 | 102 | # Could make other versions, A4, 2 pages per sheet, etc. 103 | -------------------------------------------------------------------------------- /book_src/chapters/mako/make_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Remember --no_mako (include in make.sh) 3 | # PDF 4 | bash make.sh main_mako --no_mako --no_abort 5 | # HTML 6 | bash ../make_html.sh main_mako --no_mako --no_abort 7 | # Must do a hack because of # #include examples in the file 8 | # (preprocess will try to include...so we write # INCLUDE instead 9 | # and edit here) 10 | doconce replace '# INCLUDE' '# #include' *.html ._*.html sphinx-uio/_build/html/*.html sphinx-uio/_build/html/.*.html 11 | dest=../../../pub/mako/html 12 | doconce replace '# INCLUDE' '# #include' $dest/*.html $dest/._*.html $dest/sphinx/*.html $dest/sphinx/.*.html 13 | -------------------------------------------------------------------------------- /book_src/chapters/mako/mako.do.txt: -------------------------------------------------------------------------------- 1 | Below we describe the work flow and how we can utilize many 2 | nice DocOnce features when writing chapters for a future, potential book 3 | project. 4 | 5 | !split 6 | ======= Use of variables ======= 7 | 8 | idx{variables in mako} 9 | idx{mako!variables} 10 | 11 | Mako is the preprocessor that is always run prior to translating DocOnce 12 | documents into a specific format. It means that your DocOnce source is 13 | actually a computer program where you can use variables and functions. 14 | 15 | Writing chapters that can both live their individual lives and be part of 16 | a book faces some challenges for which we have some nice solutions 17 | in the coming sections. 18 | 19 | The easiest way to utilize Mako is to introduce variables in the text. 20 | For example, one can introduce a variable `COPYRIGHT` 21 | for the type of copyright desired 22 | for authors. Most Mako variables in this text are upper case, but 23 | any legal variable name in Python is also a legal name in Mako. 24 | In the DocOnce source file we can replace the variable by its content 25 | by writing `${COPYRIGHT}`: 26 | 27 | !bc do 28 | AUTHOR: H. P. Langtangen ${COPYRIGHT} at Simula & UiO 29 | !ec 30 | The content of the variable can either be set at the command line 31 | as part of the `doconce format` command, 32 | 33 | !bc sys 34 | Terminal> doconce format html mydoc COPYRIGHT='{copyright|CC BY}' 35 | !ec 36 | or hardcoded in the DocOnce file (as a standard Python variable) inside 37 | the `<%...%>` directives (before the first use of the variable): 38 | 39 | !bc do 40 | <% 41 | COPYRIGHT = '{copyright|This work is released under a BSD license.}' 42 | %> 43 | !ec 44 | 45 | By having the copyright as a variable, we can use this variable for all 46 | authors to ensure consistency of copyrights, and we can easily compile 47 | different versions of the documents with different copyrights by just 48 | changing `COPYRIGHT=` on the command line. 49 | 50 | Mako variables can be used in loops and if tests. 51 | DocOnce always defines a variable `FORMAT` holding the chosen output 52 | format. This variable is often used for emitting different text depending 53 | on the format, e.g., 54 | 55 | !bc do 56 | See 57 | % if FORMAT in ('latex', 'pdflatex'): 58 | Section ref{mysec} 59 | % elif FORMAT == 'html': 60 | ref{mysec} 61 | % elif FORMAT == 'sphinx': 62 | ref{mysec} 63 | % else: 64 | the previous section 65 | % endif 66 | for more information. 67 | !ec 68 | 69 | ===== How to speak about ``this chapter'' ===== 70 | 71 | idx{`CHAPTER`} 72 | idx{`BOOK`} 73 | idx{`APPENDIX`} 74 | 75 | In a book you will often need the phrase ``this chapter'', but his is 76 | inappropriate if the chapter is a stand-alone document. Then you would 77 | rather say ``this document''. Similarly, ``this book'' must read 78 | ``this document'' in a stand-alone chapter. 79 | We have resolved this issue by introducing 80 | Mako variables `CHAPTER`, `BOOK`, and `APPENDIX` such that you 81 | write 82 | 83 | !bc do 84 | In this ${BOOK}, the convention is to use boldface for vectors. 85 | !ec 86 | For this to work, you need to define `CHAPTER`, `BOOK`, and `APPENDIX` 87 | as variables on the command line as part of the `doconce format` command: 88 | 89 | !bc sys 90 | Terminal> doconce format pdflatex ch2 --latex_code_style=pyg \ 91 | CHAPTER=document BOOK=document APPENDIX=document 92 | !ec 93 | When the book is compiled, you do 94 | 95 | !bc sys 96 | Terminal> doconce format pdflatex ch2 --latex_code_style=pyg \ 97 | CHAPTER=chapter BOOK=chapter APPENDIX=appendix 98 | !ec 99 | The `make*.sh` files found in `doc/src/chapter/` and 100 | `doc/src/book` make proper definitions of 101 | `CHAPTER`, `BOOK`, and `APPENDIX`. 102 | 103 | !split 104 | ======= How to make several variants of the text ======= 105 | 106 | idx{if tests in mako} 107 | idx{boolean in mako} 108 | idx{mako!if tests} 109 | idx{mako!boolean} 110 | 111 | Sometimes you want to write some text slightly differently if the 112 | chapter is a stand-alone document compared to the case when it is 113 | part of a book. Mako if tests are ideal for this. Suppose you 114 | introduce a Mako variable `ALONE` that is true/defined if the chapter 115 | is a stand-alone document and false/undefined if part of a book. Then 116 | you can simply write 117 | 118 | !bc pycod 119 | In this 120 | % if ALONE: 121 | rather small 122 | % else: 123 | large 124 | % endif 125 | ${BOOK} 126 | !ec 127 | Running `doconce format` with the option `-DALONE` will turn `ALONE` to 128 | true and the output is typically 129 | 130 | !bc 131 | In this rather small document 132 | !ec 133 | while for a book we skip `-DALONE` as argument to `doconce format`, 134 | which makes `ALONE` undefined, and we 135 | get the output 136 | 137 | !bc 138 | In this large book 139 | !ec 140 | 141 | Mako variables can be defined/undefined (boolean variables) or be 142 | standard strings: 143 | 144 | !bc pycod 145 | % if SOME_STRING_VARIABLE in ('value1', 'value2'): 146 | some running text 147 | % endif 148 | 149 | ... 150 | 151 | % if not SOME_BOOLEAN_VARIABLE: 152 | some other running text 153 | % else: 154 | yet more different text 155 | % endif 156 | !ec 157 | 158 | With Mako variables, you can easily comment out large portions 159 | of text by testing on some variable you do not intend to define: 160 | 161 | !bc pycod 162 | % if EXTRA: 163 | This is 164 | text that 165 | will never 166 | appear in the 167 | output. 168 | % endif 169 | !ec 170 | 171 | Also, it is straightforward to write more than one version of a 172 | chapter. For example, you may want to produce a version of a chapter 173 | that is tailored to a specific course, while you for general publishing 174 | on the Internet want a more general version, and maybe a third version 175 | when the chapter is included in a book for the international market. 176 | All this is easily done by if tests on appropriately defined Mako 177 | variables 178 | 179 | !bc pycod 180 | % if COURSE == 'IT1713': 181 | # Specific text for a course IT1713 182 | ... 183 | % elif COURSE == 'IT1713b': 184 | # Specific text for a the special IT1713b variant of the course 185 | ... 186 | % elif COURSE == 'general': 187 | # General text when the chapter is a stand-alone document 188 | ... 189 | % elif COURSE == 'book1': 190 | # Text when course is a part of a particular book 191 | ... 192 | % elif COURSE == 'book2': 193 | # Text when course is a part of another book 194 | ... 195 | % endif 196 | !ec 197 | 198 | !split 199 | ======= Mako's Python functions ======= 200 | 201 | idx{functions in mako} 202 | idx{mako!functions} 203 | 204 | The if tests above are fine to handle larger portions of text. What if you 205 | need to have four versions of just one word or very short text? 206 | A Mako function, defined as a standard Python function, 207 | is then more appropriate. 208 | 209 | ===== Basics of Mako functions ===== 210 | 211 | Here is a definition of a suitable Mako function, which must be 212 | defined inside 213 | `<%` and `%>` tags, using standard Python code: 214 | 215 | @@@CODE src-mako/mako_func.py 216 | 217 | In the running text you can call `chversion` with five arguments, 218 | corresponding to the desired text in the five cases, and when `doconce format` 219 | is run, the value of `COURSE` determines which of the five cases that is used. 220 | Here is an example on DocOnce text with a function call to `chversion`: 221 | 222 | !bc do 223 | It is extremely important to define the term *cure* accurately. 224 | Here we mean ${chversion('handle', 'handle', 225 | 'resolve', 'treat', 'resolve')}. 226 | !ec 227 | You can easily use long multi-line strings as arguments, e.g., 228 | 229 | !bc do 230 | ... ${chversion(""" 231 | Here comes 232 | a multi-line 233 | string""", 234 | 'short string', 235 | 'another short string', 236 | """4th 237 | multi-line 238 | string""", 239 | '5th string')} 240 | ... 241 | !ec 242 | 243 | !bnotice There are two types of Mako functions 244 | One type resembles Python functions, as demonstrated above. The other 245 | type employs a slightly different syntax and is exemplified in the file 246 | "`doc/src/chapters/index_files.do.txt`": "http://tinyurl.com/kukz8pt/index_files-do.txt". We refer to the "Mako syntax documentation": "http://docs.makotemplates.org/en/latest/syntax.html" for more information. 247 | !enotice 248 | 249 | ===== How to automatically generate a DocOnce file with repetitive structure ===== 250 | label{mako:pyscripts} 251 | 252 | To illustrate how Python and Mako can be used to efficiently 253 | generate repetitive structures with a minimum of manual work, 254 | we consider the following case. Suppose you have a DocOnce document 255 | made up of a number of sections, where the DocOnce source of each section 256 | resides in a subdirectory with name `issueX`, where `X` is an integer 257 | counter. You want to create a ``master'' DocOnce file that includes 258 | all the sections, e.g.. 259 | 260 | !bc do 261 | ======= Issue 1 ======= 262 | 263 | # INCLUDE "issue1/issue.do.txt" 264 | 265 | ======= Issue 2 ======= 266 | 267 | # INCLUDE "issue2/issue.do.txt" 268 | 269 | ======= Issue 3 ======= 270 | 271 | # INCLUDE "issue3/issue.do.txt" 272 | !ec 273 | Maybe issues come and go, and so do the subdirectories, implying that 274 | one should automate the making of the above content of the master 275 | document. 276 | 277 | Generating a set of sections via Mako is easy: 278 | 279 | !bc do 280 | <% 281 | sections = range(1, 8) 282 | %> 283 | 284 | % for i in sections: 285 | ======= Issue ${i} ======= 286 | % endfor 287 | !ec 288 | Unfortunately, we cannot write 289 | 290 | !bc do 291 | % for i in sections: 292 | ======= Issue ${i} ======= 293 | 294 | # INCLUDE "issue${i}/issue.do.txt" 295 | % endfor 296 | !ec 297 | because the `#include` statement is run by Preprocess *prior* to 298 | Mako's interpretation of the file. 299 | Instead, we can generate (parts of) the master file in a separate 300 | Python script. This makes it also easier to check which subdirectories 301 | we have and set up the contents of sections based on the file 302 | structure: 303 | 304 | !bc pycod 305 | import os, glob 306 | outfile = open('master_section.do.txt', 'w') 307 | subdirs = glob.glob('issue*') 308 | # Run through all issue* subdirectory names in sorted sequence 309 | for subdir in sorted(subdirs): 310 | if os.path.isdir(subdir): # directory? 311 | if os.path.isfile('issue.do.txt'): # file? 312 | # Extract number X from "issueX" name: 313 | no = subdir[5:] 314 | outfile.write(""" 315 | ======= Issue %s ======= 316 | 317 | # INCLUDE "%s/issue.do.txt" 318 | """ % (no, subdir)) 319 | outfile.close() 320 | !ec 321 | The master file can now just do an include of `master_sections.do.txt`. 322 | If the make script for compiling DocOnce to various formats first 323 | runs the script above, the `master_sections.do.txt` contents are 324 | up-to-date with the current file structure, and the contents 325 | automatically propagate to the master document. 326 | 327 | There is one potential problem in the above example: the `issue.do.txt` 328 | files may include figures with local paths. For example, 329 | `issue5/issue.do.txt` contains 330 | 331 | !bc do 332 | FIGURE: [fig/myfig, width=500 frac=0.8] My figure. label{my:fig} 333 | !ec 334 | When compiling the master document, no `fig/myfig.png` is found because 335 | the correct path, relative to the master document's directory, 336 | is `issue5/fig/myfig.png`. The same problem arises if there are 337 | source code inclusion statements like `@@@CODE src/myprog.f`. 338 | The master document would then need `@@@CODE issue5/src/myprog.f`. 339 | The best way out of these problems is 340 | 341 | o Let figure and source code directories have a unique name, 342 | say `fig5` and `src5` in this example. 343 | o Create links from the master document's directory to 344 | all the `fig*` and `src*` subdirectories. 345 | 346 | Point 2 can be automated by a little Python script: 347 | 348 | !bc pycod 349 | subdirs = glob.glob('issue*') 350 | for subdir in sorted(subdirs): 351 | if os.path.isdir(subdir): 352 | no = subdir[5:] 353 | figdir = 'fig' + no 354 | srcdir = 'src' + no 355 | if not os.path.islink(figdir): 356 | path = os.path.join(subdir, figdir) 357 | os.symlink(figdir, path) 358 | if not os.path.islink(srcdir): 359 | path = os.path.join(subdir, srcdir) 360 | os.symlink(figdir, path) 361 | !ec 362 | 363 | This little case study shows the power of using scripts 364 | to assist the writing process. Although Mako is very useful, 365 | turning to a separate Python program that generates text is 366 | even more useful. It is also much easier to debug a Python 367 | program than Mako code. 368 | 369 | ===== How to deal with almost repetitive structure ===== 370 | label{mako:almost:repeat} 371 | 372 | Sometimes you have a text, say some introduction, that is almost equal 373 | in various parts of the document. Here is a very simple example: 374 | ``This is an introduction for the institution's external users.'' versus 375 | ``This is an introduction for the institution's internal users.'' 376 | Just one word differs. We put the text in a separate file for inclusion 377 | (since real examples probably have longer texts that are more convenient 378 | to collect in separate files). Moreover, we parameterize the word 379 | that differs through a Mako variable `USER_TYPE`. 380 | The `intro.do.txt` file then reads 381 | 382 | !bc 383 | This is an introduction for the institution's ${USER_TYPE} users. 384 | !ec 385 | 386 | The `USER_TYPE` variable can either be set on the command line or in 387 | the document that includes `intro.do.txt` (prior to the include statement). 388 | Suppose we have a master document that needs to include `intro.do.txt` 389 | twice with both values of `USER_TYPE`: 390 | 391 | !bc do 392 | <% USER_TYPE = 'internal' %> 393 | 394 | ======= Information for ${USER_TYPE} users ======= 395 | 396 | # INCLUDE "intro.do.txt" 397 | 398 | ... 399 | 400 | <% USER_TYPE = 'external' %> 401 | 402 | ======= Information for ${USER_TYPE} users ======= 403 | 404 | # INCLUDE "intro.do.txt" 405 | !ec 406 | After running Mako, this looks like 407 | 408 | !bc do 409 | ======= Information for internal users ======= 410 | 411 | This is an introduction for the institution's internal users. 412 | 413 | ... 414 | 415 | ======= Information for external users ======= 416 | 417 | This is an introduction for the institution's external users. 418 | !ec 419 | 420 | Almost repetitive text can in many cases be parameterized by suitable 421 | Mako variables or Mako function calls to obtain the correct variations 422 | over a common theme that is placed in a single file (``document once''). 423 | 424 | 425 | ===== How to treat multiple programming languages in the same text ===== 426 | 427 | With these ideas, it becomes straightforward to write a book that 428 | has its program examples in multiple languages. Introduce `CODE` 429 | as the name of the language and use if tests for larger portions 430 | of code and text, and Mako functions for shorter inline texts, 431 | to handle text that depends on the value of `CODE`. 432 | The author has successfully co-written such a "book": 433 | "http://hplgit.github.io/Programming-for-Computations/pub/p4c/index.html" 434 | cite{Linge_Langtangen_2015} 435 | for mathematical programming with either Python or Matlab - the version 436 | is set when running `doconce format`. 437 | 438 | Here is an example of text, in the style of the mention book, 439 | where there are small differences 440 | depending on the programming language: 441 | 442 | !bc do 443 | The following ${CODE} function `sampler` does the job 444 | (see the file "${src('sampler')}": 445 | "https://github.com/myuser/myproject/src/${src('sampler')}"): 446 | 447 | ${copyfile('sampler')} 448 | 449 | Note that in ${CODE}, arrays start at index ${text2('0', '1')}. 450 | Array slices like ${verb2('vec[2:8]', 'vec(2:7)')} 451 | go from the first index (here `2`) up to 452 | ${text2('*but not including* the upper limit (here `8`)', 453 | '(including) the upper limit (here `7`)'}. 454 | % if CODE == 'Python': 455 | Also note that the file `sampler.py` is a module, meaning 456 | that we can call all the file's functions from other programs, 457 | including `sampler_vec`. 458 | % elif CODE == 'Matlab': 459 | Also note that only the `sampler` function can be called 460 | from other Matlab programs. If we want the alternative 461 | implementation in function `sampler_vec` to be reused 462 | by other programs, this function has to reside in a file 463 | `sampler_vec.py`. 464 | % endif 465 | !ec 466 | 467 | Here we have made use of a few Mako functions to easily 468 | choose between a Python or Matlab relevant text: 469 | 470 | * `src` for picking a filename with the right extension (`.py` or `.m`) 471 | * `copyfile` for constructing the right `@@@CODE` line for a Python or 472 | Matlab source code file 473 | * `text2` for picking the first (Python) or second (Matlab) argument 474 | * `verb2` for picking the first (Python) or second (Matlab) argument typeset in 475 | inline verbatim font 476 | 477 | The exact Mako code appears below. 478 | 479 | !bc do 480 | <% 481 | def src(filestem, url=None, verb=True): 482 | """Return filstem plus .m or .py.""" 483 | if CODE == "Python": 484 | filename = filestem + '.py' 485 | else: 486 | filename = filestem + '.m' 487 | if verb: 488 | filename = '`%s`' % filename 489 | if url is not None: 490 | # Make link to the file at github 491 | pass 492 | return filename 493 | 494 | def copyfile(filestem, from_=None, to_=None): 495 | """Return @@@CODE line for copying a Python/Matlab file.""" 496 | r = "@@@CODE " 497 | if CODE == "Python": 498 | r += "py-src/" + filestem + '.py' 499 | else: 500 | r += "m-src/" + filestem + '.m' 501 | if from_ is not None: 502 | r += ' fromto: ' + from_ + '@' 503 | if to_ is not None: 504 | r += to_ 505 | return r 506 | 507 | def verb2(py_expr, m_expr): 508 | """Return py_expr or m_expr in verbatim depending on CODE.""" 509 | if CODE == "Python": 510 | expr = py_expr 511 | else: 512 | expr = m_expr 513 | expr = '`%s`' % expr 514 | return expr 515 | 516 | def text2(py_expr, m_expr): 517 | """Return py_expr or m_expr depending on CODE.""" 518 | if CODE == "Python": 519 | expr = py_expr 520 | else: 521 | expr = m_expr 522 | return expr 523 | 524 | %> 525 | !ec 526 | 527 | Compiling the document with 528 | 529 | !bc sys 530 | Terminal> doconce format plain mydoc CODE=Python \ 531 | --latex_code_style=pyg 532 | !ec 533 | results in the output 534 | 535 | !bc 536 | The following Python function \Verb!sampler! does the job 537 | (see the file 538 | \href{{https://github.com/myuser/myproject/src/`sampler.py`}}{ 539 | \nolinkurl{sampler.py}}): 540 | 541 | \begin{minted}[fontsize=\fontsize{9pt}{9pt},linenos=false, 542 | baselinestretch=1.0,fontfamily=tt,xleftmargin=2mm]{python} 543 | """Sampler module.""" 544 | 545 | def sampler(...): 546 | ... 547 | \end{minted} 548 | 549 | Note that in Python, arrays start at index 0. 550 | Array slices like \Verb!vec[2:8]! 551 | go from the first index (here \Verb!2!) up to 552 | \emph{but not including} the upper limit (here \Verb!8!). 553 | Also note that the file \Verb!sampler.py! is a module, meaning 554 | that we can call all the file's functions from other programs, 555 | including \Verb!sampler_vec!. 556 | !ec 557 | Switching to `CODE=Matlab` gives 558 | 559 | !bc 560 | The following Matlab function \Verb!sampler! does the job 561 | (see the file 562 | \href{{https://github.com/myuser/myproject/src/`sampler.m`}}{ 563 | \nolinkurl{sampler.m}}): 564 | 565 | \begin{minted}[fontsize=\fontsize{9pt}{9pt},linenos=false, 566 | baselinestretch=1.0,fontfamily=tt,xleftmargin=2mm]{matlab} 567 | % Sampler code 568 | 569 | function samples = sampler(...): 570 | ... 571 | \end{minted} 572 | 573 | Note that in Matlab, arrays start at index 1. 574 | Array slices like \Verb!vec(2:7)! 575 | go from the first index (here \Verb!2!) up to 576 | (including) the upper limit (here \Verb!7!. 577 | Also note that only the \Verb!sampler! function can be called 578 | from other Matlab programs. If we want the alternative 579 | implementation in function \Verb!sampler_vec! to be reused 580 | by other programs, this function has to reside in a file 581 | \Verb!sampler_vec.py!. 582 | !ec 583 | 584 | === Another example === 585 | 586 | The manual contains a useful "example": "http://hplgit.github.io/doconce/doc/pub/manual/._manual024.html#manual:mako:nomenclature" 587 | on how to use Mako to implement 588 | the nomenclature functionality in the LaTeX package `nomencl`. 589 | 590 | === Yet another example === 591 | 592 | Here is a more complicated use of Mako for dealing with code files or 593 | strings in three different computer languages: 594 | 595 | !bc do 596 | Include a complete program in the language ${CODE}: 597 | 598 | ${code(filename='apb')} 599 | 600 | Include a portion of this file: 601 | 602 | # Recall that + is reserved char in regex, must be escaped 603 | ${code(filename='apb', from_regex='a =', to_regex=r'a \+ b')} 604 | 605 | Include a C++ program from file: 606 | 607 | ${code(filename='demo', language='C++')} 608 | 609 | Include a C++ program from computer code in text: 610 | 611 | ${code(language='C++', code=""" 612 | #include 613 | using namespace std; 614 | 615 | int main() 616 | { 617 | a = 1; 618 | b = 2; 619 | cout << a + b; 620 | return 0; 621 | } 622 | """)} 623 | !ec 624 | 625 | The point in this example is that we have a Mako variable `CODE` for 626 | the default language to be used, but we can also insert code in a 627 | specific language through the `language` argument in the Mako function 628 | `code`. This function can take a filename (`filename`) and include 629 | code from this file. We may make the convention that Python code is in 630 | `src/py`, C++ in `src/cpp`, and Fortran code in `src/f`. 631 | We just supply the filename without directory and extension, say `apb`, 632 | and the `code` function figures out the right directory and extension for 633 | us, based on the chosen default or specified language. 634 | Alternatively, the computer code can be supplied in a string as the 635 | `code` argument. 636 | 637 | What does the Mako function `code` look like? Before we show the statements, 638 | we remark that code of this complexity may be hard to debug inside 639 | DocOnce files. It is therefore better to put the code in a separate Python 640 | file and debug it externally. Here, we have put the code in `src/make/code.py`. 641 | In the DocOnce document we must include this file: 642 | 643 | !bc do 644 | <% 645 | # INCLUDE "src/mako/code.py" 646 | %> 647 | !ec 648 | The `code.py` file looks like 649 | 650 | !bc pycod 651 | import os 652 | src_path = 'src' 653 | 654 | def code(code='', filename='', 655 | language=None, 656 | from_regex=None, to_regex=None): 657 | # code can be a filename or computer code 658 | if language is None: 659 | language = CODE # Use global language if not specified 660 | if language == 'Python': 661 | if filename: 662 | filename += '.py' 663 | # Include from file 664 | text = '@@@CODE src/py/%s' % filename 665 | if from_regex is not None and to_regex is not None: 666 | # Include just a portion of the file 667 | text += ' fromto: %s@%s' % (from_regex, to_regex) 668 | elif from_regex is not None and to_regex is None: 669 | text += ' fromto: %s@' % (from_regex) 670 | else: 671 | # The code argumnet holds the actual computer code, 672 | # assume it's just a code snippet (not complete program) 673 | text = '!bc pycod\n%s\n!ec' % code.strip() 674 | elif language == 'Fortran': 675 | if filename: 676 | for ext in '.f', '.f90': 677 | if os.path.isfile(os.path.join( 678 | src_path, 'f', filename + ext)): 679 | filename += ext 680 | break 681 | # Include from file 682 | text = '@@@CODE src/f/%s' % filename 683 | if from_regex is not None and to_regex is not None: 684 | # Include just a portion of the file 685 | text += ' fromto: %s@%s' % (from_regex, to_regex) 686 | elif from_regex is not None and to_regex is None: 687 | text += ' fromto: %s@' % (from_regex) 688 | else: 689 | # The code argumnet holds the actual computer code, 690 | # assume it's just a code snippet (not complete program) 691 | text = '!bc fcod\n%s\n!ec' % code.strip() 692 | elif language == 'C++': 693 | if filename: 694 | for ext in '.cpp', '.c++', '.cxx': 695 | if os.path.isfile(os.path.join( 696 | src_path, 'cpp', filename + ext)): 697 | 698 | filename += ext 699 | break 700 | # Include from file 701 | text = '@@@CODE src/cpp/%s' % filename 702 | if from_regex is not None and to_regex is not None: 703 | # Include just a portion of the file 704 | text += ' fromto: %s@%s' % (from_regex, to_regex) 705 | elif from_regex is not None and to_regex is None: 706 | text += ' fromto: %s@' % (from_regex) 707 | else: 708 | # The code argumnet holds the actual computer code, 709 | # assume it's just a code snippet (not complete program) 710 | text = '!bc cppcod\n%s\n!ec' % code.strip() 711 | else: 712 | print 'language=%s is illegal' % language 713 | return text 714 | !ec 715 | -------------------------------------------------------------------------------- /book_src/chapters/mako/src-mako/mako_func.py: -------------------------------------------------------------------------------- 1 | <% 2 | def chversion(text_IT1413, text_IT1713b, text_general, 3 | text_book1, text_book2): 4 | if COURSE == 'IT1713': 5 | return text_IT1413 6 | elif COURSE == 'IT1713b': 7 | text_IT1413b 8 | elif COURSE == 'general': 9 | return text_general 10 | elif COURSE == 'book1': 11 | return text_book1 12 | elif COURSE == 'book2': 13 | return text_book2 14 | else: 15 | return 'XXX WRONG value of COURSE: %s' % COURSE 16 | %> 17 | -------------------------------------------------------------------------------- /book_src/chapters/mako_code.txt: -------------------------------------------------------------------------------- 1 | ## Mako variables and functions common to a lot of files 2 | ## (this file is included in, e.g., flow/main_flow.do.txt). 3 | 4 | <% 5 | # Note that goo.gl and bitly URLs cannot have slashes and continuation, 6 | # therefore we use tinyurl. 7 | 8 | src_path = 'https://github.com/hplgit/setup4book-doconce/tree/master/doc/src/chapters' 9 | src_path = 'http://tinyurl.com/kukz8pt' 10 | 11 | doc_path = 'http://hplgit.github.io/setup4book-doconce/doc/pub' 12 | doc_path = 'http://tinyurl.com/oul3xhn' 13 | 14 | #doc_index = doc_path + '/index.html' 15 | 16 | # All chapters: nickname -> title dict 17 | chapters = { 18 | 'rules': 'Directory and file structure', 19 | 'fake': 'Some document', 20 | 'mako': 'Use of Mako to aid book writing', 21 | } 22 | 23 | %> 24 | -------------------------------------------------------------------------------- /book_src/chapters/preface_ode.do.txt: -------------------------------------------------------------------------------- 1 | This book was based on a set of lecture notes written for the book *A Primer on Scientific Programming with Python* by 2 | Hans Petter Langtangen cite{langtangen2012primer}, mainly covering topics from Appendix A, C, 3 | and E. The original notes have been extended with more material on implicit solvers and 4 | automatic time stepping methods, to provide a more complete and balanced 5 | overview of state-of-the-art solvers for ordinary differential equations (ODEs). The 6 | main purpose of the notes is to serve as a brief and gentle introduction to solving 7 | differential equations in Python, for use in the course 8 | *Introduction to programming for scientific applications* (IN1900, 10 ETCS credits) at the 9 | University of Oslo. To read these notes one should have a basic knowledge of Python 10 | and NumPy, see for instance cite{sundnes2020introduction}, and it is also useful to have a 11 | fundamental understanding of ODEs. 12 | 13 | One may ask why this is useful to learn how to write your own ODE solvers in Python, when there are already 14 | multiple such solvers available, for instance in the SciPy library. However, no single ODE 15 | solver is the best and most efficient tool for all possible ODE problems, and the choice 16 | of solver should always be based on the characteristics of the problem. To make such choices, 17 | it is extremely useful to know the strengths and weaknesses of the different solvers, 18 | and the best way to obtain this knowledge is to program your own collection of ODE solvers. 19 | Different ODE solvers are also conveniently grouped into families and hierarchies of solvers, 20 | and provide an excellent example of how object-oriented programming (OOP) can be 21 | used to maximize code reuse and minimize duplication. 22 | 23 | The presentation style of the book is compact and pragmatic, and includes 24 | numerous code examples to illustrate how the various ODE solvers can 25 | be implemented and applied in practice. The complete source code for all examples, 26 | as well as Jupyter notebooks for all the chapters, is provided in the online resources 27 | accompanying this book. All the 28 | programs and code examples are written in a simple and 29 | compact Python style, and generally avoid the use of advanced tools and features. 30 | Experienced Python programmers will therefore surely find more elegant 31 | and modern solutions to many of the examples, including, for instance, abstract base 32 | classes, type hints, and data classes, to mention a few. However, the main goal of 33 | the book is to introduce the fundamentals of ODE solvers and OOP as part of an 34 | introductory programming course, and we believe this purpose is best served by 35 | focusing on the basics. Readers familiar with scientific computing or numerical 36 | software will probably also miss a discussion of computational performance. While 37 | performance is clearly relevant when solving ODEs, optimizing the performance of 38 | a Python based solver easily becomes quite technical, and requires features like 39 | just-in-time compilers (e.g., Numba) or mixed-language programming. The solvers in this 40 | book only use fairly basic features of Python and NumPy, which sacrifices some performance 41 | but enhances understanding of the solver properties and their implementation.[^source0] 42 | 43 | [^source0]: Complete 44 | source code for all the solvers and examples in the book can be found here: 45 | https://sundnes.github.io/solving\_odes\_in\_python/ 46 | 47 | The book is organized as follows. Chapter ref{ch:ode_intro} introduces the forward Euler method, 48 | and uses this simple method to introduce the fundamental ideas and principles that 49 | underpin all the methods considered later. The chapter introduces the notation and general 50 | mathematical formulation used throughout the book, both for scalar ODEs and systems of ODEs, 51 | and is essential reading for everyone with little prior experience with ODEs and ODE solvers. 52 | The chapter also briefly explains how to use the ODE solvers from the SciPy library. 53 | Readers already familiar with the fundamentals of the forward Euler method and its 54 | implementation may consider moving straight to Chapter ref{ch:runge_kutta}, which presents 55 | explicit Runge-Kutta methods. The fundamental ideas of the methods are introduced, and 56 | the main focus of the chapter is how a collection of ODE solvers can be implemented as 57 | a class hierarchy with minimal code duplication. Chapter ref{ch:stiff} introduces so-called 58 | *stiff* ODEs, and presents techniques for simple stability analysis of Runge-Kutta methods. 59 | The bulk of the chapter is then devoted to programming of implicit Runge-Kutta methods, 60 | which have better stability properties than explicit methods and therefore perform better 61 | for stiff ODEs. Chapter ref{ch:adaptive} then concludes the presentation of ODE solvers by 62 | introducing methods for adaptive time step control, which is an essential component of 63 | all modern ODE software. Chapter ref{ch:disease_models} is quite different 64 | from the preceding ones, since the focus is on a specific class of ODE models rather than 65 | a set of solvers. The simpler ODE problems considered in earlier chapters are useful for 66 | introducing and testing the solvers, but in order to appreciate both the 67 | potential and the challenges of modelling with ODEs it is useful to step beyond this. 68 | As an example of a real-world application of ODEs we have chosen the famous Kermack-McKendrick 69 | SIR (Susceptible-Infected-Recovered) model from epidemiology. These classic models were 70 | first developed in the early 1900s (see cite{SIR1}), and have 71 | received quite some attention in recent years, for obvious reasons. We derive the models 72 | from a set of fundamental assumptions, and discuss the implications and limitations resulting 73 | from these assumptions. The main focus of the chapter is then on how the models can be 74 | modified and extended to capture new phenomena, and how these changes can be implemented 75 | and explored using the solvers developed in preceding chapters. 76 | 77 | Although the focus of the text is on differential equations, Appendix ref{ch:diff_eq} is 78 | devoted to the related topic of *difference equations*. The motivation 79 | for including this chapter is that difference equations are closely related to 80 | ODEs, they have many important applications on their own, and numerical methods 81 | for ODEs are essentially methods for turning differential equations into 82 | difference equations. Solving difference equations can therefore be seen as 83 | a natural step on the way towards solving ODEs, and the standard formulation of 84 | difference equations in mathematical textbooks is already in a "computer-friendly" form, 85 | which is very easy to translate into a Python program using for-loops and arrays. 86 | Some students find difference equations easier to understand than differential equations, and 87 | may benefit from reading Appendix ref{ch:diff_eq} first, while others find it easier to 88 | go straight to the ODEs and leave Appendix ref{ch:diff_eq} for later. 89 | 90 | 91 | 92 | 93 | # #if FORMAT in ("latex", "pdflatex") 94 | ## Just dump native latex 95 | \noindent 96 | \ \\ 97 | {\it May 2023} \hfill {\it Joakim Sundnes} 98 | # #else 99 | *Joakim Sundnes*, May 2023 100 | # #endif 101 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Solving ODEs in Python 2 | 3 | This repository contains source code and Jupyter notebooks for the lecture notes ["Solving Ordinary Differential Equations in Python"](./ode_book.pdf), 4 | which was written for the introductory programming course "IN1900 – Introduction to Programming with Scientific Applications" at the University of Oslo. 5 | 6 | 7 | 8 | The purpose of the notes is to explain how to write generic and usable 9 | ODE solvers in Python. Parts of the notes are based on ["A Primer on Scientific Programming with Python"](https://link.springer.com/book/10.1007/978-3-662-49887-3). 10 | A complete pdf version of the notes can be found [here](./ode_book.pdf). 11 | 12 | ## Source code for code examples 13 | Most of the code examples are available for download as regular .py files: 14 | * Click [here](./src.zip) to download all the code examples as a single zip-file 15 | * Or browse the chapters and download individual .py-files [here](https://github.com/sundnes/solving_odes_in_python/tree/master/docs/src) 16 | Some of the code examples have been altered slightly from the book, to fix minor bugs 17 | and give more sensible output when running the files. 18 | 19 | ## Jupyter notebooks for all book chapters 20 | All the chapters of the book are available as Jupyter notebooks. 21 | * Click [here](./ipynb.zip) to download all the notebooks in a single zip file. 22 | * Or browse the individual chapter files [here](https://github.com/sundnes/solving_odes_in_python/tree/master/docs/ipynb). 23 | The ipynb files should render nicely when you view them on github, but to run the 24 | embedded Python code and make full use of the notebook format you need to 25 | download the files and run them locally using jupyter-notebook. 26 | 27 | It should be noted that the notebooks have been automatically generated from the book 28 | source files and may contain minor bugs and inconsistencies. Be particularly aware 29 | of the following: 30 | * The order of the notebook code cells is not always correct, since they were written 31 | for a static book-style and not intended to be live code. Most of the cells will 32 | run nicely if all previous cells have been run, but some will have problems with 33 | undefined variables and functions. 34 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | title: Solving ODEs in Python 3 | -------------------------------------------------------------------------------- /docs/figs/ode_cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/docs/figs/ode_cover.jpg -------------------------------------------------------------------------------- /docs/ipynb.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/docs/ipynb.zip -------------------------------------------------------------------------------- /docs/ipynb/ODESolver.py: -------------------------------------------------------------------------------- 1 | """ 2 | Version 2 of the ODESolver class hierarchy. This class works for systems 3 | of ODEs and for a single (scalar) ODE. 4 | """ 5 | 6 | import numpy as np 7 | 8 | 9 | class ODESolver: 10 | def __init__(self, f): 11 | # Wrap user's f in a new function that always 12 | # converts list/tuple to array (or let array be array) 13 | self.model = f 14 | self.f = lambda t, u: np.asarray(f(t, u), float) 15 | 16 | def set_initial_condition(self, u0): 17 | if isinstance(u0, (float, int)): # scalar ODE 18 | self.neq = 1 # no of equations 19 | u0 = float(u0) 20 | else: # system of ODEs 21 | u0 = np.asarray(u0) 22 | self.neq = u0.size # no of equations 23 | self.u0 = u0 24 | 25 | def solve(self, t_span, N): 26 | """Compute solution for t_span[0] <= t <= t_span[1], 27 | using N steps.""" 28 | t0, T = t_span 29 | self.dt = (T - t0) / N 30 | self.t = np.zeros(N + 1) # N steps ~ N+1 time points 31 | if self.neq == 1: 32 | self.u = np.zeros(N + 1) 33 | else: 34 | self.u = np.zeros((N + 1, self.neq)) 35 | 36 | self.t[0] = t0 37 | self.u[0] = self.u0 38 | 39 | for n in range(N): 40 | self.n = n 41 | self.t[n + 1] = self.t[n] + self.dt 42 | self.u[n + 1] = self.advance() 43 | return self.t, self.u 44 | 45 | 46 | class ForwardEuler(ODESolver): 47 | def advance(self): 48 | u, f, n, t = self.u, self.f, self.n, self.t 49 | 50 | dt = self.dt 51 | unew = u[n] + dt * f(t[n], u[n]) 52 | return unew 53 | 54 | 55 | class Heun(ODESolver): 56 | def advance(self): 57 | u, f, n, t = self.u, self.f, self.n, self.t 58 | dt = self.dt 59 | k1 = f(t[n], u[n]) 60 | k2 = f(t[n] + dt, u[n] + dt * k1) 61 | unew = u[n] + dt / 2 * (k1 + k2) 62 | return unew 63 | 64 | 65 | class ExplicitMidpoint(ODESolver): 66 | def advance(self): 67 | u, f, n, t = self.u, self.f, self.n, self.t 68 | dt = self.dt 69 | dt2 = dt / 2.0 70 | k1 = f(t[n], u[n]) 71 | k2 = f(t[n] + dt2, u[n] + dt2 * k1) 72 | unew = u[n] + dt * k2 73 | return unew 74 | 75 | 76 | class RungeKutta4(ODESolver): 77 | def advance(self): 78 | u, f, n, t = self.u, self.f, self.n, self.t 79 | dt = self.dt 80 | dt2 = dt / 2.0 81 | k1 = f(t[n], u[n],) 82 | k2 = f(t[n] + dt2, u[n] + dt2 * k1, ) 83 | k3 = f(t[n] + dt2, u[n] + dt2 * k2, ) 84 | k4 = f(t[n] + dt, u[n] + dt * k3, ) 85 | unew = u[n] + (dt / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4) 86 | return unew 87 | 88 | 89 | registered_solver_classes = [ 90 | ForwardEuler, ExplicitMidpoint, RungeKutta4] 91 | 92 | 93 | def test_exact_numerical_solution(): 94 | """ 95 | Test the different methods for a problem 96 | where the analytical solution is known and linear. 97 | All the methods should be exact to machine precision 98 | for this choice. 99 | """ 100 | a = 0.2 101 | b = 3 102 | 103 | def f(t, u): 104 | return a # + (u - u_exact(t))**5 105 | 106 | def u_exact(t): 107 | """Exact u(t) corresponding to f above.""" 108 | return a * t + b 109 | 110 | u0 = u_exact(0) 111 | T = 8 112 | N = 10 113 | tol = 1E-15 114 | #t_points = np.linspace(0, T, N) 115 | t_span = (0, T) 116 | for solver_class in registered_solver_classes: 117 | solver = solver_class(f) 118 | solver.set_initial_condition(u0) 119 | t, u = solver.solve(t_span, N) 120 | u_e = u_exact(t) 121 | max_error = (u_e - u).max() 122 | msg = f'{solver.__class__.__name__} failed with max_error={max_error}' 123 | assert max_error < tol, msg 124 | 125 | 126 | if __name__ == '__main__': 127 | test_exact_numerical_solution() 128 | -------------------------------------------------------------------------------- /docs/ipynb/ode_systems.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Solving systems of ODEs\n", 8 | "\n", 9 | "So far we have only considered ODEs with a single solution component, often called scalar ODEs.\n", 10 | "Many interesting processes can be described\n", 11 | "by systems of ODEs, i.e., multiple ODEs where the right hand side of one equation depends on the solution of the others. Such equation\n", 12 | "systems are also referred to as vector ODEs. One simple example is" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "$$\n", 20 | "\\begin{alignat*}{2}\n", 21 | "u' &= v, \\quad &&u(0) = 1\\\\\n", 22 | "v' &= -u, \\quad &&v(0) = 0.\n", 23 | "\\end{alignat*}\n", 24 | "$$" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "The solution of this system is $u=\\cos t, v=\\sin t$, which can easily be verified by insterting the solution into the equations\n", 32 | "and initial conditions. For more general cases, it is usually even more difficult to find analytical solutions of ODE systems\n", 33 | "than of scalar ODEs, and numerical methods are usually required. In this chapter we will extend the solvers introduced in\n", 34 | "Chapter 2 to be able to solve systems of ODEs. We shall see that such an extension requires relatively small\n", 35 | "modifications of the code.\n", 36 | "\n", 37 | "We want to develop general software that can be applied to any vector ODE or scalar ODE, and for this purpose it is\n", 38 | "useful to introduce general mathematical notation. We have $n$ unknowns" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "$$\n", 46 | "u^{(0)}(t), u^{(1)}(t), \\ldots, u^{(n-1)}(t)\n", 47 | "$$" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "in a system of $n$ ODEs:" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "$$\n", 62 | "\\begin{align*}\n", 63 | "{d\\over dt}u^{(0)} &= f^{(0)}(u^{(0)}, u^{(1)}, \\ldots, u^{(n-1)}, t),\\\\\n", 64 | "{d\\over dt}u^{(1)} &= f^{(1)}(u^{(0)}, u^{(1)}, \\ldots, u^{(n-1)}, t),\\\\\n", 65 | "\\vdots &= \\vdots\\\\\n", 66 | "{d\\over dt}u^{(n-1)} &= f^{(n-1)}(u^{(0)}, u^{(1)}, \\ldots, u^{(n-1)}, t).\n", 67 | "\\end{align*}\n", 68 | "$$" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "To simplify the notation (and later the implementation), we collect both the solutions $u^{(i)}(t)$\n", 76 | "and right-hand side functions $f^{(i)}$ in vectors;" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "$$\n", 84 | "u = (u^{(0)}, u^{(1)}, \\ldots, u^{(n-1)}),\n", 85 | "$$" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | "and" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "$$\n", 100 | "f = (f^{(0)}, f^{(1)}, \\ldots, f^{(n-1)}).\n", 101 | "$$" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "Note that $f$ is now a vector-valued function. It takes $n+1$ input arguments ($t$ and the $n$ components of $u$) and returns\n", 109 | "a vector of $n$ values.\n", 110 | "The ODE system can now be written" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "$$\n", 118 | "u' = f(u, t),\\quad u(0) = u_0\n", 119 | "$$" 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": [ 126 | "where $u$ and $f$ are vectors and $u_0$ is a vector of initial conditions. We see that we use exactly the\n", 127 | "same notation as for scalar ODEs, and whether we solve a scalar or system of ODEs is determined by how we define $f$ and the initial\n", 128 | "condition $u_0$. This general notation is completely standard in text books on ODEs, and we can easily make the Python\n", 129 | "implementation just as general.\n", 130 | "\n", 131 | "# An `ODESolver` class for systems of ODEs\n", 132 | "The `ODESolver` class above was written for a scalar ODE. We now want to make it work for a system\n", 133 | "$u'=f$, $u(0)=U_0$, where $u$, $f$ and $U_0$ are vectors (arrays). To identify how the code needs to be changed, let us start with\n", 134 | "the simplest method. Applying the forward Euler method to a system of ODEs yields an update formula that\n", 135 | "looks exactly as for the scalar case, but where all the terms are vectors:" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "metadata": {}, 141 | "source": [ 142 | "$$\n", 143 | "\\underbrace{u_{k+1}}_{\\mbox{vector}} =\n", 144 | "\\underbrace{u_k}_{\\mbox{vector}} +\n", 145 | "\\Delta t\\, \\underbrace{f(u_k, t_k)}_{\\mbox{vector}} .\n", 146 | "$$" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": {}, 152 | "source": [ 153 | "We could also write this formula in terms of the individual components, as in" 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": {}, 159 | "source": [ 160 | "$$\n", 161 | "u^{(i)}_{k+1} = u^{(i)}_{k} + \\Delta t f^{(i)}(u_{k},t_k), \\mbox{ for } i = 0,\\ldots , {n-1},\n", 162 | "$$" 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": {}, 168 | "source": [ 169 | "but the compact vector notation is much easier to read. Fortunately, the way we write the vector\n", 170 | "version of the formula is also how NumPy arrays are used in calculations. The\n", 171 | "Python code for the formula above may therefore look idential to the version for scalar ODEs;" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 1, 177 | "metadata": { 178 | "collapsed": false 179 | }, 180 | "outputs": [], 181 | "source": [ 182 | "u[k+1] = u[k] + dt*f(u[k], t)" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "with the important difference that both `u[k]` and `u[k+1]` are now arrays.\n", 190 | "Since these are arrays, the solution `u` must be a\n", 191 | "two-dimensional array, and `u[k],u[k+1]`, etc. are the rows of this array.\n", 192 | "The function `f` expects an array as its first argument, and must return a one-dimensional array,\n", 193 | "containing all the right-hand sides $f^{(0)},\\ldots,f^{(n-1)}$. To get a better\n", 194 | "feel for how these arrays look and how they are used,\n", 195 | "we may compare the array holding the solution of a scalar ODE to that of a system of two ODEs.\n", 196 | "For the scalar equation, both `t` and `u` are one-dimensional NumPy\n", 197 | "arrays, and indexing into `u` gives us numbers, representing the solution at each time step:" 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "metadata": {}, 203 | "source": [ 204 | " t = [0. 0.4 0.8 1.2 (...) ]\n", 205 | " \n", 206 | " u = [ 1.0 1.4 1.96 2.744 (...)]\n", 207 | " \n", 208 | " u[0] = 1.0\n", 209 | " u[1] = 1.4\n", 210 | " \n", 211 | " (...)\n" 212 | ] 213 | }, 214 | { 215 | "cell_type": "markdown", 216 | "metadata": {}, 217 | "source": [ 218 | "In the case of a system of two ODEs, `t` is still a one-dimensional array, but the solution array `u` is\n", 219 | "now two-dimensional, with one column for each solution component. Indexing into it\n", 220 | "yields one-dimensional arrays of length two, which are the two solution components\n", 221 | "at each time step:" 222 | ] 223 | }, 224 | { 225 | "cell_type": "markdown", 226 | "metadata": {}, 227 | "source": [ 228 | " u = [[1.0 0.8][1.4 1.1] [1.9 2.7] (...)]\n", 229 | " \n", 230 | " u[0] = [1.0 0.8]\n", 231 | " u[1] = [1.4 1.1]\n", 232 | " \n", 233 | " (...)\n" 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "metadata": {}, 239 | "source": [ 240 | "The similarity of the generic notation for vector and scalar ODEs, and the\n", 241 | "convenient algebra of NumPy arrays, indicate that the solver\n", 242 | "implementation for scalar and system ODEs can also be very similar. This is indeed true,\n", 243 | "and the `ODESolver` class from the previous chapter can be made to work for ODE\n", 244 | "systems by a few minor modifactions:\n", 245 | " * Ensure that `f(u,t)` always returns an array.\n", 246 | "\n", 247 | " * Inspect $U_0$ to see if it is a single number or a list/array/tuple and\n", 248 | " make the `u` either a one-dimensional or two-dimensional array\n", 249 | "\n", 250 | "If these two items are handled and initialized correctly, the rest of the code from\n", 251 | "Chapter 2 will in fact work with no modifications.\n", 252 | "The extended superclass implementation may look like:" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 2, 258 | "metadata": { 259 | "collapsed": false 260 | }, 261 | "outputs": [], 262 | "source": [ 263 | "class ODESolver:\n", 264 | " def __init__(self, f):\n", 265 | " # Wrap user's f in a new function that always\n", 266 | " # converts list/tuple to array (or let array be array)\n", 267 | " self.f = lambda u, t: np.asarray(f(u, t), float)\n", 268 | "\n", 269 | " def set_initial_condition(self, U0):\n", 270 | " if isinstance(U0, (float,int)): # scalar ODE\n", 271 | " self.neq = 1 # no of equations\n", 272 | " U0 = float(U0)\n", 273 | " else: # system of ODEs\n", 274 | " U0 = np.asarray(U0)\n", 275 | " self.neq = U0.size # no of equations\n", 276 | " self.U0 = U0\n", 277 | "\n", 278 | " def solve(self, time_points):\n", 279 | " self.t = np.asarray(time_points)\n", 280 | " N = len(self.t)\n", 281 | " if self.neq == 1: # scalar ODEs\n", 282 | " self.u = np.zeros(N)\n", 283 | " else: # systems of ODEs\n", 284 | " self.u = np.zeros((N,self.neq))\n", 285 | "\n", 286 | " # Assume that self.t[0] corresponds to self.U0\n", 287 | " self.u[0] = self.U0\n", 288 | "\n", 289 | " # Time loop\n", 290 | " for n in range(N-1):\n", 291 | " self.n = n\n", 292 | " self.u[n+1] = self.advance()\n", 293 | " return self.u, self.t" 294 | ] 295 | }, 296 | { 297 | "cell_type": "markdown", 298 | "metadata": {}, 299 | "source": [ 300 | "It is worth commenting on some parts of this code. First, the constructor looks\n", 301 | "almost identical to the scalar case, but we use a lambda function and\n", 302 | "`np.asarray` to convert any `f` that returns a list or tuple to a function\n", 303 | "returning a NumPy array. This modification is not strictly\n", 304 | "needed, since we could just assume that the user implements `f` to return\n", 305 | "an array, but it makes the class more robust and flexible. We have also included\n", 306 | "tests in the `set_initial_condition` method, to check if `U0` is a single\n", 307 | "number (`float`) or a NumPy array, and define the attribute `self.neq` to\n", 308 | "hold the number of equations.\n", 309 | "The final modification is found in the method `solve`, where\n", 310 | "the `self.neq` attribute is inspected and `u` is\n", 311 | "initialized to a one- or two-dimensional array of the correct size. The\n", 312 | "actual for-loop, as well as the implementation of the `advance` method in the\n", 313 | "subclasses, can be left unchanged.\n", 314 | "\n", 315 | "### Example: ODE model for throwing a ball.\n", 316 | "\n", 317 | "To demonstrate the use of\n", 318 | "the extended `ODESolver` hierarchy, let us derive and solve a system of ODEs\n", 319 | "describing the trajectory of a ball.\n", 320 | "We first define $x(t),y(t)$ to be the position of the ball, $v_x$ and $v_y$ the velocity components,\n", 321 | "and $a_x,a_y$ the acceleration components. From the definition of velocity and acceleration,\n", 322 | "we have $v_x = dx/dt, v_y = dy/dt, a_x = dv_x/dt$, and $a_y = dv_y/dt$.\n", 323 | "If we neglect air resistance there are no forces acting on the ball in the $x$-direction,\n", 324 | "so from Newton's second law we have $a_x = 0$. In the $y$-direction the acceleration\n", 325 | "must be equal to the acceleration of gravity, which yields $a_y = -g$.\n", 326 | "In terms of the velocities, we have" 327 | ] 328 | }, 329 | { 330 | "cell_type": "markdown", 331 | "metadata": {}, 332 | "source": [ 333 | "$$\n", 334 | "\\begin{align*}\n", 335 | "a_x &= 0 \\quad \\Rightarrow \\quad \\frac{dv_x}{dt} = 0, \\\\\n", 336 | "a_y &= -g \\quad \\Rightarrow \\quad \\frac{dv_y}{dt} = -g\\mbox{\\ } ,\n", 337 | "\\end{align*}\n", 338 | "$$" 339 | ] 340 | }, 341 | { 342 | "cell_type": "markdown", 343 | "metadata": {}, 344 | "source": [ 345 | "and the complete ODE system can be written as" 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": {}, 351 | "source": [ 352 | "\n", 353 | "
\n", 354 | "\n", 355 | "$$\n", 356 | "\\begin{equation}\n", 357 | "{dx\\over dt} = v_x, \\label{vx} \\tag{1} \n", 358 | "\\end{equation}\n", 359 | "$$" 360 | ] 361 | }, 362 | { 363 | "cell_type": "markdown", 364 | "metadata": {}, 365 | "source": [ 366 | "\n", 367 | "
\n", 368 | "\n", 369 | "$$\n", 370 | "\\begin{equation} \n", 371 | "{dv_x\\over dt} = 0,\\label{ax} \\tag{2} \n", 372 | "\\end{equation}\n", 373 | "$$" 374 | ] 375 | }, 376 | { 377 | "cell_type": "markdown", 378 | "metadata": {}, 379 | "source": [ 380 | "\n", 381 | "
\n", 382 | "\n", 383 | "$$\n", 384 | "\\begin{equation} \n", 385 | "{dy\\over dt} = v_y,\\label{vy} \\tag{3} \n", 386 | "\\end{equation}\n", 387 | "$$" 388 | ] 389 | }, 390 | { 391 | "cell_type": "markdown", 392 | "metadata": {}, 393 | "source": [ 394 | "\n", 395 | "
\n", 396 | "\n", 397 | "$$\n", 398 | "\\begin{equation} \n", 399 | "{dv_y\\over dt} = -g.\\label{ay} \\tag{4}\n", 400 | "\\end{equation}\n", 401 | "$$" 402 | ] 403 | }, 404 | { 405 | "cell_type": "markdown", 406 | "metadata": {}, 407 | "source": [ 408 | "To solve the system we need to define initial conditions for all four unknowns,\n", 409 | "i.e., we need to know the initial position and velocity of the ball.\n", 410 | "\n", 411 | "A closer inspection of the system ([1](#vx))-([4](#ay)) will reveal that although\n", 412 | "this is a coupled system of ODEs, the coupling is in fact quite weak and the system is\n", 413 | "easy to solve analytically. There is essentially a one-way coupling between equations\n", 414 | "([2](#ax)) and ([1](#vx)), the same between ([4](#ay)) and ([3](#vy)), and no other coupling\n", 415 | "between the equations. We can easily solve ([2](#ax)) to conclude that $v_x$ is a constant,\n", 416 | "and inserting a constant on the right hand side of ([1](#vx)) yields that $x$ must be a linear\n", 417 | "function of $t$. Similarly, we can solve ([4](#ay)) to find that $v_y$ is a linear function, and then\n", 418 | "insert this into ([3](#vy)) to find that $y$ is a quadratic function of $t$. The functions\n", 419 | "$x(t)$ and $y(t)$ will contain four unknown coefficients that must be determined from the\n", 420 | "initial conditions.\n", 421 | "\n", 422 | "Although the analytical solution is available, we want to use the `ODESolver`\n", 423 | "class hierarchy presented above to solve this system. The first step is then to\n", 424 | "implement the right hand side as a Python function:" 425 | ] 426 | }, 427 | { 428 | "cell_type": "code", 429 | "execution_count": 3, 430 | "metadata": { 431 | "collapsed": false 432 | }, 433 | "outputs": [], 434 | "source": [ 435 | "def f(u, t):\n", 436 | " x, vx, y, vy = u\n", 437 | " g = 9.81\n", 438 | " return [vx, 0, vy, -g]" 439 | ] 440 | }, 441 | { 442 | "cell_type": "markdown", 443 | "metadata": {}, 444 | "source": [ 445 | "We see that the function here returns a list, but this will automatically\n", 446 | "be converted to an array by the solver class' constructor,\n", 447 | "as mentioned above. The main program is not very different\n", 448 | "from the examples of the previous chapter, except that we need to define an\n", 449 | "initial condition with four components:" 450 | ] 451 | }, 452 | { 453 | "cell_type": "code", 454 | "execution_count": 4, 455 | "metadata": { 456 | "collapsed": false 457 | }, 458 | "outputs": [], 459 | "source": [ 460 | "%matplotlib inline\n", 461 | "\n", 462 | "from ODESolver import ForwardEuler\n", 463 | "import numpy as np\n", 464 | "import matplotlib.pyplot as plt\n", 465 | "\n", 466 | "# Initial condition, start at the origin:\n", 467 | "x = 0; y = 0\n", 468 | "# velocity magnitude and angle:\n", 469 | "v0 = 5; theta = 80*np.pi/180\n", 470 | "vx = v0*np.cos(theta); vy = v0*np.sin(theta)\n", 471 | "\n", 472 | "U0 = [x, vx, y, vy]\n", 473 | "\n", 474 | "solver= ForwardEuler(f)\n", 475 | "solver.set_initial_condition(U0)\n", 476 | "time_points = np.linspace(0, 1.0, 101)\n", 477 | "u, t = solver.solve(time_points)\n", 478 | "# u is an array of [x,vx,y,vy] arrays, plot y vs x:\n", 479 | "x = u[:,0]; y = u[:,2]\n", 480 | "\n", 481 | "plt.plot(x, y)\n", 482 | "plt.show()" 483 | ] 484 | }, 485 | { 486 | "cell_type": "markdown", 487 | "metadata": {}, 488 | "source": [ 489 | "Notice that since `u` is a two-dimensional array, we use array slicing to extract and plot the individual components.\n", 490 | "A call like `plt.plot(t,u)` will also work, but it will plot all the solution\n", 491 | "components in the same window, which for this particular model is not very useful.\n", 492 | "A very useful exercise is to extend this code to plot the analytical solution of the system in the\n", 493 | "same window as the numerical solution. The system can be solved as outlined above, and the\n", 494 | "unknown coefficients in the solution formulas can be determined from the given initial conditions.\n", 495 | "With the chosen number of time steps there will be a visible difference between the numerical\n", 496 | "solution and the analytical solution, but this can easily be removed\n", 497 | "by reducing the time step or choosing a more accurate solver." 498 | ] 499 | } 500 | ], 501 | "metadata": {}, 502 | "nbformat": 4, 503 | "nbformat_minor": 2 504 | } 505 | -------------------------------------------------------------------------------- /docs/ode_book.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/docs/ode_book.pdf -------------------------------------------------------------------------------- /docs/src.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundnes/solving_odes_in_python/21b2caa703f12d2cef6f0017c69d607bccfc5fc6/docs/src.zip -------------------------------------------------------------------------------- /docs/src/appendix1/Newton.py: -------------------------------------------------------------------------------- 1 | def Newton(f, dfdx, x, epsilon=1.0E-7, max_n=100): 2 | n = 0 3 | while abs(f(x)) > epsilon and n <= max_n: 4 | x = x - f(x) / dfdx(x) 5 | n += 1 6 | return x, n, f(x) 7 | 8 | 9 | # Demo: solve x**3-4x+3=0 10 | def f(x): 11 | return x**3 - 4 * x - 3 12 | 13 | 14 | def df(x): 15 | return 3 * x**2 - 4 16 | 17 | 18 | x0 = 1.0 19 | sol, its, f_val = Newton(f, df, x0) 20 | print(f'Solver converged in {its} iterations, x={sol:g}, f(x)={f_val:g}') 21 | -------------------------------------------------------------------------------- /docs/src/appendix1/fibonacci_diffeq.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from numpy import zeros 3 | 4 | try: 5 | N = int(sys.argv[1]) 6 | except BaseException: 7 | print('Provide a command line argument; a number between 1 and 90.') 8 | exit() 9 | 10 | x = zeros(N + 1, int) 11 | x[0] = 1 12 | x[1] = 1 13 | for n in range(2, N + 1): 14 | x[n] = x[n - 1] + x[n - 2] 15 | print(n, x[n]) 16 | -------------------------------------------------------------------------------- /docs/src/appendix1/fortune_inflation.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | F = 1e7 # initial amount 4 | p = 5 # interest rate 5 | I = 3 6 | q = 75 7 | N = 40 # number of years 8 | index_set = range(N + 1) 9 | x = np.zeros(len(index_set)) 10 | c = np.zeros_like(x) 11 | 12 | x[0] = F 13 | c[0] = q * p * F * 1e-4 14 | 15 | for n in index_set[1:]: 16 | x[n] = x[n - 1] + (p / 100.0) * x[n - 1] - c[n - 1] 17 | c[n] = c[n - 1] + (I / 100.0) * c[n - 1] 18 | 19 | plt.plot(index_set, x, 'ro', label='Fortune') 20 | plt.plot(index_set, c, 'go', label='Yearly consume') 21 | plt.xlabel('years') 22 | plt.ylabel('amounts') 23 | plt.legend() 24 | plt.show() 25 | -------------------------------------------------------------------------------- /docs/src/appendix1/interest_v1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | x0 = 100 # initial amount 4 | p = 5 # interest rate 5 | N = 4 # number of years 6 | index_set = range(N + 1) 7 | x = np.zeros(len(index_set)) 8 | 9 | x[0] = x0 10 | for n in index_set[1:]: 11 | x[n] = x[n - 1] + (p / 100.0) * x[n - 1] 12 | 13 | plt.plot(index_set, x, 'ro') 14 | plt.xlabel('years') 15 | plt.ylabel('amount') 16 | plt.show() 17 | -------------------------------------------------------------------------------- /docs/src/appendix1/interest_v2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import datetime 4 | 5 | x0 = 100 # initial amount 6 | p = 5 # annual interest rate 7 | r = p / 360.0 # daily interest rate 8 | 9 | date1 = datetime.date(2017, 9, 29) 10 | date2 = datetime.date(2018, 8, 4) 11 | diff = date2 - date1 12 | N = diff.days 13 | index_set = range(N + 1) 14 | x = np.zeros(len(index_set)) 15 | 16 | x[0] = x0 17 | for n in index_set[1:]: 18 | x[n] = x[n - 1] + (r / 100.0) * x[n - 1] 19 | 20 | plt.plot(index_set, x) 21 | plt.xlabel('days') 22 | plt.ylabel('amount') 23 | plt.show() 24 | -------------------------------------------------------------------------------- /docs/src/appendix1/logistic_diffeq.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | x0 = 100 # initial population 4 | rho = 5 # growth rate in % 5 | R = 500 # max population (carrying capacity) 6 | N = 200 # number of years 7 | 8 | index_set = range(N + 1) 9 | x = np.zeros(len(index_set)) 10 | 11 | x[0] = x0 12 | for n in index_set[1:]: 13 | x[n] = x[n - 1] + (rho / 100) * x[n - 1] * (1 - x[n - 1] / R) 14 | 15 | plt.plot(index_set, x) 16 | plt.xlabel('years') 17 | plt.ylabel('amount') 18 | plt.show() 19 | -------------------------------------------------------------------------------- /docs/src/appendix1/lotke_volterra.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | 5 | x0 = 100 # initial prey population 6 | y0 = 8 # initial predator pop. 7 | a = 0.0015 8 | b = 0.0003 9 | c = 0.006 10 | d = 0.5 11 | N = 10000 # number of time units (days) 12 | index_set = range(N + 1) 13 | x = np.zeros(len(index_set)) 14 | y = np.zeros_like(x) 15 | 16 | x[0] = x0 17 | y[0] = y0 18 | 19 | for n in index_set[1:]: 20 | x[n] = x[n - 1] + a * x[n - 1] - b * x[n - 1] * y[n - 1] 21 | y[n] = y[n - 1] + d * b * x[n - 1] * y[n - 1] - c * y[n - 1] 22 | 23 | plt.plot(index_set, x, label='Prey') 24 | plt.plot(index_set, y, label='Predator') 25 | plt.xlabel('Time') 26 | plt.ylabel('Population') 27 | plt.legend() 28 | plt.show() 29 | -------------------------------------------------------------------------------- /docs/src/appendix1/taylor_exp_diffeq.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | x = 0.5 # approximate exp(x) for x = 0.5 4 | 5 | N = 5 6 | index_set = range(N + 1) 7 | a = np.zeros(len(index_set)) 8 | e = np.zeros(len(index_set)) 9 | a[0] = 1 10 | 11 | print(f'Exact: exp({x}) = {np.exp(x)}') 12 | for n in index_set[1:]: 13 | e[n] = e[n - 1] + a[n - 1] 14 | a[n] = x / n * a[n - 1] 15 | print(f'n = {n}, approx. {e[n]}, error = {np.abs(e[n]-np.exp(x)):4.5f}') 16 | -------------------------------------------------------------------------------- /docs/src/chapter1/check_convergence_fe.py: -------------------------------------------------------------------------------- 1 | from forward_euler_class import * 2 | import numpy as np 3 | 4 | 5 | def rhs(t, u): 6 | return u 7 | 8 | 9 | def exact(t): 10 | return np.exp(t) 11 | 12 | 13 | solver = ForwardEuler(rhs) 14 | solver.set_initial_condition(1.0) 15 | 16 | T = 3.0 17 | t_span = (0, T) 18 | N = 30 19 | 20 | print(f'Time step (dt) Error (e) e/dt') 21 | for _ in range(10): 22 | t, u = solver.solve(t_span, N) 23 | dt = T / N 24 | e = abs(u[-1] - exact(T)) 25 | print(f'{dt:<14.7f} {e:<14.7f} {e/dt:5.4f}') 26 | N = N * 2 27 | -------------------------------------------------------------------------------- /docs/src/chapter1/forward_euler_class.py: -------------------------------------------------------------------------------- 1 | """ 2 | Forward Euler class implementation for systems of ODEs. 3 | """ 4 | 5 | import numpy as np 6 | 7 | 8 | class ForwardEuler: 9 | def __init__(self, f): 10 | """Initialize the right-hand side function f """ 11 | self.f = lambda t, u: np.asarray(f(t, u), float) 12 | 13 | def set_initial_condition(self, u0): 14 | """Store the initial condition as 15 | an instance attribute. 16 | """ 17 | self.u0 = np.asarray(u0) 18 | self.neq = self.u0.size 19 | 20 | def solve(self, t_span, N): 21 | """Compute solution for 22 | t_span[0] <= t <= t_span[1], 23 | using N steps. 24 | Returns the solution and the 25 | time points as arrays. 26 | """ 27 | t0, T = t_span 28 | self.dt = (T - t0) / N 29 | self.t = np.zeros(N + 1) 30 | self.u = np.zeros((N + 1, self.neq)) 31 | 32 | msg = "Please set initial condition before calling solve" 33 | assert hasattr(self, "u0"), msg 34 | 35 | self.t[0] = t0 36 | self.u[0] = self.u0 37 | 38 | for n in range(N): 39 | self.n = n 40 | self.t[n + 1] = self.t[n] + self.dt 41 | self.u[n + 1] = self.advance() 42 | return self.t, self.u 43 | 44 | def advance(self): 45 | """Advance the solution one time step.""" 46 | u, dt, f, n, t = self.u, self.dt, self.f, self.n, self.t 47 | return u[n] + dt * f(t[n], u[n]) 48 | 49 | 50 | class Logistic: 51 | def __init__(self, alpha, R): 52 | self.alpha, self.R = alpha, R 53 | 54 | def __call__(self, t, u): 55 | return self.alpha * u * (1 - u / self.R) 56 | 57 | 58 | if __name__ == '__main__': 59 | """ 60 | Demonstrate how the class is used, 61 | by solving the logistic growth problem. 62 | To test the class for a system of ODEs, 63 | run pendulum.py 64 | """ 65 | import matplotlib.pyplot as plt 66 | 67 | problem = Logistic(alpha=0.2, R=1.0) 68 | solver = ForwardEuler(problem) 69 | u0 = 0.1 70 | solver.set_initial_condition(u0) 71 | 72 | T = 40 73 | t, u = solver.solve(t_span=(0, T), N=400) 74 | 75 | plt.plot(t, u) 76 | plt.title('Logistic growth, Forward Euler') 77 | plt.xlabel('t') 78 | plt.ylabel('u') 79 | plt.show() 80 | -------------------------------------------------------------------------------- /docs/src/chapter1/forward_euler_class_scalar.py: -------------------------------------------------------------------------------- 1 | """ 2 | First version of the Forward Euler class implementation, 3 | including a class implementation of the logistic 4 | growth model. 5 | """ 6 | import numpy as np 7 | 8 | 9 | class ForwardEuler_v0: 10 | def __init__(self, f): 11 | self.f = f # , self.U0, self.T, self.N = f, U0, T, N 12 | 13 | def set_initial_condition(self, u0): 14 | self.u0 = u0 15 | 16 | def solve(self, t_span, N): 17 | """Compute solution for t_span[0] <= t <= t_span[1], 18 | using N steps.""" 19 | t0, T = t_span 20 | self.dt = T / N 21 | self.t = np.zeros(N + 1) # N steps ~ N+1 time points 22 | self.u = np.zeros(N + 1) 23 | 24 | msg = "Please set initial condition before calling solve" 25 | assert hasattr(self, "u0"), msg 26 | 27 | self.t[0] = t0 28 | self.u[0] = self.u0 29 | 30 | for n in range(N): 31 | self.n = n 32 | self.t[n + 1] = self.t[n] + self.dt 33 | self.u[n + 1] = self.advance() 34 | return self.t, self.u 35 | 36 | def advance(self): 37 | """Advance the solution one time step.""" 38 | # Create local variables to get rid of "self." in 39 | # the numerical formula 40 | u, dt, f, n, t = self.u, self.dt, self.f, self.n, self.t 41 | 42 | return u[n] + dt * f(t[n], u[n]) 43 | 44 | 45 | class Logistic: 46 | def __init__(self, alpha, R): 47 | self.alpha, self.R = alpha, float(R) 48 | 49 | def __call__(self, t, u): 50 | return self.alpha * u * (1 - u / self.R) 51 | 52 | 53 | if __name__ == '__main__': 54 | """ 55 | Demonstrate how the class the class is used, 56 | by solving the logistic growth problem. 57 | """ 58 | import matplotlib.pyplot as plt 59 | 60 | problem = Logistic(alpha=0.2, R=1.0) 61 | solver = ForwardEuler_v0(problem) 62 | u0 = 0.1 63 | solver.set_initial_condition(u0) 64 | 65 | T = 40 66 | t, u = solver.solve(t_span=(0, T), N=400) 67 | 68 | plt.plot(t, u) 69 | plt.title('Logistic growth, Forward Euler') 70 | plt.xlabel('t') 71 | plt.ylabel('u') 72 | plt.show() 73 | -------------------------------------------------------------------------------- /docs/src/chapter1/forward_euler_fun.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implementation of the ForwardEuler method as a function. 3 | """ 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | 7 | 8 | def forward_euler(f, u0, T, N): 9 | """Solve u'=f(u,t), u(0)=U0, with n steps until t=T.""" 10 | t = np.zeros(N + 1) 11 | u = np.zeros(N + 1) # u[n] is the solution at time t[n] 12 | 13 | u[0] = u0 14 | t[0] = 0 15 | dt = T / N 16 | 17 | for n in range(N): 18 | t[n + 1] = t[n] + dt 19 | u[n + 1] = u[n] + dt * f(t[n], u[n]) 20 | 21 | return t, u 22 | 23 | # demo: solve u' = u, u(0) = 1 24 | 25 | 26 | def f(t, u): 27 | return u 28 | 29 | 30 | U0 = 1 31 | T = 4 32 | N = 20 33 | t, u = forward_euler(f, U0, T, N) 34 | plt.plot(t, u, label=f'$\\Delta t$ = {T/N}') 35 | plt.plot(t, np.exp(t), label='Exact') 36 | plt.title('Exp. growth, Forward Euler') 37 | plt.xlabel('t') 38 | plt.ylabel('u') 39 | plt.legend() 40 | plt.show() 41 | -------------------------------------------------------------------------------- /docs/src/chapter1/pendulum.py: -------------------------------------------------------------------------------- 1 | from math import sin 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | 5 | 6 | class Pendulum: 7 | def __init__(self, L, g=9.81): 8 | self.L = L 9 | self.g = g 10 | 11 | def __call__(self, t, u): 12 | theta, omega = u 13 | dtheta = omega 14 | domega = -self.g / self.L * sin(theta) 15 | return [dtheta, domega] 16 | 17 | 18 | if __name__ == '__main__': 19 | from forward_euler_class import ForwardEuler 20 | problem = Pendulum(L=1) 21 | solver = ForwardEuler(problem) 22 | solver.set_initial_condition([np.pi / 4, 0]) 23 | T = 10 24 | N = 1000 25 | t, u = solver.solve(t_span=(0, T), N=N) 26 | 27 | plt.plot(t, u[:, 0], label=r'$\theta$') 28 | plt.plot(t, u[:, 1], label=r'$\omega$') 29 | plt.title(f'Pendulum problem, Forward Euler, $\\Delta t$ = {T/N}') 30 | plt.xlabel('t') 31 | plt.ylabel(r'Angle ($\theta$) and angular velocity ($\omega$)') 32 | plt.legend() 33 | plt.show() 34 | -------------------------------------------------------------------------------- /docs/src/chapter1/pendulum_scipy.py: -------------------------------------------------------------------------------- 1 | from scipy.integrate import solve_ivp 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | from pendulum import Pendulum 5 | 6 | problem = Pendulum(L=1) 7 | t_span = (0, 10.0) 8 | u0 = (np.pi / 4, 0) 9 | 10 | # optional arguments to solve_ivp: 11 | #t_eval = np.linspace(0,10.0,1001) 12 | #rtol = 1e-6 13 | 14 | solution = solve_ivp(problem, t_span, u0) 15 | 16 | plt.plot(solution.t, solution.y[0, :], label=r'$\theta$') 17 | plt.plot(solution.t, solution.y[1, :], label=r'$\omega$') 18 | 19 | solution = solve_ivp(problem, t_span, u0, rtol=1e-10) 20 | plt.plot(solution.t, solution.y[0, :], ':') 21 | plt.plot(solution.t, solution.y[1, :], ':') 22 | 23 | plt.title(f'Pendulum problem, SciPy solve_ivp') 24 | plt.xlabel('t') 25 | plt.ylabel(r'Angle ($\theta$) and angular velocity ($\omega$)') 26 | plt.legend() 27 | plt.show() 28 | -------------------------------------------------------------------------------- /docs/src/chapter2/ODESolver.py: -------------------------------------------------------------------------------- 1 | """ 2 | Version 2 of the ODESolver class hierarchy. This class works for systems 3 | of ODEs and for a single (scalar) ODE. 4 | """ 5 | 6 | import numpy as np 7 | 8 | 9 | class ODESolver: 10 | def __init__(self, f): 11 | # Wrap user's f in a new function that always 12 | # converts list/tuple to array (or let array be array) 13 | self.model = f 14 | self.f = lambda t, u: np.asarray(f(t, u), float) 15 | 16 | def set_initial_condition(self, u0): 17 | if np.isscalar(u0): # scalar ODE 18 | self.neq = 1 # no of equations 19 | u0 = float(u0) 20 | else: # system of ODEs 21 | u0 = np.asarray(u0) 22 | self.neq = u0.size # no of equations 23 | self.u0 = u0 24 | 25 | def solve(self, t_span, N): 26 | """Compute solution for 27 | t_span[0] <= t <= t_span[1], 28 | using N steps. 29 | Returns the solution and the 30 | time points as arrays. 31 | """ 32 | t0, T = t_span 33 | self.dt = (T - t0) / N 34 | self.t = np.zeros(N + 1) # N steps ~ N+1 time points 35 | if self.neq == 1: 36 | self.u = np.zeros(N + 1) 37 | else: 38 | self.u = np.zeros((N + 1, self.neq)) 39 | 40 | msg = "Please set initial condition before calling solve" 41 | assert hasattr(self, "u0"), msg 42 | 43 | self.t[0] = t0 44 | self.u[0] = self.u0 45 | 46 | for n in range(N): 47 | self.n = n 48 | self.t[n + 1] = self.t[n] + self.dt 49 | self.u[n + 1] = self.advance() 50 | return self.t, self.u 51 | 52 | def advance(self): 53 | raise NotImplementedError( 54 | "Advance method is not implemented in the base class") 55 | 56 | 57 | 58 | class ForwardEuler(ODESolver): 59 | def advance(self): 60 | u, f, n, t = self.u, self.f, self.n, self.t 61 | dt = self.dt 62 | return u[n] + dt * f(t[n], u[n]) 63 | 64 | class Heun(ODESolver): 65 | def advance(self): 66 | u, f, n, t = self.u, self.f, self.n, self.t 67 | dt = self.dt 68 | k1 = f(t[n], u[n]) 69 | k2 = f(t[n] + dt, u[n] + dt * k1) 70 | return u[n] + dt / 2 * (k1 + k2) 71 | 72 | class ExplicitMidpoint(ODESolver): 73 | def advance(self): 74 | u, f, n, t = self.u, self.f, self.n, self.t 75 | dt = self.dt 76 | dt2 = dt / 2.0 77 | k1 = f(t[n], u[n]) 78 | k2 = f(t[n] + dt2, u[n] + dt2 * k1) 79 | return u[n] + dt * k2 80 | 81 | class RungeKutta4(ODESolver): 82 | def advance(self): 83 | u, f, n, t = self.u, self.f, self.n, self.t 84 | dt = self.dt 85 | dt2 = dt / 2.0 86 | k1 = f(t[n], u[n],) 87 | k2 = f(t[n] + dt2, u[n] + dt2 * k1, ) 88 | k3 = f(t[n] + dt2, u[n] + dt2 * k2, ) 89 | k4 = f(t[n] + dt, u[n] + dt * k3, ) 90 | return u[n] + (dt / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4) 91 | 92 | def test_exact_numerical_solution(): 93 | """ 94 | Test the different methods for a problem 95 | where the analytical solution is known and linear. 96 | All the methods should be exact to machine precision 97 | for this choice. 98 | """ 99 | solver_classes = [ForwardEuler, Heun, 100 | ExplicitMidpoint, RungeKutta4] 101 | a = 0.2 102 | b = 3 103 | 104 | def f(t, u): 105 | return a 106 | 107 | def u_exact(t): 108 | """Exact u(t) corresponding to f above.""" 109 | return a * t + b 110 | 111 | u0 = u_exact(0) 112 | T = 8 113 | N = 10 114 | tol = 1E-14 115 | t_span = (0, T) 116 | for solver_class in solver_classes: 117 | solver = solver_class(f) 118 | solver.set_initial_condition(u0) 119 | t, u = solver.solve(t_span, N) 120 | u_e = u_exact(t) 121 | max_error = abs((u_e - u)).max() 122 | msg = f'{solver_class.__name__} failed with max_error={max_error}' 123 | assert max_error < tol, msg 124 | 125 | 126 | if __name__ == '__main__': 127 | test_exact_numerical_solution() 128 | -------------------------------------------------------------------------------- /docs/src/chapter2/check_convergence_erk.py: -------------------------------------------------------------------------------- 1 | from ODESolver import * 2 | import numpy as np 3 | 4 | 5 | def rhs(t, u): 6 | return u 7 | 8 | 9 | def exact(t): 10 | return np.exp(t) 11 | 12 | solver_classes = [(ForwardEuler,1), (Heun,2), 13 | (ExplicitMidpoint,2), (RungeKutta4,4)] 14 | 15 | for solver_class, order in solver_classes: 16 | solver = solver_class(rhs) 17 | solver.set_initial_condition(1.0) 18 | 19 | T = 3.0 20 | t_span = (0, T) 21 | N = 30 22 | print(f'{solver_class.__name__}, order = {order}') 23 | print(f'Time step (dt) Error (e) e/dt**{order}') 24 | for _ in range(10): 25 | t, u = solver.solve(t_span, N) 26 | dt = T / N 27 | e = abs(u[-1] - exact(T)) 28 | if e < 1e-13: # break if error is close to machine precision 29 | break 30 | print(f'{dt:<14.7f} {e:<12.7f} {e/dt**order:5.4f}') 31 | N = N * 2 32 | -------------------------------------------------------------------------------- /docs/src/chapter2/simple_compare.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from ODESolver import ForwardEuler, ExplicitMidpoint, RungeKutta4 4 | 5 | 6 | def f(t, u): 7 | return u 8 | 9 | 10 | t_span = (0, 3) 11 | N = 6 12 | 13 | fe = ForwardEuler(f) 14 | fe.set_initial_condition(u0=1) 15 | t1, u1 = fe.solve(t_span, N) 16 | plt.plot(t1, u1, label='Forward Euler') 17 | 18 | em = ExplicitMidpoint(f) 19 | em.set_initial_condition(u0=1) 20 | t2, u2 = em.solve(t_span, N) 21 | plt.plot(t2, u2, label='Explicit Midpoint') 22 | 23 | rk4 = RungeKutta4(f) 24 | rk4.set_initial_condition(u0=1) 25 | t3, u3 = rk4.solve(t_span, N) 26 | plt.plot(t3, u3, label='Runge-Kutta 4') 27 | 28 | # plot the exact solution in the same plot 29 | time_exact = np.linspace(0, 3, 301) 30 | plt.plot(time_exact, np.exp(time_exact), label='Exact') 31 | plt.title('RK solvers for exponential growth, $\\Delta t = 0.5$') 32 | plt.xlabel('$t$') 33 | plt.ylabel('$u(t)$') 34 | plt.legend() 35 | plt.show() 36 | -------------------------------------------------------------------------------- /docs/src/chapter3/ImplicitRK.py: -------------------------------------------------------------------------------- 1 | from ODESolver import * 2 | from scipy.optimize import root 3 | 4 | 5 | class ImplicitRK(ODESolver): 6 | def solve_stages(self): 7 | u, f, n, t = self.u, self.f, self.n, self.t 8 | s = self.stages 9 | k0 = f(t[n], u[n]) 10 | k0 = np.tile(k0,s) 11 | 12 | sol = root(self.stage_eq, k0) 13 | 14 | return np.split(sol.x, s) 15 | 16 | def stage_eq(self, k_all): 17 | a, c = self.a, self.c 18 | s, neq = self.stages, self.neq 19 | 20 | u, f, n, t = self.u, self.f, self.n, self.t 21 | dt = self.dt 22 | 23 | res = np.zeros_like(k_all) 24 | k = np.split(k_all, s) 25 | for i in range(s): 26 | fi = f(t[n] + c[i] * dt, u[n] + dt * 27 | sum([a[i, j] * k[j] for j in range(s)])) 28 | res[i * neq:(i + 1) * neq] = k[i] - fi 29 | 30 | return res 31 | 32 | def advance(self): 33 | b = self.b 34 | u, n, t = self.u, self.n, self.t 35 | dt = self.dt 36 | k = self.solve_stages() 37 | 38 | return u[n] + dt * sum(b_ * k_ for b_, k_ in zip(b, k)) 39 | 40 | 41 | class BackwardEuler(ImplicitRK): 42 | def __init__(self, f): 43 | super().__init__(f) 44 | self.stages = 1 45 | self.a = np.array([[1]]) 46 | self.c = np.array([1]) 47 | self.b = np.array([1]) 48 | 49 | 50 | class ImplicitMidpoint(ImplicitRK): 51 | def __init__(self, f): 52 | super().__init__(f) 53 | self.stages = 1 54 | self.a = np.array([[1 / 2]]) 55 | self.c = np.array([1 / 2]) 56 | self.b = np.array([1]) 57 | 58 | 59 | class Radau2(ImplicitRK): 60 | def __init__(self, f): 61 | super().__init__(f) 62 | self.stages = 2 63 | self.a = np.array([[5 / 12, -1 / 12], [3 / 4, 1 / 4]]) 64 | self.c = np.array([1 / 3, 1]) 65 | self.b = np.array([3 / 4, 1 / 4]) 66 | 67 | 68 | class Radau3(ImplicitRK): 69 | def __init__(self, f): 70 | super().__init__(f) 71 | self.stages = 3 72 | sq6 = np.sqrt(6) 73 | self.a = np.array([[(88 - 7 * sq6) / 360, 74 | (296 - 169 * sq6) / 1800, 75 | (-2 + 3 * sq6) / (225)], 76 | [(296 + 169 * sq6) / 1800, 77 | (88 + 7 * sq6) / 360, 78 | (-2 - 3 * sq6) / (225)], 79 | [(16 - sq6) / 36, (16 + sq6) / 36, 1 / 9]]) 80 | self.c = np.array([(4 - sq6) / 10, (4 + sq6) / 10, 1]) 81 | self.b = np.array([(16 - sq6) / 36, (16 + sq6) / 36, 1 / 9]) 82 | 83 | 84 | class SDIRK(ImplicitRK): 85 | def stage_eq(self, k, c_i, k_sum): 86 | u, f, n, t = self.u, self.f, self.n, self.t 87 | dt = self.dt 88 | gamma = self.gamma 89 | 90 | return k - f(t[n] + c_i * dt, u[n] + dt * (k_sum + gamma * k)) 91 | 92 | def solve_stages(self): 93 | u, f, n, t = self.u, self.f, self.n, self.t 94 | a, c = self.a, self.c 95 | s = self.stages 96 | 97 | k = f(t[n], u[n]) # initial guess for first stage 98 | k_sum = np.zeros_like(k) 99 | k_all = [] 100 | for i in range(s): 101 | k_sum = sum(a_ * k_ for a_, k_ in zip(a[i, :i], k_all)) 102 | k = root(self.stage_eq, k, args=(c[i], k_sum)).x 103 | k_all.append(k) 104 | return k_all 105 | 106 | 107 | class SDIRK2(SDIRK): 108 | def __init__(self, f): 109 | super().__init__(f) 110 | self.stages = 2 111 | gamma = (2 - np.sqrt(2)) / 2 112 | self.gamma = gamma 113 | self.a = np.array([[gamma, 0], 114 | [1 - gamma, gamma]]) 115 | self.c = np.array([gamma, 1]) 116 | self.b = np.array([1 - gamma, gamma]) 117 | 118 | 119 | class ESDIRK(SDIRK): 120 | def solve_stages(self): 121 | u, f, n, t = self.u, self.f, self.n, self.t 122 | a, c = self.a, self.c 123 | s = self.stages 124 | 125 | k = f(t[n], u[n]) # initial guess for first stage 126 | k_sum = np.zeros_like(k) 127 | k_all = [k] 128 | for i in range(1, s): 129 | k_sum = sum(a_ * k_ for a_, k_ in zip(a[i, :i], k_all)) 130 | k = root(self.stage_eq, k, args=(c[i], k_sum)).x 131 | k_all.append(k) 132 | 133 | return k_all 134 | 135 | 136 | class TR_BDF2(ESDIRK): 137 | def __init__(self, f): 138 | super().__init__(f) 139 | self.stages = 3 140 | gamma = 1 - np.sqrt(2) / 2 141 | beta = np.sqrt(2) / 4 142 | self.gamma = gamma 143 | self.a = np.array([[0, 0, 0], 144 | [gamma, gamma, 0], 145 | [beta, beta, gamma]]) 146 | self.c = np.array([0, 2 * gamma, 1]) 147 | self.b = np.array([beta, beta, gamma]) 148 | 149 | 150 | if __name__ == "__main__": 151 | registered_solver_classes.extend( 152 | [BackwardEuler, ImplicitMidpoint, Radau2, Radau3, SDIRK2, TR_BDF2]) 153 | test_exact_numerical_solution() 154 | -------------------------------------------------------------------------------- /docs/src/chapter3/ImplicitRK_v0.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple implementation of the Backward Euler 3 | and Crank-Nicolson methods, which are both implicit RK 4 | solvers. A more general implementation of implicit RK 5 | solvers is found in the file ImplicitRK.py 6 | """ 7 | 8 | 9 | from ODESolver import * 10 | from scipy.optimize import root 11 | 12 | 13 | class BackwardEuler(ODESolver): 14 | 15 | def stage_eq(self, k): 16 | u, f, n, t = self.u, self.f, self.n, self.t 17 | dt = self.dt 18 | return k - f(t[n] + dt, u[n] + dt * k) 19 | 20 | def solve_stage(self): 21 | u, f, n, t = self.u, self.f, self.n, self.t 22 | k0 = f(t[n], u[n]) 23 | sol = root(self.stage_eq, k0) 24 | return sol.x 25 | 26 | def advance(self): 27 | u, f, n, t = self.u, self.f, self.n, self.t 28 | dt = self.dt 29 | k1 = self.solve_stage() 30 | return u[n] + dt * k1 31 | 32 | 33 | class CrankNicolson(BackwardEuler): 34 | def advance(self): 35 | u, f, n, t = self.u, self.f, self.n, self.t 36 | dt = self.dt 37 | k1 = f(t[n], u[n]) 38 | k2 = self.solve_stage() 39 | return u[n] + dt / 2 * (k1 + k2) 40 | 41 | 42 | if __name__ == "__main__": 43 | registered_solver_classes.extend([BackwardEuler, CrankNicolson]) 44 | test_exact_numerical_solution() 45 | -------------------------------------------------------------------------------- /docs/src/chapter3/ODESolver.py: -------------------------------------------------------------------------------- 1 | """ 2 | Version 2 of the ODESolver class hierarchy. This class works for systems 3 | of ODEs and for a single (scalar) ODE. 4 | """ 5 | 6 | import numpy as np 7 | 8 | 9 | class ODESolver: 10 | def __init__(self, f): 11 | # Wrap user's f in a new function that always 12 | # converts list/tuple to array (or let array be array) 13 | self.model = f 14 | self.f = lambda t, u: np.asarray(f(t, u), float) 15 | 16 | def set_initial_condition(self, u0): 17 | if np.isscalar(u0): # scalar ODE 18 | self.neq = 1 # no of equations 19 | u0 = float(u0) 20 | else: # system of ODEs 21 | u0 = np.asarray(u0) 22 | self.neq = u0.size # no of equations 23 | self.u0 = u0 24 | 25 | def solve(self, t_span, N): 26 | """Compute solution for 27 | t_span[0] <= t <= t_span[1], 28 | using N steps. 29 | Returns the solution and the 30 | time points as arrays. 31 | """ 32 | t0, T = t_span 33 | self.dt = (T - t0) / N 34 | self.t = np.zeros(N + 1) # N steps ~ N+1 time points 35 | if self.neq == 1: 36 | self.u = np.zeros(N + 1) 37 | else: 38 | self.u = np.zeros((N + 1, self.neq)) 39 | 40 | msg = "Please set initial condition before calling solve" 41 | assert hasattr(self, "u0"), msg 42 | 43 | self.t[0] = t0 44 | self.u[0] = self.u0 45 | 46 | for n in range(N): 47 | self.n = n 48 | self.t[n + 1] = self.t[n] + self.dt 49 | self.u[n + 1] = self.advance() 50 | return self.t, self.u 51 | 52 | def advance(self): 53 | raise NotImplementedError( 54 | "Advance method is not implemented in the base class") 55 | 56 | 57 | 58 | class ForwardEuler(ODESolver): 59 | def advance(self): 60 | u, f, n, t = self.u, self.f, self.n, self.t 61 | dt = self.dt 62 | return u[n] + dt * f(t[n], u[n]) 63 | 64 | class Heun(ODESolver): 65 | def advance(self): 66 | u, f, n, t = self.u, self.f, self.n, self.t 67 | dt = self.dt 68 | k1 = f(t[n], u[n]) 69 | k2 = f(t[n] + dt, u[n] + dt * k1) 70 | return u[n] + dt / 2 * (k1 + k2) 71 | 72 | class ExplicitMidpoint(ODESolver): 73 | def advance(self): 74 | u, f, n, t = self.u, self.f, self.n, self.t 75 | dt = self.dt 76 | dt2 = dt / 2.0 77 | k1 = f(t[n], u[n]) 78 | k2 = f(t[n] + dt2, u[n] + dt2 * k1) 79 | return u[n] + dt * k2 80 | 81 | class RungeKutta4(ODESolver): 82 | def advance(self): 83 | u, f, n, t = self.u, self.f, self.n, self.t 84 | dt = self.dt 85 | dt2 = dt / 2.0 86 | k1 = f(t[n], u[n],) 87 | k2 = f(t[n] + dt2, u[n] + dt2 * k1, ) 88 | k3 = f(t[n] + dt2, u[n] + dt2 * k2, ) 89 | k4 = f(t[n] + dt, u[n] + dt * k3, ) 90 | return u[n] + (dt / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4) 91 | 92 | def test_exact_numerical_solution(): 93 | """ 94 | Test the different methods for a problem 95 | where the analytical solution is known and linear. 96 | All the methods should be exact to machine precision 97 | for this choice. 98 | """ 99 | solver_classes = [ForwardEuler, Heun, 100 | ExplicitMidpoint, RungeKutta4] 101 | a = 0.2 102 | b = 3 103 | 104 | def f(t, u): 105 | return a 106 | 107 | def u_exact(t): 108 | """Exact u(t) corresponding to f above.""" 109 | return a * t + b 110 | 111 | u0 = u_exact(0) 112 | T = 8 113 | N = 10 114 | tol = 1E-14 115 | t_span = (0, T) 116 | for solver_class in solver_classes: 117 | solver = solver_class(f) 118 | solver.set_initial_condition(u0) 119 | t, u = solver.solve(t_span, N) 120 | u_e = u_exact(t) 121 | max_error = abs((u_e - u)).max() 122 | msg = f'{solver_class.__name__} failed with max_error={max_error}' 123 | assert max_error < tol, msg 124 | 125 | 126 | if __name__ == '__main__': 127 | test_exact_numerical_solution() 128 | -------------------------------------------------------------------------------- /docs/src/chapter3/check_convergence_irk.py: -------------------------------------------------------------------------------- 1 | from ImplicitRK import BackwardEuler,Radau2, Radau3 2 | import numpy as np 3 | 4 | def rhs(t, u): 5 | return u 6 | 7 | def exact(t): 8 | return np.exp(t) 9 | 10 | solver_classes = [(BackwardEuler, 1), 11 | (Radau2, 3), (Radau3, 5)] 12 | 13 | for solver_class, order in solver_classes: 14 | solver = solver_class(rhs) 15 | solver.set_initial_condition(1.0) 16 | 17 | T = 3.0 18 | t_span = (0, T) 19 | N = 30 20 | print(f'{solver_class.__name__}, order = {order}') 21 | print(f'Time step (dt) Error (e) e/dt**{order}') 22 | for _ in range(10): 23 | t, u = solver.solve(t_span, N) 24 | dt = T / N 25 | e = abs(u[-1] - exact(T)) 26 | if e < 1e-13: # break if error is close to machine precision 27 | break 28 | print(f'{dt:<14.7f} {e:<12.7f} {e/dt**order:5.4f}') 29 | N = N * 2 30 | -------------------------------------------------------------------------------- /docs/src/chapter3/vanderpol.py: -------------------------------------------------------------------------------- 1 | from ImplicitRK import * 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | 6 | class VanderPol: 7 | def __init__(self, mu): 8 | self.mu = mu 9 | 10 | def __call__(self, t, u): 11 | du0 = u[1] 12 | du1 = self.mu * (1 - u[0]**2) * u[1] - u[0] 13 | return du0, du1 14 | 15 | 16 | if __name__ == '__main__': 17 | model = VanderPol(mu=1) 18 | 19 | solver = ForwardEuler(model) 20 | solver.set_initial_condition([1, 0]) 21 | 22 | t_span = (0, 20) 23 | t, u = solver.solve(t_span=(0, 20), N=1000) 24 | 25 | plt.plot(t, u) 26 | plt.show() 27 | -------------------------------------------------------------------------------- /docs/src/chapter3/vanderpol_euler_methods.py: -------------------------------------------------------------------------------- 1 | """ 2 | Solve the Van der Pol model with forward and backward 3 | Euler methods, and a reference solution with the 4 | solve_ivp function and a low tolerance. 5 | With the chosen time step both methods solve the model, 6 | but forward Euler gives spurious oscillations, and 7 | the backward Euler solution is stable but inaccurate. 8 | """ 9 | 10 | from ImplicitRK import * 11 | import numpy as np 12 | import matplotlib.pyplot as plt 13 | from vanderpol import VanderPol 14 | from scipy.integrate import solve_ivp 15 | 16 | 17 | fig = plt.figure() 18 | gs = fig.add_gridspec(3, hspace=0.5) 19 | axs = gs.subplots(sharex=True, sharey=False) 20 | 21 | mu = 10 22 | model = VanderPol(mu=mu) 23 | solvers = [ForwardEuler(model), BackwardEuler(model)] 24 | solver_names = ['ForwardEuler', 'BackwardEuler'] 25 | 26 | T = 20 27 | t_span = (0, T) 28 | u0 = [1, 0] 29 | rtol = 1e-10 30 | solution = solve_ivp(model, t_span, u0, rtol=rtol) 31 | axs[0].plot(solution.t, solution.y[0, :]) 32 | axs[0].plot(solution.t, solution.y[1, :]) 33 | axs[0].set(title='Reference solution') 34 | 35 | for i, solver in enumerate(solvers): 36 | solver.set_initial_condition([1, 0]) 37 | N = 500 38 | t, u = solver.solve(t_span=(0, 20), N=N) 39 | axs[i + 1].plot(t, u) 40 | axs[i + 1].set(title=f'{solver_names[i]}, $\\Delta t$ = {T/N:g}') 41 | 42 | plt.show() 43 | -------------------------------------------------------------------------------- /docs/src/chapter3/vanderpol_figure_irk.py: -------------------------------------------------------------------------------- 1 | from ImplicitRK import * 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | from vanderpol import VanderPol 5 | from scipy.integrate import solve_ivp 6 | 7 | 8 | fig = plt.figure(figsize=(6.4, 8.4)) 9 | gs = fig.add_gridspec(6, hspace=0.5) 10 | axs = gs.subplots(sharex=True, sharey=False) 11 | 12 | mu = 10 13 | model = VanderPol(mu=mu) 14 | solvers = [ 15 | BackwardEuler(model), 16 | SDIRK2(model), 17 | TR_BDF2(model), 18 | Radau2(model), 19 | Radau3(model)] 20 | solver_names = ['BackwardEuler', 'SDIRK2', 'TR2_BDF2', 'Radau2', 'Radau3'] 21 | 22 | T = 20 23 | t_span = (0, T) 24 | u0 = [1, 0] 25 | rtol = 1e-10 26 | solution = solve_ivp(model, t_span, u0, rtol=rtol) 27 | axs[0].plot(solution.t, solution.y[0, :]) 28 | axs[0].plot(solution.t, solution.y[1, :]) 29 | axs[0].set(title='Reference solution') 30 | 31 | for i, solver in enumerate(solvers): 32 | solver.set_initial_condition([1, 0]) 33 | N = 200 34 | t, u = solver.solve(t_span=(0, 20), N=N) 35 | axs[i + 1].plot(t, u) 36 | axs[i + 1].set(title=f'{solver_names[i]}, $\\Delta t$ = {T/N:g}') 37 | plt.show() 38 | -------------------------------------------------------------------------------- /docs/src/chapter3/vanderpol_mu0_1_5.py: -------------------------------------------------------------------------------- 1 | from ImplicitRK import * 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | from vanderpol import VanderPol 5 | 6 | 7 | fig = plt.figure() 8 | gs = fig.add_gridspec(3, hspace=0.5) 9 | axs = gs.subplots(sharex=True, sharey=False) 10 | #fig, axs = plt.subplots(3, sharex=True, sharey=False) 11 | for i, mu in enumerate([0, 1, 5]): 12 | model = VanderPol(mu=mu) 13 | 14 | solver = Radau3(model) 15 | solver.set_initial_condition([1, 0]) 16 | 17 | t_span = (0, 20) 18 | t, u = solver.solve(t_span=(0, 20), N=100) 19 | axs[i].plot(t, u) 20 | axs[i].set(title=f'$\\mu={mu}$') 21 | plt.show() 22 | -------------------------------------------------------------------------------- /docs/src/chapter4/AdaptiveImplicitRK.py: -------------------------------------------------------------------------------- 1 | from ImplicitRK import * 2 | from AdaptiveODESolver import * 3 | import numpy as np 4 | 5 | 6 | class AdaptiveESDIRK(AdaptiveODESolver, ESDIRK): 7 | 8 | def advance(self): 9 | b = self.b 10 | e = self.e 11 | u = self.u 12 | dt = self.dt 13 | k = self.solve_stages() 14 | u_step = dt * sum(b_ * k_ for b_, k_ in zip(b, k)) 15 | error = dt * sum(e_ * k_ for e_, k_ in zip(e, k)) 16 | 17 | u_new = u[-1] + u_step 18 | error_norm = np.linalg.norm(error) 19 | return u_new, error_norm 20 | 21 | 22 | class TR_BDF2_Adaptive(AdaptiveESDIRK): 23 | def __init__(self, f, eta=0.9): 24 | super().__init__(f, eta) # calls AdaptiveODESolver.__init__ 25 | self.stages = 3 26 | self.order = 2 27 | gamma = 1 - np.sqrt(2) / 2 28 | beta = np.sqrt(2) / 4 29 | self.gamma = gamma 30 | self.a = np.array([[0, 0, 0], 31 | [gamma, gamma, 0], 32 | [beta, beta, gamma]]) 33 | self.c = np.array([0, 2 * gamma, 1]) 34 | self.b = np.array([beta, beta, gamma]) 35 | bh = np.array([(1 - beta) / 3, (3 * beta + 1) / 3, gamma / 3]) 36 | self.e = self.b - bh 37 | 38 | 39 | if __name__ == '__main__': 40 | from hodgkinhuxley import HodgkinHuxley 41 | import matplotlib.pyplot as plt 42 | 43 | model = HodgkinHuxley() 44 | 45 | solver = TR_BDF2_Adaptive(model) 46 | solver.set_initial_condition([-45, 0.31, 0.05, 0.59]) 47 | 48 | t, u = solver.solve((0, 50), tol=0.1) 49 | 50 | plt.plot(t, u[:, 0], '+') 51 | plt.show() 52 | -------------------------------------------------------------------------------- /docs/src/chapter4/AdaptiveODESolver.py: -------------------------------------------------------------------------------- 1 | from ODESolver import * 2 | from math import isnan, isinf 3 | 4 | 5 | class AdaptiveODESolver(ODESolver): 6 | def __init__(self, f, eta=0.9): 7 | super().__init__(f) 8 | self.eta = eta 9 | 10 | def new_step_size(self, dt, loc_error): 11 | eta = self.eta 12 | tol = self.tol 13 | p = self.order 14 | if isnan(loc_error) or isinf(loc_error): 15 | return self.min_dt 16 | 17 | new_dt = eta * (tol / loc_error)**(1 / (p + 1)) * dt 18 | new_dt = max(new_dt, self.min_dt) 19 | return min(new_dt, self.max_dt) 20 | 21 | def solve(self, t_span, tol=1e-3, max_dt=np.inf, min_dt=1e-5): 22 | """Compute solution for t_span[0] <= t <= t_span[1], 23 | using N steps.""" 24 | t0, T = t_span 25 | self.tol = tol 26 | self.min_dt = min_dt 27 | self.max_dt = max_dt 28 | self.t = [t0] 29 | 30 | if self.neq == 1: 31 | self.u = [np.asarray(self.u0).reshape(1)] 32 | else: 33 | self.u = [self.u0] 34 | 35 | self.n = 0 36 | self.dt = 0.1 / np.linalg.norm(self.f(t0, self.u0)) 37 | 38 | loc_t = t0 39 | while loc_t < T: 40 | u_new, loc_error = self.advance() 41 | if loc_error < tol or self.dt < self.min_dt: 42 | loc_t += self.dt 43 | self.t.append(loc_t) 44 | self.u.append(u_new) 45 | self.dt = self.new_step_size(self.dt, loc_error) 46 | self.dt = min(self.dt, T - loc_t, max_dt) 47 | self.n += 1 48 | else: 49 | self.dt = self.new_step_size(self.dt, loc_error) 50 | return np.array(self.t), np.array(self.u) 51 | 52 | 53 | class EulerHeun(AdaptiveODESolver): 54 | def __init__(self, f, eta=0.9): 55 | super().__init__(f, eta) 56 | self.order = 1 57 | 58 | def advance(self): 59 | u, f, t = self.u, self.f, self.t 60 | dt = self.dt 61 | k1 = f(t[-1], u[-1]) 62 | k2 = f(t[-1] + dt, u[-1] + dt * k1) 63 | high = dt / 2 * (k1 + k2) 64 | low = dt * k1 65 | 66 | unew = u[-1] + low 67 | error = np.linalg.norm(high - low) 68 | return unew, error 69 | 70 | 71 | class RKF45(AdaptiveODESolver): 72 | def __init__(self, f, eta=0.9): 73 | super().__init__(f, eta) 74 | self.order = 4 75 | 76 | def advance(self): 77 | u, f, t = self.u, self.f, self.t 78 | dt = self.dt 79 | c2 = 1 / 4 80 | a21 = 1 / 4 81 | c3 = 3 / 8 82 | a31 = 3 / 32 83 | a32 = 9 / 32 84 | c4 = 12 / 13 85 | a41 = 1932 / 2197 86 | a42 = -7200 / 2197 87 | a43 = 7296 / 2197 88 | c5 = 1 89 | a51 = 439 / 216 90 | a52 = -8 91 | a53 = 3680 / 513 92 | a54 = -845 / 4104 93 | c6 = 1 / 2 94 | a61 = -8 / 27 95 | a62 = 2 96 | a63 = -3544 / 2565 97 | a64 = 1859 / 4104 98 | a65 = -11 / 40 99 | b1 = 25 / 216 100 | b2 = 0 101 | b3 = 1408 / 2565 102 | b4 = 2197 / 4104 103 | b5 = -1 / 5 104 | b6 = 0 105 | bh1 = 16 / 135 106 | bh2 = 0 107 | bh3 = 6656 / 12825 108 | bh4 = 28561 / 56430 109 | bh5 = -9 / 50 110 | bh6 = 2 / 55 111 | 112 | k1 = f(t[-1], u[-1]) 113 | k2 = f(t[-1] + c2 * dt, u[-1] + dt * (a21 * k1)) 114 | k3 = f(t[-1] + c3 * dt, u[-1] + dt * (a31 * k1 + a32 * k2)) 115 | k4 = f(t[-1] + c4 * dt, u[-1] + dt * (a41 * k1 + a42 * k2 + a43 * k3)) 116 | k5 = f(t[-1] + c5 * dt, u[-1] + dt * 117 | (a51 * k1 + a52 * k2 + a53 * k3 + a54 * k4)) 118 | k6 = f(t[-1] + c6 * dt, u[-1] + 119 | dt * (a61 * k1 + a62 * k2 + a63 * k3 120 | + a64 * k4 + a65 * k5)) 121 | 122 | low = dt * (b1 * k1 + b3 * k3 + b4 * k4 + b5 * k5) 123 | high = dt * (bh1 * k1 + bh3 * k3 + bh4 * k4 124 | + bh5 * k5 + bh6 * k6) 125 | 126 | unew = u[-1] + low 127 | error = np.linalg.norm(high - low) 128 | 129 | return unew, error 130 | 131 | 132 | if __name__ == '__main__': 133 | from hodgkinhuxley import * 134 | import matplotlib.pyplot as plt 135 | 136 | model = HodgkinHuxley() 137 | solver = RKF45(model) 138 | solver.set_initial_condition([-45, 0.31, 0.05, 0.59]) 139 | t, u = solver.solve((0, 50), tol=0.01) 140 | 141 | plt.plot(t, u[:, 0], '+') 142 | plt.show() 143 | -------------------------------------------------------------------------------- /docs/src/chapter4/ImplicitRK.py: -------------------------------------------------------------------------------- 1 | from ODESolver import * 2 | from scipy.optimize import root 3 | 4 | 5 | class ImplicitRK(ODESolver): 6 | def solve_stages(self): 7 | u, f, n, t = self.u, self.f, self.n, self.t 8 | s = self.stages 9 | k0 = f(t[n], u[n]) 10 | k0 = np.tile(k0,s) 11 | 12 | sol = root(self.stage_eq, k0) 13 | 14 | return np.split(sol.x, s) 15 | 16 | def stage_eq(self, k_all): 17 | a, c = self.a, self.c 18 | s, neq = self.stages, self.neq 19 | 20 | u, f, n, t = self.u, self.f, self.n, self.t 21 | dt = self.dt 22 | 23 | res = np.zeros_like(k_all) 24 | k = np.split(k_all, s) 25 | for i in range(s): 26 | fi = f(t[n] + c[i] * dt, u[n] + dt * 27 | sum([a[i, j] * k[j] for j in range(s)])) 28 | res[i * neq:(i + 1) * neq] = k[i] - fi 29 | 30 | return res 31 | 32 | def advance(self): 33 | b = self.b 34 | u, n, t = self.u, self.n, self.t 35 | dt = self.dt 36 | k = self.solve_stages() 37 | 38 | return u[n] + dt * sum(b_ * k_ for b_, k_ in zip(b, k)) 39 | 40 | 41 | class BackwardEuler(ImplicitRK): 42 | def __init__(self, f): 43 | super().__init__(f) 44 | self.stages = 1 45 | self.a = np.array([[1]]) 46 | self.c = np.array([1]) 47 | self.b = np.array([1]) 48 | 49 | 50 | class ImplicitMidpoint(ImplicitRK): 51 | def __init__(self, f): 52 | super().__init__(f) 53 | self.stages = 1 54 | self.a = np.array([[1 / 2]]) 55 | self.c = np.array([1 / 2]) 56 | self.b = np.array([1]) 57 | 58 | 59 | class Radau2(ImplicitRK): 60 | def __init__(self, f): 61 | super().__init__(f) 62 | self.stages = 2 63 | self.a = np.array([[5 / 12, -1 / 12], [3 / 4, 1 / 4]]) 64 | self.c = np.array([1 / 3, 1]) 65 | self.b = np.array([3 / 4, 1 / 4]) 66 | 67 | 68 | class Radau3(ImplicitRK): 69 | def __init__(self, f): 70 | super().__init__(f) 71 | self.stages = 3 72 | sq6 = np.sqrt(6) 73 | self.a = np.array([[(88 - 7 * sq6) / 360, 74 | (296 - 169 * sq6) / 1800, 75 | (-2 + 3 * sq6) / (225)], 76 | [(296 + 169 * sq6) / 1800, 77 | (88 + 7 * sq6) / 360, 78 | (-2 - 3 * sq6) / (225)], 79 | [(16 - sq6) / 36, (16 + sq6) / 36, 1 / 9]]) 80 | self.c = np.array([(4 - sq6) / 10, (4 + sq6) / 10, 1]) 81 | self.b = np.array([(16 - sq6) / 36, (16 + sq6) / 36, 1 / 9]) 82 | 83 | 84 | class SDIRK(ImplicitRK): 85 | def stage_eq(self, k, c_i, k_sum): 86 | u, f, n, t = self.u, self.f, self.n, self.t 87 | dt = self.dt 88 | gamma = self.gamma 89 | 90 | return k - f(t[n] + c_i * dt, u[n] + dt * (k_sum + gamma * k)) 91 | 92 | def solve_stages(self): 93 | u, f, n, t = self.u, self.f, self.n, self.t 94 | a, c = self.a, self.c 95 | s = self.stages 96 | 97 | k = f(t[n], u[n]) # initial guess for first stage 98 | k_sum = np.zeros_like(k) 99 | k_all = [] 100 | for i in range(s): 101 | k_sum = sum(a_ * k_ for a_, k_ in zip(a[i, :i], k_all)) 102 | k = root(self.stage_eq, k, args=(c[i], k_sum)).x 103 | k_all.append(k) 104 | return k_all 105 | 106 | 107 | class SDIRK2(SDIRK): 108 | def __init__(self, f): 109 | super().__init__(f) 110 | self.stages = 2 111 | gamma = (2 - np.sqrt(2)) / 2 112 | self.gamma = gamma 113 | self.a = np.array([[gamma, 0], 114 | [1 - gamma, gamma]]) 115 | self.c = np.array([gamma, 1]) 116 | self.b = np.array([1 - gamma, gamma]) 117 | 118 | 119 | class ESDIRK(SDIRK): 120 | def solve_stages(self): 121 | u, f, n, t = self.u, self.f, self.n, self.t 122 | a, c = self.a, self.c 123 | s = self.stages 124 | 125 | k = f(t[n], u[n]) # initial guess for first stage 126 | k_sum = np.zeros_like(k) 127 | k_all = [k] 128 | for i in range(1, s): 129 | k_sum = sum(a_ * k_ for a_, k_ in zip(a[i, :i], k_all)) 130 | k = root(self.stage_eq, k, args=(c[i], k_sum)).x 131 | k_all.append(k) 132 | 133 | return k_all 134 | 135 | 136 | class TR_BDF2(ESDIRK): 137 | def __init__(self, f): 138 | super().__init__(f) 139 | self.stages = 3 140 | gamma = 1 - np.sqrt(2) / 2 141 | beta = np.sqrt(2) / 4 142 | self.gamma = gamma 143 | self.a = np.array([[0, 0, 0], 144 | [gamma, gamma, 0], 145 | [beta, beta, gamma]]) 146 | self.c = np.array([0, 2 * gamma, 1]) 147 | self.b = np.array([beta, beta, gamma]) 148 | 149 | 150 | if __name__ == "__main__": 151 | registered_solver_classes.extend( 152 | [BackwardEuler, ImplicitMidpoint, Radau2, Radau3, SDIRK2, TR_BDF2]) 153 | test_exact_numerical_solution() 154 | -------------------------------------------------------------------------------- /docs/src/chapter4/ODESolver.py: -------------------------------------------------------------------------------- 1 | """ 2 | Version 2 of the ODESolver class hierarchy. This class works for systems 3 | of ODEs and for a single (scalar) ODE. 4 | """ 5 | 6 | import numpy as np 7 | 8 | 9 | class ODESolver: 10 | def __init__(self, f): 11 | # Wrap user's f in a new function that always 12 | # converts list/tuple to array (or let array be array) 13 | self.model = f 14 | self.f = lambda t, u: np.asarray(f(t, u), float) 15 | 16 | def set_initial_condition(self, u0): 17 | if np.isscalar(u0): # scalar ODE 18 | self.neq = 1 # no of equations 19 | u0 = float(u0) 20 | else: # system of ODEs 21 | u0 = np.asarray(u0) 22 | self.neq = u0.size # no of equations 23 | self.u0 = u0 24 | 25 | def solve(self, t_span, N): 26 | """Compute solution for 27 | t_span[0] <= t <= t_span[1], 28 | using N steps. 29 | Returns the solution and the 30 | time points as arrays. 31 | """ 32 | t0, T = t_span 33 | self.dt = (T - t0) / N 34 | self.t = np.zeros(N + 1) # N steps ~ N+1 time points 35 | if self.neq == 1: 36 | self.u = np.zeros(N + 1) 37 | else: 38 | self.u = np.zeros((N + 1, self.neq)) 39 | 40 | msg = "Please set initial condition before calling solve" 41 | assert hasattr(self, "u0"), msg 42 | 43 | self.t[0] = t0 44 | self.u[0] = self.u0 45 | 46 | for n in range(N): 47 | self.n = n 48 | self.t[n + 1] = self.t[n] + self.dt 49 | self.u[n + 1] = self.advance() 50 | return self.t, self.u 51 | 52 | def advance(self): 53 | raise NotImplementedError( 54 | "Advance method is not implemented in the base class") 55 | 56 | 57 | 58 | class ForwardEuler(ODESolver): 59 | def advance(self): 60 | u, f, n, t = self.u, self.f, self.n, self.t 61 | dt = self.dt 62 | return u[n] + dt * f(t[n], u[n]) 63 | 64 | class Heun(ODESolver): 65 | def advance(self): 66 | u, f, n, t = self.u, self.f, self.n, self.t 67 | dt = self.dt 68 | k1 = f(t[n], u[n]) 69 | k2 = f(t[n] + dt, u[n] + dt * k1) 70 | return u[n] + dt / 2 * (k1 + k2) 71 | 72 | class ExplicitMidpoint(ODESolver): 73 | def advance(self): 74 | u, f, n, t = self.u, self.f, self.n, self.t 75 | dt = self.dt 76 | dt2 = dt / 2.0 77 | k1 = f(t[n], u[n]) 78 | k2 = f(t[n] + dt2, u[n] + dt2 * k1) 79 | return u[n] + dt * k2 80 | 81 | class RungeKutta4(ODESolver): 82 | def advance(self): 83 | u, f, n, t = self.u, self.f, self.n, self.t 84 | dt = self.dt 85 | dt2 = dt / 2.0 86 | k1 = f(t[n], u[n],) 87 | k2 = f(t[n] + dt2, u[n] + dt2 * k1, ) 88 | k3 = f(t[n] + dt2, u[n] + dt2 * k2, ) 89 | k4 = f(t[n] + dt, u[n] + dt * k3, ) 90 | return u[n] + (dt / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4) 91 | 92 | def test_exact_numerical_solution(): 93 | """ 94 | Test the different methods for a problem 95 | where the analytical solution is known and linear. 96 | All the methods should be exact to machine precision 97 | for this choice. 98 | """ 99 | solver_classes = [ForwardEuler, Heun, 100 | ExplicitMidpoint, RungeKutta4] 101 | a = 0.2 102 | b = 3 103 | 104 | def f(t, u): 105 | return a 106 | 107 | def u_exact(t): 108 | """Exact u(t) corresponding to f above.""" 109 | return a * t + b 110 | 111 | u0 = u_exact(0) 112 | T = 8 113 | N = 10 114 | tol = 1E-14 115 | t_span = (0, T) 116 | for solver_class in solver_classes: 117 | solver = solver_class(f) 118 | solver.set_initial_condition(u0) 119 | t, u = solver.solve(t_span, N) 120 | u_e = u_exact(t) 121 | max_error = abs((u_e - u)).max() 122 | msg = f'{solver_class.__name__} failed with max_error={max_error}' 123 | assert max_error < tol, msg 124 | 125 | 126 | if __name__ == '__main__': 127 | test_exact_numerical_solution() 128 | -------------------------------------------------------------------------------- /docs/src/chapter4/check_error_adaptive_hh.py: -------------------------------------------------------------------------------- 1 | """ 2 | The code solves the Hodgkin-Huxley model with the 3 | RKF45, Euler-Heun, and TR-BDF2 adaptive RK solvers. 4 | The global error is estimated by comparing to a 5 | reference solution computed by solve_ivp. 6 | The tolerance, global error and number of steps 7 | are printed to the terminal. 8 | """ 9 | 10 | from AdaptiveImplicitRK import * 11 | from hodgkinhuxley import * 12 | import matplotlib.pyplot as plt 13 | from scipy.integrate import solve_ivp 14 | 15 | import numpy as np 16 | 17 | 18 | def rhs(t, u): 19 | return u 20 | 21 | 22 | def exact(t): 23 | return np.exp(t) 24 | 25 | 26 | solvers = {'TR-BDF2': [TR_BDF2_Adaptive, 2], 27 | 'RKF45': [RKF45, 4], 28 | 'EulerHeun': [EulerHeun, 1], 29 | } 30 | 31 | 32 | model = HodgkinHuxley() 33 | u0 = [-45, 0.31, 0.05, 0.59] 34 | t_span = (0, 50) 35 | 36 | ref_sol = solve_ivp(model, t_span, u0, rtol=1e-10, atol=1e-12) 37 | t_ref, u_ref = ref_sol.t, ref_sol.y 38 | 39 | for solver_name, (solver_class, order) in solvers.items(): 40 | solver = solver_class(model) 41 | solver.set_initial_condition(u0) 42 | 43 | print(f'{solver_name} order = {order}') 44 | print(f'Tolerance Global error Number of steps') 45 | for tol in [1.0, 0.1, 0.01]: 46 | t, u = solver.solve(t_span, tol) 47 | steps = len(t) 48 | e = abs(u[-1, 0] - u_ref[0, -1]) 49 | print(f'{tol:<8.3f} {e:<12.7f} {steps}') 50 | -------------------------------------------------------------------------------- /docs/src/chapter4/hodgkinhuxley.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from ODESolver import * 3 | 4 | 5 | class HodgkinHuxley: 6 | def __init__( 7 | self, 8 | Cm=1.0, 9 | gK=36, 10 | gNa=120.0, 11 | gL=0.3, 12 | EK=-77.0, 13 | ENa=50.0, 14 | EL=-54.4, 15 | I_stim=None): 16 | 17 | self.Cm = Cm # Membrane capacitance, in uF/cm^2 18 | self.gK = gK # Maximum conductance of potassium ion channels, in mS/cm^2 19 | self.gNa = gNa # Maximum conductance of sodium ion channels, in mS/cm^2 20 | self.gL = gL # Maximum conductance of leak channels, in mS/cm^2 21 | self.EK = EK # Nernst potential of potassium ions, in mV 22 | self.ENa = ENa # Nernst potential of sodium ions, in mV 23 | self.EL = EL # Nernst potential of leak channels, in mV 24 | if I_stim is None: 25 | self.I_stim = lambda t: 0 26 | else: 27 | self.I_stim = I_stim 28 | 29 | def alpha_n(self, V): 30 | return 0.01 * (V + 55.0) / (1.0 - np.exp(-0.1 * (V + 55.0))) 31 | 32 | def beta_n(self, V): 33 | return 0.125 * np.exp(-0.0125 * (V + 65.0)) 34 | 35 | def alpha_m(self, V): 36 | return 0.1 * (V + 40.0) / (1.0 - np.exp(-0.1 * (V + 40.0))) 37 | 38 | def beta_m(self, V): 39 | return 4.0 * np.exp(-0.0556 * (V + 65.0)) 40 | 41 | def alpha_h(self, V): 42 | return 0.07 * np.exp(-0.05 * (V + 65.0)) 43 | 44 | def beta_h(self, V): 45 | return 1.0 / (1.0 + np.exp(-0.1 * (V + 35.0))) 46 | 47 | def IK(self, V, n): 48 | return self.gK * n**4 * (V - self.EK) 49 | 50 | def INa(self, V, m, h): 51 | return self.gNa * m**3 * h * (V - self.ENa) 52 | 53 | def IL(self, V): 54 | return self.gL * (V - self.EL) 55 | 56 | def __call__(self, t, u): 57 | V, n, m, h = u 58 | dVdt = -(self.INa(V, m, h) + self.IK(V, n) + 59 | self.IL(V) - self.I_stim(t)) / self.Cm 60 | dndt = self.alpha_n(V) * (1.0 - n) - self.beta_n(V) * n 61 | dmdt = self.alpha_m(V) * (1.0 - m) - self.beta_m(V) * m 62 | dhdt = self.alpha_h(V) * (1.0 - h) - self.beta_h(V) * h 63 | return [dVdt, dndt, dmdt, dhdt] 64 | -------------------------------------------------------------------------------- /docs/src/chapter5/ODESolver.py: -------------------------------------------------------------------------------- 1 | """ 2 | Version 2 of the ODESolver class hierarchy. This class works for systems 3 | of ODEs and for a single (scalar) ODE. 4 | """ 5 | 6 | import numpy as np 7 | 8 | 9 | class ODESolver: 10 | def __init__(self, f): 11 | # Wrap user's f in a new function that always 12 | # converts list/tuple to array (or let array be array) 13 | self.model = f 14 | self.f = lambda t, u: np.asarray(f(t, u), float) 15 | 16 | def set_initial_condition(self, u0): 17 | if np.isscalar(u0): # scalar ODE 18 | self.neq = 1 # no of equations 19 | u0 = float(u0) 20 | else: # system of ODEs 21 | u0 = np.asarray(u0) 22 | self.neq = u0.size # no of equations 23 | self.u0 = u0 24 | 25 | def solve(self, t_span, N): 26 | """Compute solution for 27 | t_span[0] <= t <= t_span[1], 28 | using N steps. 29 | Returns the solution and the 30 | time points as arrays. 31 | """ 32 | t0, T = t_span 33 | self.dt = (T - t0) / N 34 | self.t = np.zeros(N + 1) # N steps ~ N+1 time points 35 | if self.neq == 1: 36 | self.u = np.zeros(N + 1) 37 | else: 38 | self.u = np.zeros((N + 1, self.neq)) 39 | 40 | msg = "Please set initial condition before calling solve" 41 | assert hasattr(self, "u0"), msg 42 | 43 | self.t[0] = t0 44 | self.u[0] = self.u0 45 | 46 | for n in range(N): 47 | self.n = n 48 | self.t[n + 1] = self.t[n] + self.dt 49 | self.u[n + 1] = self.advance() 50 | return self.t, self.u 51 | 52 | def advance(self): 53 | raise NotImplementedError( 54 | "Advance method is not implemented in the base class") 55 | 56 | 57 | class ForwardEuler(ODESolver): 58 | def advance(self): 59 | u, f, n, t = self.u, self.f, self.n, self.t 60 | dt = self.dt 61 | return u[n] + dt * f(t[n], u[n]) 62 | 63 | 64 | class Heun(ODESolver): 65 | def advance(self): 66 | u, f, n, t = self.u, self.f, self.n, self.t 67 | dt = self.dt 68 | k1 = f(t[n], u[n]) 69 | k2 = f(t[n] + dt, u[n] + dt * k1) 70 | return u[n] + dt / 2 * (k1 + k2) 71 | 72 | 73 | class ExplicitMidpoint(ODESolver): 74 | def advance(self): 75 | u, f, n, t = self.u, self.f, self.n, self.t 76 | dt = self.dt 77 | dt2 = dt / 2.0 78 | k1 = f(t[n], u[n]) 79 | k2 = f(t[n] + dt2, u[n] + dt2 * k1) 80 | return u[n] + dt * k2 81 | 82 | 83 | class RungeKutta4(ODESolver): 84 | def advance(self): 85 | u, f, n, t = self.u, self.f, self.n, self.t 86 | dt = self.dt 87 | dt2 = dt / 2.0 88 | k1 = f(t[n], u[n],) 89 | k2 = f(t[n] + dt2, u[n] + dt2 * k1, ) 90 | k3 = f(t[n] + dt2, u[n] + dt2 * k2, ) 91 | k4 = f(t[n] + dt, u[n] + dt * k3, ) 92 | return u[n] + (dt / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4) 93 | 94 | 95 | def test_exact_numerical_solution(): 96 | """ 97 | Test the different methods for a problem 98 | where the analytical solution is known and linear. 99 | All the methods should be exact to machine precision 100 | for this choice. 101 | """ 102 | solver_classes = [ForwardEuler, Heun, 103 | ExplicitMidpoint, RungeKutta4] 104 | a = 0.2 105 | b = 3 106 | 107 | def f(t, u): 108 | return a 109 | 110 | def u_exact(t): 111 | """Exact u(t) corresponding to f above.""" 112 | return a * t + b 113 | 114 | u0 = u_exact(0) 115 | T = 8 116 | N = 10 117 | tol = 1E-14 118 | t_span = (0, T) 119 | for solver_class in solver_classes: 120 | solver = solver_class(f) 121 | solver.set_initial_condition(u0) 122 | t, u = solver.solve(t_span, N) 123 | u_e = u_exact(t) 124 | max_error = abs((u_e - u)).max() 125 | msg = f'{solver_class.__name__} failed with max_error={max_error}' 126 | assert max_error < tol, msg 127 | 128 | 129 | if __name__ == '__main__': 130 | test_exact_numerical_solution() 131 | -------------------------------------------------------------------------------- /docs/src/chapter5/SEEIIR.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from ODESolver import * 4 | 5 | 6 | class SEEIIR: 7 | def __init__(self, beta=0.33, r_ia=0.1, 8 | r_e2=1.25, lmbda_1=0.33, 9 | lmbda_2=0.5, p_a=0.4, mu=0.2): 10 | 11 | self.beta = beta 12 | self.r_ia = r_ia 13 | self.r_e2 = r_e2 14 | self.lmbda_1 = lmbda_1 15 | self.lmbda_2 = lmbda_2 16 | self.p_a = p_a 17 | self.mu = mu 18 | 19 | def __call__(self, t, u): 20 | beta = self.beta 21 | r_ia = self.r_ia 22 | r_e2 = self.r_e2 23 | lmbda_1 = self.lmbda_1 24 | lmbda_2 = self.lmbda_2 25 | p_a = self.p_a 26 | mu = self.mu 27 | 28 | S, E1, E2, I, Ia, R = u 29 | N = sum(u) 30 | dS = -beta * S * I / N - r_ia * beta * S * Ia / N \ 31 | - r_e2 * beta * S * E2 / N 32 | dE1 = beta * S * I / N + r_ia * beta * S * Ia / N \ 33 | + r_e2 * beta * S * E2 / N - lmbda_1 * E1 34 | dE2 = lmbda_1 * (1 - p_a) * E1 - lmbda_2 * E2 35 | dI = lmbda_2 * E2 - mu * I 36 | dIa = lmbda_1 * p_a * E1 - mu * Ia 37 | dR = mu * (I + Ia) 38 | return [dS, dE1, dE2, dI, dIa, dR] 39 | 40 | 41 | S_0 = 5.5e6 42 | E1_0 = 0.0 43 | E2_0 = 100.0 44 | I_0 = 0.0 45 | Ia_0 = 0.0 46 | R_0 = 0.0 47 | U0 = [S_0, E1_0, E2_0, I_0, Ia_0, R_0] 48 | 49 | 50 | model = SEEIIR() 51 | solver = RungeKutta4(model) 52 | solver.set_initial_condition(U0) 53 | 54 | t_span = (0, 300) 55 | N = 200 56 | t, u = solver.solve(t_span, N) 57 | S = u[:, 0] 58 | E1 = u[:, 1] 59 | E2 = u[:, 2] 60 | I = u[:, 3] 61 | Ia = u[:, 4] 62 | R = u[:, 5] 63 | 64 | print(max(I)) 65 | plt.plot(t, S, label='S') 66 | plt.plot(t, I, label='I') 67 | plt.plot(t, Ia, label='Ia') 68 | plt.plot(t, R, label='R') 69 | plt.legend() 70 | plt.title('Disease dynamics predicted by the SEEIIR model') 71 | plt.xlabel('Time (days)') 72 | plt.ylabel('People in each category') 73 | plt.savefig('seir_fig0.pdf') 74 | plt.show() 75 | -------------------------------------------------------------------------------- /docs/src/chapter5/SEIR.py: -------------------------------------------------------------------------------- 1 | from ODESolver import RungeKutta4 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | class SEIR: 6 | def __init__(self, beta, mu, nu, gamma): 7 | self.beta = beta 8 | self.mu = mu 9 | self.nu = nu 10 | self.gamma = gamma 11 | 12 | def __call__(self, t, u): 13 | S, E, I, R = u 14 | N = S + I + R + E 15 | dS = -self.beta * S * I / N + self.gamma * R 16 | dE = self.beta * S * I / N - self.mu * E 17 | dI = self.mu * E - self.nu * I 18 | dR = self.nu * I - self.gamma * R 19 | return [dS, dE, dI, dR] 20 | 21 | 22 | S0 = 1000 23 | E0 = 0 24 | I0 = 1 25 | R0 = 0 26 | model = SEIR(beta=1.0, mu=1.0 / 5, nu=1.0 / 7, gamma=1.0 / 50) 27 | 28 | solver = RungeKutta4(model) 29 | solver.set_initial_condition([S0, E0, I0, R0]) 30 | t_span = (0, 100) 31 | t, u = solver.solve(t_span, N=101) 32 | 33 | S = u[:, 0] 34 | E = u[:, 1] 35 | I = u[:, 2] 36 | R = u[:, 3] 37 | 38 | plt.plot(t, S, t, E, t, I, t, R) 39 | plt.show() 40 | -------------------------------------------------------------------------------- /docs/src/chapter5/SIR_class_v1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Class implementation of the SIR model. The model is exactly the 3 | same as the function implementation in SIR_fun.py, but the 4 | class implementation is more convenient for varying parameters. 5 | """ 6 | 7 | 8 | from ODESolver import RungeKutta4 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | 12 | 13 | class SIR: 14 | def __init__(self, beta, nu): 15 | self.beta = beta 16 | self.nu = nu 17 | 18 | def __call__(self, t, u): 19 | S, I, R = u[0], u[1], u[2] 20 | dS = -self.beta * S * I 21 | dI = self.beta * S * I - self.nu * I 22 | dR = self.nu * I 23 | return [dS, dI, dR] 24 | 25 | 26 | S0 = 1000 27 | I0 = 1 28 | R0 = 0 29 | 30 | model = SIR(beta=0.001, nu=1 / 7.0) 31 | solver = RungeKutta4(model) 32 | solver.set_initial_condition([S0, I0, R0]) 33 | t_span = (0, 100) 34 | t, u = solver.solve(t_span, N=101) 35 | S = u[:, 0] 36 | I = u[:, 1] 37 | R = u[:, 2] 38 | 39 | plt.plot(t, S, t, I, t, R) 40 | plt.legend(['S', 'I', 'R']) 41 | plt.xlabel('Time (days)') 42 | plt.ylabel('Number of people') 43 | plt.savefig('SIR_simple.pdf') 44 | plt.show() 45 | -------------------------------------------------------------------------------- /docs/src/chapter5/SIR_class_v2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Class implementation of the SIR model. The model is mostly 3 | the same as the one in SIR_class_v1.py, but immunity is 4 | lost after a while. The loss of immunity is modeled as 5 | a slow leak of people from the R category back to S. 6 | """ 7 | from ODESolver import RungeKutta4 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | 11 | 12 | class SIR: 13 | def __init__(self, beta, nu, gamma): 14 | self.beta = beta 15 | self.nu = nu 16 | self.gamma = gamma 17 | 18 | def __call__(self, t, u): 19 | S, I, R = u[0], u[1], u[2] 20 | dS = -self.beta * S * I + self.gamma * R 21 | dI = self.beta * S * I - self.nu * I 22 | dR = self.nu * I - self.gamma * R 23 | return [dS, dI, dR] 24 | 25 | 26 | S0 = 1000 27 | I0 = 1 28 | R0 = 0 29 | 30 | model = SIR(beta=0.001, nu=1 / 7.0, gamma=1.0 / 50) 31 | solver = RungeKutta4(model) 32 | solver.set_initial_condition([S0, I0, R0]) 33 | t_span = (0, 100) 34 | t, u = solver.solve(t_span, N=101) 35 | S = u[:, 0] 36 | I = u[:, 1] 37 | R = u[:, 2] 38 | 39 | plt.plot(t, S, t, I, t, R) 40 | plt.legend(['S', 'I', 'R']) 41 | plt.xlabel('Time (days)') 42 | plt.ylabel('Number of people') 43 | plt.savefig('SIR_immunity_loss.pdf') 44 | plt.show() 45 | -------------------------------------------------------------------------------- /docs/src/chapter5/SIR_fun.py: -------------------------------------------------------------------------------- 1 | from ODESolver import RungeKutta4 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | 6 | def SIR_model(t, u): 7 | beta = 0.001 8 | nu = 1 / 7.0 9 | S, I, R = u[0], u[1], u[2] 10 | dS = -beta * S * I 11 | dI = beta * S * I - nu * I 12 | dR = nu * I 13 | return [dS, dI, dR] 14 | 15 | 16 | S0 = 1000 17 | I0 = 1 18 | R0 = 0 19 | 20 | solver = RungeKutta4(SIR_model) 21 | solver.set_initial_condition([S0, I0, R0]) 22 | t_span = (0, 100) 23 | t, u = solver.solve(t_span, N=101) 24 | S = u[:, 0] 25 | I = u[:, 1] 26 | R = u[:, 2] 27 | 28 | plt.plot(t, S, t, I, t, R) 29 | plt.show() 30 | --------------------------------------------------------------------------------