Generic-RK
This npm package contains generic Runge-Kutta code that can be used to implement any explicit or embedded methods. The methods currently implemented for use are Forward Euler, Midpoint, Classic 4th order, Cash-Karp, Fehlberg 4-5, and Fehlberg 7-8. If another is needed, simply email me the Butcher tableau, and I'll add it.
The proper way to implement this package is documented below by example. We will create a pendulum entity object.
Example
First import the package:
const RK = ;
ODESystem
Extend Next we will create a class that extends the ODESystem
class. Every ODESystem child must implement two different methods odeSetup()
and updateODEs()
.
ODESystem { //TODO: add logic } { //TODO: add logic } { //TODO: add logic }
Constructor
While not strictly necessary, using the constructor method is good practice.
{ super; //call super constructor so you can access `this` thist = t; //add a time field, which will also be our interval field //We make use of the `RK.Pointer` object. This object emulates //the way pointers work in other languages. This allows us to //create our object in the way that makes most sense for the model //and then not have to worry about copying values back and forth //from the rk method. thispx = px; //add a position field thisvx = vx; //add a velocity field thisax = 00; //add an acceleration field thispx0 = px; //save the initial position thisvx0 = vx; //save the initial velocity thisomega = Math; //natural frequency for the pendulum //construct the rk object thisrk = 2 //the number of ode's in the system RKMethodsCashKarp //Use the RK.Methods Enum to choose the embedded or explicit method to use //We will use CashKarp which is 4th/5th order embedded method. I've chosen //this method to illustrate the more complex initialization and because //some the simpler methods do not stay accurate for very long minDh: 10e-4 //this is the minimum interval to allow, it will overrule the //interval calculation based on the desired error value. maxDh: 10 //this is the maximum interval to allow, it will overrule the //interval calculation based on the desired error value. //maxDh is ignored for explicit (or non-interval varying methods) error:10e-4 //the desired allowed error between the 4th and 5th order methods //error is ignored for explicit (or non-interval varying methods) safetyFactor:09 //the safety factor used to make it less likely to have to re-run //rk passes too frequently for interval-varying methods. //safetyFactor is ignored for explicit (or non-interval varying methods) ; this; //this is a call to one of the required overridden method }
odeSetup()
Implement This methods purpose is to link the model's fields to the rk's ode's.
{ //`this.rk.odes` should have been initialized during the construction of `this.rk`. //`this.rk.odes` length should match the first value in the rk costructor. //we will now implement the benefit of the rk.pointer object by connecting the fields of //the model to the rk method odes. We will also define the derivative and integral relations //of the ode system in this method. thisrkodes0; //note that the same value that is the derivative in the previous ode is the integral of this ode //this will allow us to only have to calculate the acceleration in order to calculate both //velocity and position. thisrkodes1; }
updateODEs()
Implement This method's purpose is to calculate the top level derivative of your system.
{ //this method's purpose is to calculate the top level derivatives od your system. //to set the value of a pointer object you can use `ptr.val = ...;` as shown below. thisaxval = -Math * thispxval; //formula for the current acceleration //of a pendulum, based on curent position }
Congratulations you have implemented the Adaptive Stepsize Cash-Karp RK method for a pendulum. Next I we will implement a few other optional but I believe helpful patterns for entities using this package.
Timestep
I believe it is convenient to wrap the rk.motion
call inside of a timestep function. For other more complicated entities there is often additional logic that needs to be handle at each movement forward in time.
{ //target time is the desired time for the entity to now be to thist = thisrk; //the `rk.motion` does not alter the interval value //passed in due to the way js arguments work. It returns //`targetTime` when the method call completes.}
Solution
When not simulating a toy model (like a pendulum), one cannot usually exactly calculate the correct value at a given time, hence why we create a simulation. However, since this is likely your first entity created with the generic-rk package, and it might be nice for a sanity check we will also implement the solution function for this pendulum.
{ let phi = Math; //calculate the initial angle let amplitude = thispx0/Math; //calculate the initial amplitude let position = amplitude*Math; //caculate the current position let velocity = -thisomega*amplitude*Math; //calculate the current velocity let acceleration = -thisomega*thisomega*amplitude*Math; //calculate the current acceleration return time position velocity acceleration; //return the calculated values so you can check your work.}
Run the System
Now I will walk you through running the pendulum model and performing a simple test.
var p0 = -2000; //set the initial positionvar v0 = -670; //set the initial velocityvar startTime = 00; //set the initial timevar endTime = 100; //set the end time var e = startTime p0 v0; //construct your pendulum entitye; //move pendulum entity forward to the end time var output = et epxval evxval eaxval; //get the current values that you want to checkvar correct = e; //calculate the correct values console;console; //if `output` and `correct` are very close it is very likely you implemented everything correctly//keep in mind the error will get larger over time, and if you are using simpler RK methods it might//diverge in accuracy much more quickly depending on your model.