├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── README.mdtex ├── examples └── circle.js ├── gulpfile.js ├── images ├── begineqnarray-yt-ft-yt-yt_0-y_0-endeqnarray-0d72e38d4c.png ├── begineqnarray-yt-ft-yt-yt_0-y_0-endeqnarray-870096dfab.png ├── delta-t-9813ae7971.png ├── n-66e1b1ee17.png ├── t-3f19307093.png ├── y-adb83ba1d7.png ├── y_n1-y_n-ft_n-y_n-a622b96922.png ├── y_n1-y_n-ft_n-y_n-delta-t-7e1ddc48e6.png ├── yt-ft-yt-4304a0aa5e.png ├── yt-ft-yt-fae25965d3.png ├── yt-ft-yt-yt_0-y_0-309707f928.png ├── yt-ft-yt-yt_0-y_0-f7515619c6.png ├── yt_0-y_0-42d14f447f.png └── yt_0-y_0-d7b398c658.png ├── lib └── index.js ├── package.json └── test └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled binary addons (http://nodejs.org/api/addons.html) 22 | build/Release 23 | build 24 | 25 | # Dependency directory 26 | # Deployed apps should consider commenting this line out: 27 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 28 | node_modules 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12" 4 | - "0.11" 5 | - "0.10" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ricky Reusser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ode-euler [![Build Status](https://travis-ci.org/scijs/ode-euler.svg)](https://travis-ci.org/scijs/ode-euler) [![npm version](https://badge.fury.io/js/ode-euler.svg)](http://badge.fury.io/js/ode-euler) [![Dependency Status](https://david-dm.org/scijs/ode-euler.svg)](https://david-dm.org/scijs/ode-euler) 2 | 3 | > Integrate a system of ODEs using the Euler method 4 | 5 | 6 | ## Introduction 7 | 8 | This module integrates a system of ordinary differential equations of the form

undefined

undefined

where undefined is a vector of length undefined. Given time step undefined, the Euler method integrates the ODE with update

undefined

9 | 10 | ## Install 11 | 12 | ```bash 13 | $ npm install ode-euler 14 | ``` 15 | 16 | ## Example 17 | 18 | ```javascript 19 | var euler = require('ode-euler') 20 | 21 | var deriv = function(dydt, y, t) { 22 | dydt[0] = -y[1] 23 | dydt[1] = y[0] 24 | } 25 | 26 | var y0 = [1,0] 27 | var n = 1000 28 | var t0 = 0 29 | var dt = 2.0 * Math.PI / n 30 | 31 | var integrator = euler( y0, deriv, t0, dt ) 32 | 33 | // Integrate 1000 steps: 34 | integrator.steps(n) 35 | 36 | // Integrate all the way around a circle: 37 | // => integrator.y = [ 1.0199349143076457, -0.00008432969374211775 ] 38 | ``` 39 | 40 | 41 | 42 | ## API 43 | 44 | ### `require('ode-euler')( y0, deriv, t0, dt )` 45 | **Arguments:** 46 | - `y0`: an array or typed array containing initial conditions. This vector is updated in-place with each integrator step. 47 | - `deriv`: a function that calculates the derivative. Format is `function( dydt, y, t )`. Inputs are current state `y` and current time `t`, output is calcualted derivative `dydt`. 48 | - `t0`: initial time undefined. 49 | - `dt`: time step undefined. 50 | 51 | **Returns**: 52 | Initialized integrator object. 53 | 54 | **Properties:** 55 | - `n`: dimension of `y0`. 56 | - `y`: current state. Initialized as a shallow copy of input `y0`. 57 | - `deriv`: function that calcualtes derivative. Initialized from input. May be changed. 58 | - `t`: current time, incremented by `dt` with each time step. 59 | - `dt`: time step undefined. Initialized from input `dt`. May be changed. 60 | 61 | **Methods:** 62 | - `.step()`: takes a single step of the Euler integrator and stores the result in-place in the `y` property. 63 | - `.steps( n )`: takes `n` steps of the Euler integrator, storing the result in-place in the `y` property. 64 | 65 | ## Credits 66 | 67 | (c) 2015 Ricky Reusser. MIT License -------------------------------------------------------------------------------- /README.mdtex: -------------------------------------------------------------------------------- 1 | # ode-euler [![Build Status](https://travis-ci.org/scijs/ode-euler.svg)](https://travis-ci.org/scijs/ode-euler) [![npm version](https://badge.fury.io/js/ode-euler.svg)](http://badge.fury.io/js/ode-euler) [![Dependency Status](https://david-dm.org/scijs/ode-euler.svg)](https://david-dm.org/scijs/ode-euler) 2 | 3 | > Integrate a system of ODEs using the Euler method 4 | 5 | 6 | ## Introduction 7 | 8 | This module integrates a system of ordinary differential equations of the form $$ y'(t) &=& f(t, y(t)),$$ $$y(t_0) = y_0$$ where $y$ is a vector of length $n$. Given time step $\Delta t$, the Euler method integrates the ODE with update $$y_{n+1} = y_{n} + f(t_n, y_n) \Delta t.$$ 9 | 10 | ## Install 11 | 12 | ```bash 13 | $ npm install ode-euler 14 | ``` 15 | 16 | ## Example 17 | 18 | ```javascript 19 | var euler = require('ode-euler') 20 | 21 | var deriv = function(dydt, y, t) { 22 | dydt[0] = -y[1] 23 | dydt[1] = y[0] 24 | } 25 | 26 | var y0 = [1,0] 27 | var n = 1000 28 | var t0 = 0 29 | var dt = 2.0 * Math.PI / n 30 | 31 | var integrator = euler( y0, deriv, t0, dt ) 32 | 33 | // Integrate 1000 steps: 34 | integrator.steps(n) 35 | 36 | // Integrate all the way around a circle: 37 | // => integrator.y = [ 1.0199349143076457, -0.00008432969374211775 ] 38 | ``` 39 | 40 | 41 | 42 | ## API 43 | 44 | ### `require('ode-euler')( y0, deriv, t0, dt )` 45 | **Arguments:** 46 | - `y0`: an array or typed array containing initial conditions. This vector is updated in-place with each integrator step. 47 | - `deriv`: a function that calculates the derivative. Format is `function( dydt, y, t )`. Inputs are current state `y` and current time `t`, output is calcualted derivative `dydt`. 48 | - `t0`: initial time $t$. 49 | - `dt`: time step $\Delta t$. 50 | 51 | **Returns**: 52 | Initialized integrator object. 53 | 54 | **Properties:** 55 | - `n`: dimension of `y0`. 56 | - `y`: current state. Initialized as a shallow copy of input `y0`. 57 | - `deriv`: function that calcualtes derivative. Initialized from input. May be changed. 58 | - `t`: current time, incremented by `dt` with each time step. 59 | - `dt`: time step $\Delta t$. Initialized from input `dt`. May be changed. 60 | 61 | **Methods:** 62 | - `.step()`: takes a single step of the Euler integrator and stores the result in-place in the `y` property. 63 | - `.steps( n )`: takes `n` steps of the Euler integrator, storing the result in-place in the `y` property. 64 | 65 | ## Credits 66 | 67 | (c) 2015 Ricky Reusser. MIT License 68 | -------------------------------------------------------------------------------- /examples/circle.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var euler = require('../lib') 4 | 5 | var dydt = function(dydt, y, t) { 6 | dydt[0] = -y[1] 7 | dydt[1] = y[0] 8 | } 9 | 10 | var y0 = [1,0] 11 | var n = 1000 12 | var t0 = 0 13 | var dt = 2.0 * Math.PI / n 14 | 15 | var integrator = euler( y0, dydt, t0, dt ) 16 | 17 | // Integrate 1000 steps: 18 | integrator.steps(n) 19 | 20 | console.log(integrator.y) 21 | 22 | // Integrate all the way around a circle: 23 | // => integrator.y = [ 1.0199349143076457, -0.00008432969374211775 ] 24 | 25 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | , mdEqs = require('gulp-markdown-equations') 3 | , tap = require('gulp-tap') 4 | , filter = require('gulp-filter') 5 | , latex = require('gulp-latex') 6 | , pdftocairo = require('gulp-pdftocairo') 7 | 8 | 9 | gulp.task('mdtex',function() { 10 | var texFilter = filter('*.tex') 11 | var mdFilter = filter('*.md') 12 | 13 | var transform = mdEqs({ 14 | defaults: { 15 | display: { margin: '1pt' }, 16 | inline: { margin: '1pt' } 17 | } 18 | }) 19 | 20 | return gulp.src('*.mdtex') 21 | .pipe(transform) 22 | .pipe(texFilter) 23 | //.pipe(tap(function(file) { console.log(file.contents.toString()) })) 24 | .pipe(latex()) 25 | .pipe(pdftocairo({format: 'png'})) 26 | .pipe(gulp.dest('images')) 27 | .pipe(tap(function(file) { 28 | transform.completeSync(file,function() { 29 | var img = ''+this.alt+'' 31 | return this.display ? '

'+img+'

' : img 32 | }) 33 | })) 34 | .pipe(texFilter.restore()).pipe(mdFilter) 35 | .pipe(gulp.dest('./')) 36 | }) 37 | -------------------------------------------------------------------------------- /images/begineqnarray-yt-ft-yt-yt_0-y_0-endeqnarray-0d72e38d4c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/begineqnarray-yt-ft-yt-yt_0-y_0-endeqnarray-0d72e38d4c.png -------------------------------------------------------------------------------- /images/begineqnarray-yt-ft-yt-yt_0-y_0-endeqnarray-870096dfab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/begineqnarray-yt-ft-yt-yt_0-y_0-endeqnarray-870096dfab.png -------------------------------------------------------------------------------- /images/delta-t-9813ae7971.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/delta-t-9813ae7971.png -------------------------------------------------------------------------------- /images/n-66e1b1ee17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/n-66e1b1ee17.png -------------------------------------------------------------------------------- /images/t-3f19307093.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/t-3f19307093.png -------------------------------------------------------------------------------- /images/y-adb83ba1d7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/y-adb83ba1d7.png -------------------------------------------------------------------------------- /images/y_n1-y_n-ft_n-y_n-a622b96922.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/y_n1-y_n-ft_n-y_n-a622b96922.png -------------------------------------------------------------------------------- /images/y_n1-y_n-ft_n-y_n-delta-t-7e1ddc48e6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/y_n1-y_n-ft_n-y_n-delta-t-7e1ddc48e6.png -------------------------------------------------------------------------------- /images/yt-ft-yt-4304a0aa5e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/yt-ft-yt-4304a0aa5e.png -------------------------------------------------------------------------------- /images/yt-ft-yt-fae25965d3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/yt-ft-yt-fae25965d3.png -------------------------------------------------------------------------------- /images/yt-ft-yt-yt_0-y_0-309707f928.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/yt-ft-yt-yt_0-y_0-309707f928.png -------------------------------------------------------------------------------- /images/yt-ft-yt-yt_0-y_0-f7515619c6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/yt-ft-yt-yt_0-y_0-f7515619c6.png -------------------------------------------------------------------------------- /images/yt_0-y_0-42d14f447f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/yt_0-y_0-42d14f447f.png -------------------------------------------------------------------------------- /images/yt_0-y_0-d7b398c658.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scijs/ode-euler/7cf782a33f1adceabcc8ee8c7d9f10bd6252d526/images/yt_0-y_0-d7b398c658.png -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = IntegratorFactory 4 | 5 | var Integrator = function Integrator( y0, deriv, t, dt ) { 6 | // Bind variables to this: 7 | this.deriv = deriv 8 | this.y = y0 9 | this.n = this.y.length 10 | this.dt = dt 11 | this.t = t 12 | 13 | // Create a scratch array into which we compute the derivative: 14 | this._ctor = this.y.constructor 15 | this._yp = new this._ctor( this.n ) 16 | } 17 | 18 | Integrator.prototype.step = function() { 19 | 20 | this.deriv( this._yp, this.y, this.t ) 21 | 22 | for(var i=0; i y = exp(-t) 129 | var result = richardson(function(h) { 130 | var f = function(dydt, y) { dydt[0] = -y[0] } 131 | return euler( new ctor([1]), f, 0, h ).step().y[0] - Math.exp(-h) 132 | }, [0.06,0.01], { f: 0 } ) 133 | 134 | assert.closeTo( result.n, 2, 1e-2, 'n ~ 2' ) 135 | }) 136 | 137 | 138 | it('local truncation error is order O(h^2)', function() { 139 | var result = richardson(function(h) { 140 | 141 | // Integrate along a sector of a circle: 142 | var f = function(dydt, y) { dydt[0] = -y[1]; dydt[1] = y[0] } 143 | var i = euler( new ctor([1,0]), f, 0, h ).step() 144 | 145 | // Return the distance from the expected endpoint: 146 | return Math.sqrt( Math.pow(i.y[0]-Math.cos(h),2) + Math.pow(i.y[1]-Math.sin(h),2) ) 147 | 148 | }, 2*Math.PI/100, { f: 0 } ) 149 | 150 | assert.closeTo( result.n, 2, 1e-2, 'n ~ 2' ) 151 | }) 152 | 153 | it('total accumulated error is order O(h^1)', function() { 154 | 155 | var result = richardson(function(h) { 156 | 157 | // Integrate around a circle with this step size: 158 | var f = function(dydt, y) { dydt[0] = -y[1]; dydt[1] = y[0] } 159 | var i = euler( new ctor([1,0]), f, 0, h ).steps( Math.floor(2*Math.PI/h + 0.5) ) 160 | 161 | // Return the distance from the expected endpoint: 162 | return Math.sqrt( Math.pow(i.y[0]-1,2) + Math.pow(i.y[1],2) ) 163 | 164 | }, 2*Math.PI/100, { f: 0 } ) 165 | 166 | assert.closeTo( result.n, 1, 1e-1, 'n ~ 1' ) 167 | }) 168 | 169 | it('total accumulated error is order O(h^1) in all variables', function() { 170 | 171 | var result = richardson(function(h) { 172 | // Integrate around a circle at an accelerating rate 173 | var f = function(dydt, y, t) { 174 | var s = Math.sin(t * Math.PI) * Math.PI / 2 175 | dydt[0] = -y[1]* 2 * Math.PI * s 176 | dydt[1] = y[0]* 2 * Math.PI * s 177 | } 178 | var i = euler( new ctor([1,0]), f, 0, h ).steps( Math.floor(1/h+0.5)) 179 | 180 | // Return the distance from the expected endpoint: 181 | return Math.sqrt( Math.pow(i.y[0]-1,2) + Math.pow(i.y[1],2) ) 182 | 183 | }, 0.001, { f: 0 } ) 184 | 185 | assert.closeTo( result.n, 1, 1e-2, 'n ~ 1' ) 186 | }) 187 | 188 | 189 | 190 | }) 191 | 192 | }) 193 | 194 | }) 195 | --------------------------------------------------------------------------------