Differential Equations in Visual Basic QuickStart Sample
Illustrates integrating systems of ordinary differential equations (ODE’s) in Visual Basic.
View this sample in: C# F# IronPython
Option Infer On
Imports System
Imports Numerics.NET.Calculus.OrdinaryDifferentialEquations
' We'll also need the basic math types:
Imports Numerics.NET
' <summary>
' Illustrates integrating systems of ordinary differential equations
' (ODE's) using classes in the
' Numerics.NET.Calculus.OrdinaryDifferentialEquations
' namespace of Numerics.NET.
Module DifferentialEquations
Sub main()
' The ClassicRungeKuttaIntegrator class implements the
' well-known 4th order fixed step Runge-Kutta method.
Dim rk4 As New ClassicRungeKuttaIntegrator()
' The differential equation is expressed in terms of a
' DifferentialFunction delegate. This is a function that
' takes a double (time value) and two Vectors (y value and
' return value) as arguments.
'
' The Lorentz function below defines the differential function
' for the Lorentz attractor.
rk4.DifferentialFunction = AddressOf Lorentz
' To perform the computations, we need to set the initial time...
rk4.InitialTime = 0.0
' and the initial value.
rk4.InitialValue = Vector.Create(1.0, 0.0, 0.0)
' The Runge-Kutta integrator also requires a step size:
rk4.InitialStepsize = 0.1
Console.WriteLine("Classic 4th order Runge-Kutta")
For i As Integer = 0 To 5
Dim t As Double = 0.2 * i
' The Integrate method performs the integration.
' It takes as many steps as necessary up to
' the specified time and returns the result:
Dim y = rk4.Integrate(t)
' The IterationsNeeded always shows the number of steps
' needed to arrive at the final time.
Console.WriteLine("{0:F2}: {1,20:F14} ({2} steps)", t, y, rk4.IterationsNeeded)
Next
' The RungeKuttaFehlbergIntegrator class implements a variable-step
' Runge-Kutta method. This method chooses the stepsize, and so
' is generally more reliable.
Dim rkf45 As New RungeKuttaFehlbergIntegrator()
rkf45.InitialTime = 0.0
rkf45.InitialValue = Vector.Create(1.0, 0.0, 0.0)
rkf45.DifferentialFunction = AddressOf Lorentz
rkf45.AbsoluteTolerance = 0.00000001
Console.WriteLine("Classic 4/5th order Runge-Kutta-Fehlberg")
For i As Integer = 0 To 5
Dim t As Double = 0.2 * i
Dim y = rkf45.Integrate(t)
Console.WriteLine("{0:F2}: {1,20:F14} ({2} steps)", t, y, rkf45.IterationsNeeded)
Next
' The CVODE integrator, part of the SUNDIALS suite of ODE solvers,
' is the most advanced of the ODE integrators.
Dim cvode As New CvodeIntegrator()
cvode.InitialTime = 0.0
cvode.InitialValue = Vector.Create(1.0, 0.0, 0.0)
cvode.DifferentialFunction = AddressOf Lorentz
cvode.AbsoluteTolerance = 0.00000001
Console.WriteLine("CVODE (multistep Adams-Moulton)")
For i As Integer = 0 To 5
Dim t As Double = 0.2 * i
Dim y = cvode.Integrate(t)
Console.WriteLine("{0:F2}: {1,20:F14} ({2} steps)", t, y, cvode.IterationsNeeded)
Next
'
' Other properties and methods
'
' The IntegrateSingleStep method takes just a single step
' in the direction of the specified final time:
cvode.IntegrateSingleStep(2.0)
' The CurrentTime property returns the corresponding time value.
Console.WriteLine($"t after single step: {cvode.CurrentTime}")
' CurrentValue returns the corresponding value:
Console.WriteLine($"Value at this t: {cvode.CurrentValue:F14}")
' The IntegrateMultipleSteps method performs the integration
' until at least the final time, and returns the last
' value that was computed:
cvode.IntegrateMultipleSteps(2.0)
Console.WriteLine($"t after multiple steps: {cvode.CurrentTime}")
'
' Stiff systems
'
Console.WriteLine(Environment.NewLine + "Stiff systems")
' The CVODE integrator is the only ODE integrator that can
' handle stiff problems well. The following example uses
' an equation for the size of a flame. The smaller
' the initial size, the more stiff the equation is.
' We compare the performance of the variable step Runge-Kutta method
' and the CVODE integrator:
Dim delta As Double = 0.0001
Dim tFinal As Double = 2 / delta
rkf45 = New RungeKuttaFehlbergIntegrator()
rkf45.InitialTime = 0.0
rkf45.InitialValue = Vector.Create(delta)
rkf45.DifferentialFunction = AddressOf Flame
Console.WriteLine("Classic 4/5th order Runge-Kutta-Fehlberg")
For i As Integer = 0 To 10
Dim t As Double = i * 0.1 * tFinal
Dim y = rkf45.Integrate(t)
Console.WriteLine("{0:F2}: {1,20:F14} ({2} steps)", t, y, rkf45.IterationsNeeded)
Next
' The CVODE integrator will use a special (implicit) method
' for stiff problems. To select this method, pass
' EdeKind.Stiff as an argument to the constructor.
cvode = New CvodeIntegrator(OdeKind.Stiff)
cvode.InitialTime = 0.0
cvode.InitialValue = Vector.Create(delta)
cvode.DifferentialFunction = AddressOf Flame
' When solving stiff problems, a Jacobian for the system
' must be supplied. See below for the definition.
cvode.SetDenseJacobian(AddressOf FlameJacobian)
' Notice how the CVODE integrator takes a lot fewer steps,
' and is also more accurate than the variable-step
' Runge-Kutta method.
Console.WriteLine("CVODE (Stiff - Backward Differentiation Formula)")
For i As Integer = 0 To 10
Dim t As Double = i * 0.1 * tFinal
Dim y = cvode.Integrate(t)
Console.WriteLine("{0:F2}: {1,20:F14} ({2} steps)", t, y, cvode.IterationsNeeded)
Next
Console.Write("Press Enter key to exit...")
Console.ReadLine()
End Sub
' <summary>
' Represents the differential function for the Lorentz attractor.
' </summary>
' <param name="t">The time value.</param>
' <param name="y">The current value.</param>
' <param name="dy">On output, the first derivatives.</param>
' <returns>A reference to <paramref name="dy"/>.</returns>
' <remarks><paramref name="dy"/> may be <see langword="nothing"/>
' on input.</remarks>
Function Lorentz(t As Double, y As Vector(Of Double), dy As Vector(Of Double)) As Vector(Of Double)
If (dy = Nothing) Then
dy = Vector.Create(Of Double)(3)
End If
Const sigma = 10.0
Const beta = 8.0 / 3.0
Const rho = 28.0
dy(0) = sigma * (y(1) - y(0))
dy(1) = y(0) * (rho - y(2)) - y(1)
dy(2) = y(0) * y(1) - beta * y(2)
Return dy
End Function
' <summary>
' Represents the differential function for the flame expansion
' problem.
' </summary>
Function Flame(t As Double, y As Vector(Of Double), dy As Vector(Of Double)) As Vector(Of Double)
If (dy = Nothing) Then
dy = Vector.Create(Of Double)(1)
End If
dy(0) = y(0) * y(0) * (1 - y(0))
Return dy
End Function
' <summary>
' Represents the Jacobian of the differential function
' for the flame expansion problem.
' </summary>
' <param name="t">The time value.</param>
' <param name="y">The current value.</param>
' <param name="dy">The current value of the first derivatives.</param>
' <param name="J">A Matrix that, on output, contains the value
' of the Jacobian.</param>
' <returns>A reference to <paramref name="J"/>.</returns>
' <remarks>The Jacobian is the matrix of partial derivatives of each
' equation in the system with respect to each variable in the system.
' <paramref name="J"/> may be <see langword="nothing"/>
' on input.</remarks>
Function FlameJacobian(t As Double, y As Vector(Of Double),
dy As Vector(Of Double), J As Matrix(Of Double)) As Matrix(Of Double)
If (J = Nothing) Then
J = Matrix.Create(Of Double)(1, 1)
End If
J(0, 0) = (2 - 3 * y(0)) * y(0)
Return J
End Function
End Module