(set-ode-integration-method! 'qcrk4)
(set-ode-integration-method! 'bulirsch-stoer)
(set-ode-integration-method! 'qcctrap2)
(set-ode-integration-method! 'explicit-gear)
Initial-value problems for ordinary differential equations can be attacked by a great many specialized methods. Numerical analysts agree that there is no best method. Each has situations where it works best and other situations where it fails or is not very good. Also, each technique has numerous parameters, options and defaults.
The default integration method is Bulirsch-Stoer. Usually, the Bulirsch-Stoer algorithm will give better and faster results than others, but there are applications where a quality-controlled trapezoidal method or a quality-controlled 4th order Runge-Kutta method is appropriate. The algorithm used can be set by the user:
(set-ode-integration-method! 'qcrk4)
(set-ode-integration-method! 'bulirsch-stoer)
(set-ode-integration-method! 'qcctrap2)
(set-ode-integration-method! 'explicit-gear)
In Emmy we currently only support the Gragg-Bulirsch-Stoer algorithm. If you need any of the others, please file a PR and we can discuss how to get these in for you. |
The integration methods all automatically select the step sizes to maintain the
error tolerances. But if we have an exceptionally stiff system, or a bad
discontinuity, for most integrators the step size will go down to zero and the
integrator will make no progress. If you encounter such a disaster try
explicit-gear
.
We have programs that implement other methods of integration, such as an implicit version of Gear’s stiff solver, and we have a whole language for describing error control, but these features are not available through this interface.
The two main interfaces are evolve
and state-advancer
.
The procedure state-advancer
is used to advance the state of a system
according to a system of first order ordinary differential equations for a
specified interval of the independent variable. The state may have arbitrary
structure, however we require that the first component of the state is the
independent variable.
The procedure evolve
uses state-advancer
to repeatedly advance the state of
the system by a specified interval, examining aspects of the state as the
evolution proceeds.
In the following descriptions we assume that sysder
is a user provided
procedure that gives the parametric system derivative. The parametric system
derivative takes parameters, such as a mass or length, and produces a procedure
that takes a state and returns the derivative of the state. Thus, the system
derivative takes arguments in the following way:
((sysder parameter-1 ... parameter-n) state)
There may be no parameters, but then the system derivative procedure must still be called with no arguments to produce the procedure that takes states to the derivative of the state.
For example, if we have the differential equations for an ellipse centered on the origin and aligned with the coordinate axes:
Dx(t) = -a y(t) Dy(t) = +b x(t)
We can make a parametric system derivative for this system as follows:
(defn ellipse-sysder [a b]
(fn [[t x y]]
(up 1 ;; dt/dt
(* -1 a y) ;; dx/dt
(* b x)))) ;; dy/dt
The procedure evolve
is invoked as follows:
((evolve sysder . parameters)
initial-state dt final-t optional-opts)
The user can pass a procedure via the keyword :observe
in optional-opts
that
takes the state as an argument.
The :observe
fn is passed successive states of the system as the evolution
proceeds. For example it might be used to print the state or to plot some
interesting function of the state.
The interval between calls to the monitor is the argument dt
. The evolution
stops when the independent variable is larger than final-t
. The optional
keyword argument parameter :epsilon
specifies the allowable error.
For example, we can evolve our state forward for 10 seconds:
((evolve ellipse-sysder 0.5 2.0)
(up 0. .5 .5) ;; initial state
0.01 ;; step size
10.0) ;; final value of t
;;=> (up 9.99999999999992 -0.2835304866702712 -0.9635568769766077)
To take more control of the integration one may use the state advancer directly.
The procedure state-advancer
is invoked as follows:
((state-advancer sysder . parameters) start-state dt optional-args)
The state advancer will give a new state resulting from evolving the start state
by the increment dt
of the independent variable. The allowed local truncation
error is specified by the optional keyword argument :epsilon
:
For example,
((state-advancer ellipse-sysder 0.5 2.0)
(up 0 0.5 0.5) 3.0 {:epsilon 1e-10})
;;=> (up 2.999999999999995 -0.530276250315008 -0.35387624023977055)
For a more complex example that shows the use of substructure in the state, consider the two-dimensional harmonic oscillator:
(defn harmonic-sysder [m k]
(fn [state]
(let [[x y] (coordinate state)
[px py] (momentum state)]
(up 1 ;; dt/dt
(up (/ px m) (/ py m)) ;; dq/dt
(down (* -1 k x) (* -1 k y)))))) ;; dp/dt
We could monitor the energy (the Hamiltonian):
(defn H [m k]
(fn [state]
(+ (/ (square (momentum state))
(* 2 m))
(* (/ 1 2) k
(square (coordinate state))))))
(let [initial-state (up 0
(up 0.5 0.5)
(down 0.1 0.0))
monitor (fn [_ state]
(println
(state->t state)
"\t"
((H m k) state)))
step-size 1.0
final-time 10
m 0.5
k 2.0]
((evolve harmonic-sysder m k)
initial-state
step-size
final-time
{:observe monitor}))
;; 0.0 0.51
;; 0.9999999999999986 0.5100000000359725
;; 1.9999999999999933 0.5100000001529045
;; 2.9999999999999893 0.5100000002789965
;; 3.999999999999993 0.5100000004050876
;; 5.000000000000025 0.5100000005311794
;; 6.0000000000000515 0.5100000006572705
;; 7.000000000000078 0.510000000783362
;; 8.000000000000105 0.5100000009094526
;; 9.0000000000001 0.5100000010355437
;; 10.000000000000103 0.510000001191079
There are a few constants that we find useful, and are thus provided in Emmy.
(def pi (* 4 (atan 1 1)))
(def -pi (- pi))
For numerical analysis, we provide the smallest number that when added to 1.0
makes a difference:
(def machine-epsilon
(loop [e 1.0]
(if (= 1.0 (+ e 1.0))
(* e 2.0)
(recur (/ e 2.0)))))
(def sqrt-machine-epsilon
(Math/sqrt machine-epsilon))
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close