Run-time error 5? - vba

I am trying to define a derivative function for solving a system of differential equations, however when I run the actual macro that calls up this sub routine, i keep getting Run-time error 5: Invalid procedure call or argument. This error occurs in the If statement when x is not greater than 1 and Qv is to be calculated using the equation provided. While stepping into to debug, there are values for all of the variables, but it gives me the error and I have no idea why. Can someone help please?
Sub Derivs(x As Double, y() As Double, dydx() As Double)
Const g As Double = 32.1740485564
Const Hr As Double = 100
Const h0 As Double = 80
Const fm As Double = 0.024
Const L As Double = 1500
Const dp As Double = 2
Const tc As Double = 5
Const k As Double = 25.7
Const Di As Double = 5
Dim u0 As Double
Dim Qv As Double
Dim Qv0 As Double
Dim hstar As Double
u0 = ((g * h0 / ((1 / 2) * fm * (L / dp))) * ((Hr / h0) - 1)) ^ (1 / 2)
Qv0 = (u0 * 3.14 * Di ^ 2) / 4
hstar = h0 - (Qv0 / k) ^ 2
If x >= 1 Then
Qv = 0
Else
Qv = k * (h0 ^ 0.5) * (1 - x) * (y(1) - hstar / h0) ^ 0.5
End If
dydx(0) = ((tc * g * h0) / (L * u0)) * (((Hr / h0) - y(1)) - ((Hr / h0) - 1) * y(0) * Abs(y(0)))
dydx(1) = ((dp / Di) ^ 2) * (u0 * tc / h0) * y(0) - ((4 * Qv * tc) / (3.14 * h0 * Di ^ 2))
End Sub
Well the macro that calls this sub routine is:
Sub RungeKutta()
Dim y(1) As Double
Dim dydx(1) As Double
Dim yout(1) As Double
Dim yerr(1) As Double
Dim x As Double
Dim hdid As Double
Dim yscal(1) As Double
Dim hnext As Double
Dim ystart(1) As Double
Dim NOk As Integer
Dim NBad As Integer
Dim h As Double
Const n As Integer = 2
Dim htry As Double
Const eps As Double = 0.00000001
Dim x1 As Double
Dim x2 As Double
Const nvar As Integer = 2
Dim h1 As Double
Const hmin As Double = 0.001
h = 0.001
x1 = 0
x2 = 10
h1 = 0.01
x = x1
h = Sgn(x2 - x1) * Abs(h1)
NOk = 0
NBad = 0
kount = -1
x = 0
y(0) = 1#
y(1) = 1#
Call Derivs(x, y(), dydx())
Call odeint(ystart(), nvar, x1, x2, eps, h1, hmin, NOk, NBad)
' I have a bunch of coding to input the calculations into a spreadsheet that I am omitting
End Sub
The main program in the macro is:
Sub odeint(ystart() As Double, nvar As Integer, x1 As Double, x2 As Double, eps As Double, h1 As Double, hmin As Double, NOk As Integer, NBad As Integer)
Const MaxStp As Double = 10000
Const Tiny As Double = 10 ^ (-30)
Dim y() As Double
Dim yscal() As Double
Dim dydx() As Double
Dim x As Double
Dim h As Double
Dim hdid As Double
Dim hnext As Double
Const n As Integer = 2
NM1 = n - 1
nvar = 2
ReDim y(NM1)
ReDim dydx(NM1)
ReDim yscal(NM1)
x = x1
h = Sgn(x2 - x1) * Abs(h1)
NOk = 0
NBad = 0
kount = -1
kmax = 500
ReDim xp(kmax)
ReDim yp(NM1, kmax)
dxsav = (x2 - x1) / kmax
For I = 0 To nvar - 1
y(I) = ystart(I)
Next I
If kmax > 0 Then xsav = x - 2 * dxsav
For nstp = 1 To MaxStp
Call Derivs(x, y(), dydx())
For I = 0 To nvar - 1
yscal(I) = Abs(y(I)) + Abs(h * dydx(I)) + Tiny
Next I
If kmax > 0 Then
If Abs(x - xsav) > Abs(dxsav) Then
If kount < kmax - 1 Then
kount = kount + 1
xp(kount) = x
For I = 0 To nvar - 1
yp(I, kount) = y(I)
Next I
xsav = x
End If
End If
End If
If (x + h - x2) * (x + h - x1) > 0 Then h = x2 - x
Call rkqs(y(), dydx(), nvar, x, h, eps, yscal(), hdid, hnext)
If hdid = h Then
NOk = NOk + 1
Else
NBad = NBad + 1
End If
If (x - x2) * (x2 - x1) >= 0 Then
For I = 0 To nvar - 1
ystart(I) = y(I)
Next I
If Not kmax = 0 Then
kount = kount + 1
xp(kount) = x
For I = 0 To nvar - 1
yp(I, kount) = y(I)
Next I
End If
Exit Sub
End If
If Abs(hnext) < hmin Then MsgBox "Stepsize smaller than minimum in odeint!", vbExclamation
h = hnext
Next nstp
MsgBox "Too many steps in odeint", vbExclamation
End Sub
Which calls this sub routine:
Sub rkqs(y() As Double, dydx() As Double, n As Integer, x As Double, htry As Double, eps As Double, yscal() As Double, hdid As Double, hnext As Double)
NM1 = n - 1
Dim ytemp() As Double
Dim yerr() As Double
Dim h As Double
Const Tiny As Double = 10 ^ (-30)
ReDim ytemp(NM1)
ReDim yerr(NM1)
Const Safety As Double = 0.9
Const PGrow As Double = -0.2
Const PShrink As Double = -0.25
Const ErrCon As Double = (5# / Safety) ^ (1# / PGrow)
h = htry
Do
Call rkck(y(), dydx(), n, x, h, ytemp(), yerr())
ErrMax = 0
For I = 0 To NM1
yscal(I) = Abs(y(I)) + Abs(h * dydx(I)) + Tiny
Next I
For I = 0 To n - 1
If Abs(yerr(I) / yscal(I)) > ErrMax Then ErrMax = Abs(yerr(I) / yscal(I))
Next I
ErrMax = ErrMax / eps
If ErrMax > 1 Then
dummy = h
h = Safety * h * (ErrMax ^ PShrink)
If h < 0.1 * dummy Then
h = 0.1 * dummy
End If
xNew = x + h
If xNew = x Then MsgBox "Stepsize underflow in rkqsl", vbExclamation
ContLoop = True
Else
If ErrMax > ErrCon Then
hnext = Safety * h * (ErrMax ^ PGrow)
Else
hnext = 5 * h
End If
hdid = h
x = x + h
For I = 0 To n - 1
y(I) = ytemp(I)
Next I
ContLoop = False
End If
Loop While ContLoop
End Sub
which then calls this subroutine:
Sub rkck(y() As Double, dydx() As Double, n As Integer, x As Double, h As Double, yout() As Double, yerr() As Double)
Dim NM1 As Integer
Dim I As Integer
Dim ak2() As Double
Dim ak3() As Double
Dim ak4() As Double
Dim ak5() As Double
Dim ak6() As Double
Dim ytemp() As Double
NM1 = n - 1
ReDim ak2(NM1)
ReDim ak3(NM1)
ReDim ak4(NM1)
ReDim ak5(NM1)
ReDim ak6(NM1)
ReDim ytemp(NM1)
Const A2 As Double = 1# / 5#
Const A3 As Double = 3# / 10#
Const A4 As Double = 3# / 5#
Const A5 As Double = 1#
Const A6 As Double = 7# / 8#
Const B21 As Double = 1# / 5#
Const B31 As Double = 3# / 40#
Const B32 As Double = 9# / 40#
Const B41 As Double = 3# / 10#
Const B42 As Double = -9# / 10#
Const B43 As Double = 6# / 5#
Const B51 As Double = -11# / 54#
Const B52 As Double = 5# / 2#
Const B53 As Double = -70# / 27#
Const B54 As Double = 35# / 27#
Const B61 As Double = 1631# / 55296#
Const B62 As Double = 175# / 512#
Const B63 As Double = 575# / 13824#
Const B64 As Double = 44275# / 110592#
Const B65 As Double = 253# / 4096#
Const C1 As Double = 37# / 378#
Const C3 As Double = 250# / 621#
Const C4 As Double = 125# / 594#
Const C6 As Double = 512# / 1771#
Const DC1 As Double = C1 - 2825# / 27648#
Const DC3 As Double = C3 - 18575# / 48384#
Const DC4 As Double = C4 - 13525# / 55296#
Const DC5 As Double = -277# / 14336#
Const DC6 As Double = C6 - 1# / 4#
'First Step
For I = 0 To n - 1
ytemp(I) = y(I) + B21 * h * dydx(I)
Next I
'Second Step
Call Derivs(x + A2 * h, ytemp(), ak2())
For I = 0 To n - 1
ytemp(I) = y(I) + h * (B31 * dydx(I) + B32 * ak2(I))
Next I
'Third Step
Call Derivs(x + A3 * h, ytemp(), ak3())
For I = 0 To n - 1
ytemp(I) = y(I) + h * (B41 * dydx(I) + B42 * ak2(I) + B43 * ak3(I))
Next I
'Fourth Step
Call Derivs(x + A4 * h, ytemp(), ak4())
For I = 0 To n - 1
ytemp(I) = y(I) + h * (B51 * dydx(I) + B52 * ak2(I) + B53 * ak3(I) + B54 * ak4(I))
Next I
'Fifth Step
Call Derivs(x + A5 * h, ytemp(), ak5())
For I = 0 To n - 1
ytemp(I) = y(I) + h * (B61 * dydx(I) + B62 * ak2(I) + B63 * ak3(I) + B64 * ak4(I) + B65 * ak5(I))
Next I
'Sixth Step
Call Derivs(x + A6 * h, ytemp(), ak6())
For I = 0 To n - 1
yout(I) = y(I) + h * (C1 * dydx(I) + C3 * k3(I) + C4 * ak4(I) + C6 * ak6(I))
Next I
For I = 0 To n - 1
yerr(I) = h * (DC1 * dydx(I) + DC3 * ak3(I) + DC4 * ak4(I) + DC5 * ak5(I) + DC6 * ak6(I))
Next I
End Sub
It's the Runge Kutta method.
So I debugged each of the three programs separately starting with RKCK, then going into RKQS and then to ODEINT by essentially writing test macros for each that included all the parameters, outputted the calculated values accosted with each program in a message box, and called the following example set of equations:
Sub Derivs1(x As Double, y() As Double, dydx() As Double)
dydx(0) = -2 * x * y(0)
dydx(1) = -3 * y(1) * x ^ 2
End Sub
Each program worked perfectly for this example so I decided to test each test macro with the actual problem statement equations. RKCK worked fine, so did RKQS. Then when I got to the ODEINT, the error message popped up.

Run time error 5 is an "Invalid Procedure call" error.
I can't see how that line could produce an error as long the y array has a value at index 1.
You need to give an example of calling this function similar to the following which runs without any error.
Sub test()
Dim dydx(0 To 1) As Double
Dim y(0 To 1) As Double
dydx(0) = 1
dydx(1) = 2
y(0) = 1
y(1) = 2
Derivs 0.5, y, dydx
End Sub
I've run your edited code and when the error occurs in
Qv = k * (h0 ^ 0.5) * (1 - x) * (y(1) - hstar / h0) ^ 0.5
your variable values are:
y(1) = 0
hstar = 38.3
h0 = 80
This means:
(y(1) - hstar / h0) = -0.478857734838603
As Jean-François Corbett mentioned, the square root of a -ve number isn't supported by vba and results in the run-time error 5.

You're probably taking the square root of a negative.
x ^ 0.5 will give you an "Invalid procedure call or argument" error when x is negative.
Try stepping through your code in debug mode to confirm this.

Related

type mismatch error in VBA, where is the mistake

Hey I have no idea why I get an error "run time error 13 type mismatch". Thats my code and the place where I get an error:
EDIT: That is my code:
Function payoff(S_T, K, CallPut As String)
If CallPut = "call" Then
omega = 1
Else: omega = -1
End If
payoff = WorksheetFunction.Max(omega * (S_T - K), 0)
End Function
Function BS_trajektoria(S_0 As Double, T As Double, r As Double, q As Double, sigma As Double, N As Long) As Double()
Randomize
Dim S() As Double
Dim delta_t As Double
Dim i As Long
ReDim S(N)
S(0) = S_0
delta_t = T / N
For i = 1 To N
S(i) = S(i - 1) * Exp((r - q - 0.5 * sigma ^ 2) * delta_t + sigma * delta_t ^ 0.5 * Application.NormSInv(Rnd))
Next i
BS_trajektoria = S
End Function
Function barrier_MC(S_0 As Double, K As Double, T As Double, r As Double, q As Double, sigma As Double, _
B As Double, N As Long, num_of_sim As Long, CallPut As String, BarType As String) As Double
Randomize
Dim max_value As Double
Dim suma_wyplat As Double
Dim wyplata As Double
Dim i As Long
Dim S() As Double
suma_wyplat = 0
If (BarType = "DO" Or BarType = "DI") And B > S_0 Then
MsgBox "Too high barrier!"
Exit Function
ElseIf (BarType = "UO" Or BarType = "UI") And B < S_0 Then
MsgBox "Too low barrier!"
Exit Function
End If
With WorksheetFunction
For i = 1 To num_of_sim
S = BS_trajektoria(S_0, T, r, q, sigma, N)
max_value = .Max(S)
If max_value >= B Then
wyplata = 0
Else
wyplata = payoff(S(N), K, CallPut)
End If
suma_wyplat = suma_wyplat + wyplata
Next i
End With
barrier_MC = Exp(-r * T) * suma_wyplat / num_of_sim
End Function
Sub test3()
MsgBox barrier_MC(100, 100, 1, 0.05, 0.02, 0.2, 120, 1000, 1000000, "call", "UO")
End Sub
Anyone know where is the problem? For smaller value of N and num_of_sim everything works fine, the problem is when I use bigger values for these variables.
If you declare a new Double variable called rand and modify the main loop so that it looks like:
For i = 1 To N
rand = Rnd
S(i) = S(i - 1) * Exp((r - q - 0.5 * sigma ^ 2) * delta_t + sigma * delta_t ^ 0.5 * Application.NormSInv(rand))
Next i
you will see that the problem always happens when rand = 0. Why it throws that particular error is a bit of a mystery, but it is what it is. As a fix, what you could do is to keep the code as modified above with the following twist:
For i = 1 To N
rand = Rnd
If rand = 0 Then rand = 0.0000001
S(i) = S(i - 1) * Exp((r - q - 0.5 * sigma ^ 2) * delta_t + sigma * delta_t ^ 0.5 * Application.NormSInv(rand))
Next i
Then the code will run without error. It is still somewhat slow, but optimizing it (if possible) would be for a different question.

How is a local variable in another function affecting a variable in my main function?

So I have a "main" function (SolveSixODES) that calls a secondary function (AllODEs). And when it does this, the x value in the main function gets modified. I don't understand how this can be possible, seeing as it is not a global variable.
Here is the code, my inputs I used are as follows:
x=0, xmax=3, y=0-6, h=0.1, error=0.1
Public Function SolveSixODE(x As Double, xmax As Double, Y As Range, h As Double, error As Double) 'Weird bug: You must leave the first y4 value blank
Dim i As Integer, k(7, 7) As Double, j As Integer, m As Integer 'k(Order #, equation #)
Dim Y5(7) As Double, Y4(7) As Double, Y4Old(7) As Double
Dim delta0(7) As Double, delta1(7) As Double, delRatio(7) As Double, Rmin As Double
For i = 1 To 6 'Moving the input data so it can acutally be used
Y4(i) = Y(i)
Next i
While x < xmax
If x + h < xmax Then
x = x + h
Else
h = xmax - x
x = xmax
End If
For j = 1 To 6 'j is the order i is equation number
For i = 1 To 6 'Calculating all of the k(1) values for eq 1 to 6
k(j, i) = AllODES(x, Y4, i, j, k, h) '!!!!!SOME HOW THIS LOOP MAKES X negative...!!!!!!!
Next i
Next j
For i = 1 To 6
Y4Old(i) = Y4(i) 'Saving old y4 value to calc delta0
Y4(i) = Y4(i) + h * (k(1, i) * (37 / 378) + k(3, i) * (250 / 621) + k(4, i) * (125 / 594) + k(6, i) * (512 / 1771))
Y5(i) = Y4(i) + h * (k(1, i) * (2825 / 27648) + k(3, i) * (18575 / 48384) + k(4, i) * (13525 / 55296) + k(5, i) * (277 / 14336) + k(6, i) * (0.25))
delta0(i) = error * (Abs(Y4Old(i)) + Abs(h * AllODES(x, Y4Old, i, 1, k, h))) 'First order because we don't want to use the k vals
delta1(i) = Abs(Y5(i) - Y4(i))
delRatio(i) = Abs(delta0(i) / delta1(i)) 'Ratio of errors
Next i
Rmin = delRatio(1)
For i = 2 To 6
If delRatio(i) < Rmin Then
Rmin = delRatio(i) 'Determine the smallest error ratio
End If
Next i
If Rmin < 1 Then 'If this is true then the step size was too big must repeat step
x = x - h 'Set x and y's back to previous values
For i = 1 To 6
Y4(i) = Y4Old(i)
Next i
h = 0.9 * h * Rmin ^ 0.25 'adjust h value; 0.9 is a safety factor
Else
h = 0.9 * h * Rmin ^ 0.2 'Otherwise, we march on
End If
m = m + 1
Wend
SolveSixODE = Y4
End Function
Public Function AllODES(x As Double, Y() As Double, EqNumber As Integer, order As Integer, k() As Double, h As Double) As Double
Dim conc(7) As Double, i As Integer, j As Integer
If order = 1 Then
x = x - h
For i = 1 To 6 'Movin the data so I can use it
conc(i) = Y(i) 'also adjusting the x and y values for RK4 (Cash Karp values)
Next i
ElseIf order = 2 Then
x = x - h + h * 0.2
For i = 1 To 6
conc(i) = Y(i) + h * k(1, i) * 0.2
Next i
ElseIf order = 3 Then
x = x - h + 0.3 * h
For i = 1 To 6
conc(i) = Y(i) + h * (0.075 * k(1, i) + 0.225 * k(2, i))
Next i
ElseIf order = 4 Then
x = x - h + 0.6 * h
For i = 1 To 6
conc(i) = Y(i) + h * (0.3 * k(1, i) - 0.9 * k(2, i) + 1.2 * k(3, i))
Next i
ElseIf order = 5 Then
x = x - h + h
For i = 1 To 6
conc(i) = Y(i) + h * ((-11 / 54) * k(1, i) + 2.5 * k(2, i) - (70 / 27) * k(3, i) + (35 / 27) * k(4, i))
Next i
ElseIf order = 6 Then
x = x - h + 0.875 * h
For i = 1 To 6
conc(i) = Y(i) + h * ((1631 / 55296) * k(1, i) + (175 / 512) * k(2, i) + (575 / 13824) * k(3, i) + (44275 / (110592) * k(4, i) + (253 / 4096) * k(5, i)))
Next i
Else
MsgBox ("error")
End If
If EqNumber = 1 Then 'These are the actual equations
AllODES = x + Y(1)
ElseIf EqNumber = 2 Then
AllODES = x
ElseIf EqNumber = 3 Then
AllODES = Y(3)
ElseIf EqNumber = 4 Then
AllODES = 2 * x
ElseIf EqNumber = 5 Then
AllODES = 2 * Y(2)
ElseIf EqNumber = 6 Then
AllODES = 3 * x
Else
MsgBox ("You entered an Eq Number that was dumb")
End If
End Function
It's possible that it is something really trivial that I missed but this seems to contradict my knowledge of how variables work. So if you understand how the function is able to manipulate a variable from another function in this case, I would appreciate any advice and/or explanation!
Thanks in advance!
the x value in the main function gets modified. I don't understand how this can be possible, seeing as it is not a global variable
This is normal because you are passing x by reference to the function AllODES and you do change it there. When the keyword ByVal is not explicitly specified in the function/sub prototype, the default passing mechanism is ByRef, that is, by reference.
Public Function AllODES(x As Double, ...
means
Public Function AllODES(ByRef x As Double, ....
We observe that x is manipulated in this function, so the change will appear in the caller. If you want that the change of x does not report back in the caller's scope, pass x by value:
Public Function AllODES(ByVal x As Double, ....
' ^^^^^
Only in this case the x of the caller and the x of the callee will be two different variables.

#VALUE error when trying to output value to a cell excel VBA

I have written a macro which computes x and y values. I am having trouble trying to write those values to cells on Excel.
I get #VALUE error when I try to display the values on the cell.
I have added my code below. Any suggestion about what is wrong with the code will be really helpful and appreciated?
Thanks in advance!
'Compute Points
Function ComputePoints(x1, y1, x2, y2, distance) As Double
'Calculate slope m
Dim m As Double
m = (y2 - y1) / (x2 - x1)
'Calculate intercept
Dim Intercept As Double
Intercept = y1 - m * x1
'Calculate x for distFinal
Dim message As String
Dim a As Double
Dim b As Double
Dim c As Double
Dim root1 As Double
Dim root2 As Double
Dim det As Double
Dim det1 As Double
Dim wb As Workbook
Dim ws As Worksheet
Dim x1Rng As Range
Dim x2Rng As Range
Dim yRng As Range
a = (m ^ 2 + 1)
b = 2 * (Intercept * m - m * y2 - x2)
c = x2 ^ 2 + (Intercept - y2) ^ 2 - distance ^ 2
det = ((b ^ 2) - (4 * a * c))
det1 = Sqr(det)
message = "There is no solution to your equation"
If det < 0 Then
MsgBox message, vbOKOnly, "Error"
Else
root1 = Round((-b + det1) / (2 * a), 2)
root2 = Round((-b - det1) / (2 * a), 2)
End If
'Compute y
Dim y As Double
y = m * root2 + Intercept
' Trying to set cell values to root1, root2, y
Set wb = ActiveWorkbook
Set ws = wb.Sheets("Sheet9")
Set x1Rng = ws.Range("N2")
Set x2Rng = ws.Range("O2")
Set yRng = ws.Range("P2")
x1Rng.Value2 = root1
x2Rng.Value2 = root2
yRng.Value2 = y
ComputePoints = y
End Function
I modified your code slightly to get values directly in excel cells. You need to select 3 horizontal cells, press F2 / =, enter your formula and then press Ctrl Shift Enter to make it an array formula.
This will give you the three output values in the cells.
Function ComputePoints(x1, y1, x2, y2, distance)
Dim results(3) As Variant ' #nightcrawler23
'Calculate slope m
Dim m As Double
m = (y2 - y1) / (x2 - x1)
'Calculate intercept
Dim Intercept As Double
Intercept = y1 - m * x1
'Calculate x for distFinal
Dim message As String
Dim a As Double
Dim b As Double
Dim c As Double
Dim root1 As Double
Dim root2 As Double
Dim det As Double
Dim det1 As Double
a = (m ^ 2 + 1)
b = 2 * (Intercept * m - m * y2 - x2)
c = x2 ^ 2 + (Intercept - y2) ^ 2 - distance ^ 2
det = ((b ^ 2) - (4 * a * c))
det1 = Sqr(det)
message = "There is no solution to your equation"
If det < 0 Then
MsgBox message, vbOKOnly, "Error"
Else
root1 = Round((-b + det1) / (2 * a), 2)
root2 = Round((-b - det1) / (2 * a), 2)
End If
'Compute y
Dim y As Double
y = m * root2 + Intercept
results(1) = root1 ' #nightcrawler23
results(2) = root2 ' #nightcrawler23
results(3) = y ' #nightcrawler23
ComputePoints = results ' #nightcrawler23
End Function
You need to add some code to output error message, when no roots are found

VBA root finding trough bisection

My vba code keeps returning a value of 0 when I know the roots of my function are not 0.
It's pretty simple code but I can't seem to debug it. Any idea where this error might be coming from??
Option Explicit
Public Function Bisect(ByVal xlow As Double, ByVal xhigh As Double) As Double
Dim i As Integer
Dim xmid As Double
xmid = (xlow + xhigh) / 2
For i = 1 To 100
If f(xlow) * f(xmid) < 0 Then
xhigh = xmid
xmid = (xlow + xhigh) / 2
Else
xlow = xmid
xmid = (xlow + xhigh) / 2
End If
Next i
Bisect = xmid
End Function
Function f(ByVal x As Double, Optional ByRef inputArray As Range) As Variant
Dim ca0 As Double
Dim v0 As Double
Dim k As Double
Dim e As Double
Dim ac As Double
Dim L As Double
inputArray(2, 2) = ca0
inputArray(3, 2) = v0
inputArray(4, 2) = k
inputArray(5, 2) = e
inputArray(6, 2) = ac
inputArray(7, 2) = L
f(x) = (v0 / (k * ca0 * ac)) * ((2 * e * (1 + e) * Log(1 - x)) + (e ^ 2 * x) + (((1 + e) ^ 2 * x) / (1 - x))) - L
End Function
' i Think you want to take those constant values from cells presentin the sheet
Function f(ByVal x As Double) As Variant
Dim inputArray As Range
Dim ca0 As Double
Dim v0 As Double
Dim k As Double
Dim e As Double
Dim ac As Double
Dim L As Double
' i Think you want to take values from cells in the sheet
ca0 = ActiveSheet.Cells(2, 2).Value
v0 = ActiveSheet.Cells(3, 2).Value
k = ActiveSheet.Cells(4, 2).Value
e = ActiveSheet.Cells(5, 2).Value
ac = ActiveSheet.Cells(6, 2).Value
L = ActiveSheet.Cells(7, 2).Value
Could it be that you try to assign the inputarray with empty variables?
In my mind it should be:
ca0 = inputArray(2, 2)
v0 = inputArray(3, 2)
And so on.
I'm guessing
f(x) = (v0 / (k * ca0 * ac)) * ((2 * e * (1 + e) * Log(1 - x)) + (e ^ 2 * x) + (((1 + e) ^ 2 * x) / (1 - x))) - L
Should be
f = (v0 / (k * ca0 * ac)) * ((2 * e * (1 + e) * Log(1 - x)) + (e ^ 2 * x) + (((1 + e) ^ 2 * x) / (1 - x))) - L

Subscript out of range in Runge Kutta method

I am programming the Runge Kutta method with adaptive step size in VBA and I have encountered an Error 9 "Subscript out of range". Can someone please help me figure out why and how to fix it?
I am attaching the three separate sub routines I was required to write as well as the beginning of the macro that runs all three programs.
Sub rkck(y() As Double, dydx() As Double, n As Integer, x As Double, h As Double, yout() As Double, yerr() As Double)
Dim NM1 As Integer
Dim I As Integer
Dim ak2() As Double
Dim ak3() As Double
Dim ak4() As Double
Dim ak5() As Double
Dim ak6() As Double
Dim ytemp() As Double
NM1 = n - 1
ReDim ak2(NM1)
ReDim ak3(NM1)
ReDim ak4(NM1)
ReDim ak5(NM1)
ReDim ak6(NM1)
ReDim ytemp(NM1)
Const A2 As Double = 1# / 5#
Const A3 As Double = 3# / 10#
Const A4 As Double = 3# / 5#
Const A5 As Double = 1#
Const A6 As Double = 7# / 8#
Const B21 As Double = 1# / 5#
Const B31 As Double = 3# / 40#
Const B32 As Double = 9# / 40#
Const B41 As Double = 3# / 10#
Const B42 As Double = -9# / 10#
Const B43 As Double = 6# / 5#
Const B51 As Double = -11# / 54#
Const B52 As Double = 5# / 2#
Const B53 As Double = -70# / 27#
Const B54 As Double = 35# / 27#
Const B61 As Double = 1631# / 55296#
Const B62 As Double = 175# / 512#
Const B63 As Double = 575# / 13824#
Const B64 As Double = 44275# / 110592#
Const B65 As Double = 253# / 4096#
Const C1 As Double = 37# / 378#
Const C3 As Double = 250# / 621#
Const C4 As Double = 125# / 594#
Const C6 As Double = 512# / 1771#
Const DC1 As Double = C1 - 2825# / 27648#
Const DC3 As Double = C3 - 18575# / 48384#
Const DC4 As Double = C4 - 13525# / 55296#
Const DC5 As Double = -277# / 14336#
Const DC6 As Double = C6 - 1# / 4#
'First Step
For I = 0 To n - 1
ytemp(I) = y(I) + B21 * h * dydx(I)
Next I
'Second Step
Call Derivs(x + A2 * h, ytemp(), ak2())
For I = 0 To n - 1
ytemp(I) = y(I) + h * (B31 * dydx(I) + B32 * ak2(I))
Next I
'Third Step
Call Derivs(x + A3 * h, ytemp(), ak3())
For I = 0 To n - 1
ytemp(I) = y(I) + h * (B41 * dydx(I) + B42 * ak2(I) + B43 * ak3(I))
Next I
'Fourth Step
Call Derivs(x + A4 * h, ytemp(), ak4())
For I = 0 To n - 1
ytemp(I) = y(I) + h * (B51 * dydx(I) + B52 * ak2(I) + B53 * ak3(I) + B54 * ak4(I))
Next I
'Fifth Step
Call Derivs(x + A5 * h, ytemp(), ak5())
For I = 0 To n - 1
ytemp(I) = y(I) + h * (B61 * dydx(I) + B62 * ak2(I) + B63 * ak3(I) + B64 * ak4(I) + B65 * ak5(I))
Next I
'Sixth Step
Call Derivs(x + A6 * h, ytemp(), ak6())
For I = 0 To n - 1
yout(I) = y(I) + h * (C1 * dydx(I) + C3 * ak3(I) + C4 * ak4(I) + C6 * ak6(I))
Next I
For I = 0 To n - 1
yerr(I) = h * (DC1 * dydx(I) + DC3 * ak3(I) + DC4 * ak4(I) + DC5 * ak5(I) + DC6 * ak6(I))
Next I
End Sub
Sub rkqs(y() As Double, dydx() As Double, n As Integer, x As Double, htry As Double, eps As Double, yscal() As Double, hdid As Integer, hnext As Integer)
Const Safety As Double = 0.9
Const PGrow As Double = -0.2
Const PShrink As Double = -0.25
Const ErrCon As Double = (5# / Safety) ^ (1# / PGrow)
h = htry
Do
Call rkck(y(), dydx(), n, x, h, ytemp(), yerr())
ErrMax = 0
For I = 0 To n - 1
If Abs(yerr(I) / yscal(I)) > ErrMax Then ErrMax = Abs(yerr(I) / yscal(I))
Next I
ErrMax = ErrMax / eps
If ErrMax > 1 Then
dummy = h
h = Safety * h * (ErrMax ^ PShrink)
If h < 0.1 * dummy Then
h = 0.1 * dummy
End If
xNew = x + h
If xNew = x Then MsgBox "Stepsize underflow in rkqsl", vbExclamation
ContLoop = True
Else
If ErrMax > ErrCon Then
hnext = Safety * h * (ErrMax ^ PGrow)
Else
hnext = 5 * h
End If
hdid = h
x = x + h
For I = 0 To n - 1
y(I) = ytemp(I)
Next I
ContLoop = False
End If
Loop While ContLoop
End Sub
Sub odeint(ystart() As Double, nvar As Integer, x1 As Double, x2 As Double, eps As Double, h1 As Double, hmin As Double, NOk As Double, NBad As Double)
Const MaxStp As Double = 10000
Const Tiny As Double = 10 ^ (-30)
x = x1
h = Sgn(x2 - x1) * Abs(h1)
NOk = 0
NBad = 0
kount = -1
For I = 0 To nvar - 1
y(I) = ystart(I)
Next I
If kmax > 0 Then xsav = x - 2 * dxsav
For nstp = 1 To MaxStp
Call Derivs(x, y(), dydx())
For I = 0 To nvar - 1
yscal(I) = Abs(y(I)) + Abs(h * dydx(I)) + Tiny
Next I
If kmax > 0 Then
If Abs(x - xsav) > Abs(dxsav) Then
If kount < kmax - 1 Then
kount = kount + 1
xp(kount) = x
For I = 0 To nvar - 1
yp(I, kount) = y(I)
Next I
xsav = x
End If
End If
End If
If (x + h - x2) * (x + h - x1) > 0 Then h = x2 - x
Call rkqs(y(), dydx(), nvar, x, h, eps, yscal(), hdid, hnext)
If hdid = h Then
NOk = NOk + 1
Else
NBad = NBad + 1
End If
If (x - x2) * (x2 - x1) >= 0 Then
For I = 0 To nvar - 1
ystart(I) = y(I)
Next I
If Not kmax = 0 Then
kount = kount + 1
xp(kount) = x
For I = 0 To nvar - 1
yp(I, kount) = y(I)
Next I
End If
Exit Sub
End If
If Abs(hnext) < hmin Then MsgBox "Stepsize smaller than minimum in odeint!", vbExclamation
h = hnext
Next nstp
MsgBox "Too many steps in odeint", vbExclamation
End Sub
Sub Derivs(x As Double, y() As Double, dydx() As Double)
Const g As Double = 32.1740485564
Const Hr As Double = 100
Const h0 As Double = 80
Const fm As Double = 0.024
Const L As Double = 1500
Const dp As Double = 2
Const tc As Double = 5
Const k As Double = 25.7
Const Di As Double = 5
Dim u0 As Double
Dim Qv As Double
Dim Qv0 As Double
Dim hstar As Double
u0 = ((g * h0 / ((1 / 2) * fm * (L / dp))) * ((Hr / h0) - 1)) ^ (1 / 2)
Qv0 = (u0 * Pi() * Di ^ 2) / 4
hstar = h0 - (Qv0 / k) ^ 2
If x >= 1 Then Qv = 0
Else
Qv = k * (h0 ^ 0.5) * (1 - x) * (y(1) - hstar / h0) ^ 0.5
End If
dydx(0) = ((tc * g * h0) / (L * u0)) * (((Hr / h0) - y(1)) - ((Hr / h0) - 1) * y(0) * Abs(y(0)))
dydx(1) = ((dp / Di) ^ 2) * (u0 * tc / h0) * y(0) - ((4 * Qv * tc) / (Pi() * h0 * Di ^ 2))
End Sub
Sub RungeKutta()
Dim y() As Double
Dim dydx() As Double
Dim yout() As Double
Dim yerr() As Double
Dim yscal() As Double
Dim hdid As Integer
Dim hnext As Integer
Dim ystart() As Double
Dim hmin As Double
Dim NOk As Double
Dim NBad As Double
Call rkck(y(), dydx(), 2, 0, 2, yout(), yerr())
Call rkqs(y(), dydx(), 2, 0, 2, 0.000001, yscal(), hdid, hnext)
Call odeint(ystart(), 2, 0, 100, 0.000001, 2, hmin, NOk, NBad)
One of the places where the error appears, is with Y(I) in:
'First Step
For I = 0 To n - 1
ytemp(I) = y(I) + B21 * h * dydx(I)
Next I
Because Y() is not initialized with data. I assume you want to provide some values corresponding to x values.
I just noticed that VBA does not have a Pi() function (called from your Derivs() function). You need to specify somewhere
Public Const PI As Double = 3.14159265358979
and use that instead of Pi().
Also put Option Base 0 on the top of the code.
With those changes, and adding a declaration for h, ytemp(), yerr() in rkqs() I got it to run with these values:
Sub RungeKutta()
Dim y() As Double
Dim dydx() As Double
Dim yout() As Double
Dim yerr() As Double
Dim yscal() As Double
Dim hdid As Integer
Dim hnext As Integer
Dim ystart() As Double
Dim hmin As Double
Dim NOk As Double
Dim NBad As Double
ReDim y(2), yout(2), yerr(2), dydx(2)
y(0) = 0: y(1) = 100
Call Derivs(0, y, dydx)
Call rkck(y(), dydx(), 2, 0, 2, yout(), yerr())
...