Conditional ELSEIF does not work - vba

Not sure how to solve this. "a" was meant to start from 1 to NumData, but I have deliberately change the start from 44200 to check the ELSEIF. For NumData = 117,350,
I would expect the 3rd ELSEIF to be activated. Instead throughout the whole run, it only step-into the first ELSEIF even though the "a" value does not meet the conditions.
What should I do?
For a = 44200 To NumData 'Int1
If a > 1 Then
If UCase(Trim(Range1(a, 3))) = UCase(Trim(Range1(a - 1, 3))) Then
GoTo Line1 'Next count loop if next Platform name the same
End If
End If
For b = 1 To NumData
lat1 = Range1(a, 5)
lat2 = Range1(b, 5)
long1 = Range1(a, 6)
long2 = Range1(b, 6)
CompRad = Dist(lat1, lat2, long1, long2)
If (CompRad <= Radius And CompRad >= 0) Then
z = CLng(NumData / 8)
If a <= CLng(NumData / 8) Then
For c = 1 To 6
Range2(d, c) = Range1(b, c)
Next c
Acc_Sum2 = Acc_Sum2 + Range2(d, 4)
d = d + 1
ElseIf CLng(NumData / 8) < a <= 2 * CLng(NumData / 8) Then
z = 2 * CLng(NumData / 8)
For c = 1 To 6
Range3(e, c) = Range1(b, c)
Next c
Acc_Sum3 = Acc_Sum3 + Range3(e, 4)
e = e + 1
ElseIf 2 * CLng(NumData / 8) < a <= 3 * CLng(NumData / 8) Then
For c = 1 To 6
Range4(f, c) = Range1(b, c)
Next c
Acc_Sum4 = Acc_Sum4 + Range4(f, 4)
f = f + 1
ElseIf 3 * CLng(NumData / 8) < a <= 4 * CLng(NumData / 8) Then
z = 3 * CLng(NumData / 8)
For c = 1 To 6
Range5(g, c) = Range1(b, c)
Next c
Acc_Sum5 = Acc_Sum5 + Range5(g, 4)
g = g + 1
ElseIf 4 * CLng(NumData / 8) < a <= 5 * CLng(NumData / 8) Then
For c = 1 To 6
Range6(h, c) = Range1(b, c)
Next c
Acc_Sum6 = Acc_Sum6 + Range6(h, 4)
h = h + 1
ElseIf 5 * CLng(NumData / 8) < a <= 6 * CLng(NumData / 8) Then
For c = 1 To 6
Range7(i, c) = Range1(b, c)
Next c
Acc_Sum7 = Acc_Sum7 + Range7(i, 4)
i = i + 1
ElseIf 6 * CLng(NumData / 8) < a <= 7 * CLng(NumData / 8) Then
For c = 1 To 6
Range8(j, c) = Range1(b, c)
Next c
Acc_Sum8 = Acc_Sum8 + Range8(j, 4)
j = j + 1
ElseIf 7 * CLng(NumData / 8) < a <= NumData Then
For c = 1 To 6
Range9(k, c) = Range1(b, c)
Next c
Acc_Sum9 = Acc_Sum9 + Range9(k, 4)
k = k + 1
End If
End If
Next b
Line1:
Next a

Your conditions like:
1 < a <= 10
are always true. First part (1 < a) evaluates to True or False and then it is converted to integer (True = 1, False = 0). Both values are <=10.
You should change thes conditions to:
(1 < a) And (a <= 10)
Brackets are optional, comparison operators have higher precedence.

user3964075 nailed the core issue. I think making the code a little bit more readable will help to filter out other possible trouble areas. Why do you set the value of 'z' and then not use it?
z = CLng(NumData / 8)
Where are the variables d, e, f...; Range2, Range3, Range4...; Acc_Sum2, Acc_Sum3... assigned, and what are they doing? Can each group be replaced by a single variable?
You may also want to create a simple Between function
to clean up a lot of your conditional statements.
Public Function Between(x As Integer, min As Integer, max As Integer) As Boolean
Between = x <= max And x >= min
End Function

Thanks, user3964075 & Carl for your prompt response. It's worked! Carl, the z variable was slotted in when I was trying to figure out the problem. I thought it would be too much to post the whole code. It does require a lot of cleaning up. Appreciate your tips.

Related

Recursive function structure in VBA

I'm trying to write a recursive function so that it calculates the sum of the products of the combinations of values in a dynamic array. Right now I've been trying to make it work for a simpler case, but I really don't quite understand the structure I should follow for a recursive function. In this case there's supposed to be the sum of 28 two factor products, resulting 1.4
Sub SuPC()
Dim k As Long
Dim s As Long
Dim i As Long
Dim j As Long
k = 8
s = 2
HSum i, j, s, k
End Sub
Function HSum(i As Long, j As Long, s, k) As Double
Dim P As Variant
Dim z() As Double
Dim Tot As Double
ReDim z(0 To (k * s) - 1)
P = Array(1 / 2, 1 / 3, 1 / 4, 1 / 5, 1 / 6, 1 / 7, 1 / 8, 1 / 9)
If i <= k Then
HSum i + 1, j, s, k
If j <= s Then
HSum i, j + 1, s, k
If z(i) = 0 Then z(i) = 1
z(i) = P(j) * z(i)
End If
Tot = z(i) + Tot
End If
Range("J11") = Tot
End Function
If s and k were low fixated values, I could use For loops but the point is for them to be variable.
You should try to use tail recursion as this is just a sum of the products,
see here an example for tail recursion factoring.
Public Function fact_tail(n As Double) As Double
'Tail Recursion
'fact 4 = 4 * fact 3
' = 4* 3 * fact 2
' = 4* 3 * 2 * fact 1
' = 4* 3 * 2 * 1
'fact 4 = go(4, 1)
' = go((n - 1), (a * n))
' = go((4-1),(1*4))
' = go(3, 4)
' = go(3-1, 3*4)
' = go(2, 12)
' = go(2-1, 12*2)
' = go(1, 24)
' = 4* 3 * 2 * 1 = 24
fact_tail = go_fact(n, 1)
End Function
Private Function go_fact(n, a)
If n <= 1 Then
go_fact = a
Else
go_fact = go_fact((n - 1), (a * n))
End If
End Function

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.

Formula error after file save from network to local

I have issue with an Excel worksheet that contains the formula:
=Spline($D$9:$D$34,$J$9:$J$34,$D43)
Sheet works fine until I open this sheet on network drive and save it on local drive. Then this formula throws #NAME? error. Strange is, that error is gone when I click on line with formula to edit it and press enter (nothing changes in formula).
Have someone met similar issue?
I just found another information. Formula spline is defined in VBA module, not internal in Excel. It looks like. But issue is still here.
Function spline(periodcol As Range, ratecol As Range, x As Range)
Dim period_count As Integer
Dim rate_count As Integer
period_count = periodcol.Rows.Count
rate_count = ratecol.Rows.Count
If period_count <> rate_count Then
spline = "Error: Range count does not match"
GoTo endnow
End If
ReDim xin(period_count) As Single
ReDim yin(period_count) As Single
Dim c As Integer
For c = 1 To period_count
xin(c) = periodcol(c)
yin(c) = ratecol(c)
Next c
Dim n As Integer
Dim i, k As Integer
Dim p, qn, sig, un As Single
ReDim u(period_count - 1) As Single
ReDim yt(period_count) As Single
n = period_count
yt(1) = 0
u(1) = 0
For i = 2 To n - 1
sig = (xin(i) - xin(i - 1)) / (xin(i + 1) - xin(i - 1))
p = sig * yt(i - 1) + 2
yt(i) = (sig - 1) / p
u(i) = (yin(i + 1) - yin(i)) / (xin(i + 1) - xin(i)) - (yin(i) - yin(i - 1)) / (xin(i) - xin(i - 1))
u(i) = (6 * u(i) / (xin(i + 1) - xin(i - 1)) - sig * u(i - 1)) / p
Next i
qn = 0
un = 0
yt(n) = (un - qn * u(n - 1)) / (qn * yt(n - 1) + 1)
For k = n - 1 To 1 Step -1
yt(k) = yt(k) * yt(k + 1) + u(k)
Next k
Dim klo, khi As Integer
Dim h, b, a As Single
klo = 1
khi = n
Do
k = khi - klo
If xin(k) > x Then
khi = k
Else
klo = k
End If
k = khi - klo
Loop While k > 1
h = xin(khi) - xin(klo)
a = (xin(khi) - x) / h
b = (x - xin(klo)) / h
y = a * yin(klo) + b * yin(khi) + ((a ^ 3 - a) * yt(klo) + (b ^ 3 - b) * yt(khi)) * (h ^ 2) / 6
spline = y
endnow:
End Function
Try to add:
Application.Volatile
to your VBA code. Add this just below the Function statement to force a renewed calculation as soon as anything changes.

The results of my functions when I call a spline function gives wrong values

I have a function that only call the spline function when something happens..in this case when a division is less than zero..the inputs for the function is the same that for the spline function(called CUBIC), the spline was tested and works well when I call it direct! someone can help me?...follows a party of the code
Function NDF6(T As Variant, dias As Variant, taxas As Variant)
If T <= dias(1) Then
NDF6 = taxas(1)
Exit Function
End If
If T >= dias(tam) Then
NDF6 = taxas(tam)
Exit Function
End If
For i = 1 To tam
If T <= dias(i) Then
If taxas(i) / taxas(i - 1) < 0 Then
Call CUBIC(T, dias, taxas)
Else
i0 = ((taxas(i - 1) * dias(i - 1)) / 360) + 1
i1 = ((taxas(i - 1) * dias(i - 1)) / 360) + 1
irel = i1 / i0
i2 = irel ^ ((T - dias(i - 1)) / (dias(i) - dias(i - 1)))
i2rel = i2 * i0
i2real = i2rel - 1
NDF6 = i2real * (360 / T)
End If
Public Function CUBIC(x As Variant, input_column As Variant, output_column As Variant)
The function returns a zero value when I call the cubic function. The inputs are a cell with a value with a value equivalent a day and two arrays(DUONOFF and ONOFF) equivalent a days and rates, I call the function like:
NDF6(512,DUONOFF,ONOFF)
follows the CUBIC function
Public Function CUBIC(x As Variant, input_column As Variant, output_column As Variant)
'Purpose: Given a data set consisting of a list of x values
' and y values, this function will smoothly interpolate
' a resulting output (y) value from a given input (x) value
' This counts how many points are in "input" and "output" set of data
Dim input_count As Integer
Dim output_count As Integer
input_count = input_column.Rows.Count
output_count = output_column.Rows.Count
Next check to be sure that "input" # points = "output" # points
If input_count <> output_count Then
CUBIC = "Something's messed up! The number of indeces number of output_columnues don't match!"
GoTo out
End If
ReDim xin(input_count) As Single
ReDim yin(input_count) As Single
Dim c As Integer
For c = 1 To input_count
xin(c) = input_column(c)
yin(c) = output_column(c)
Next c
values are populated
Dim N As Integer 'n=input_count
Dim i, k As Integer 'these are loop counting integers
Dim p, qn, sig, un As Single
ReDim u(input_count - 1) As Single
ReDim yt(input_count) As Single 'these are the 2nd deriv values
N = input_count
yt(1) = 0
u(1) = 0
For i = 2 To N - 1
sig = (xin(i) - xin(i - 1)) / (xin(i + 1) - xin(i - 1))
p = sig * yt(i - 1) + 2
yt(i) = (sig - 1) / p
u(i) = (yin(i + 1) - yin(i)) / (xin(i + 1) - xin(i)) - (yin(i) - yin(i - 1)) / (xin(i) - xin(i - _1))
u(i) = (6 * u(i) / (xin(i + 1) - xin(i - 1)) - sig * u(i - 1)) / p
Next i
qn = 0
un = 0
yt(N) = (un - qn * u(N - 1)) / (qn * yt(N - 1) + 1)
For k = N - 1 To 1 Step -1
yt(k) = yt(k) * yt(k + 1) + u(k)
Next k
now eval spline at one point
Dim klo, khi As Integer
Dim h, b, a As Single
first find correct interval
klo = 1
khi = N
Do
k = khi - klo
If xin(k) > x Then
khi = k
Else
klo = k
End If
k = khi - klo
Loop While k > 1
h = xin(khi) - xin(klo)
a = (xin(khi) - x) / h
b = (x - xin(klo)) / h
y = a * yin(klo) + b * yin(khi) + ((a ^ 3 - a) * yt(klo) + (b ^ 3 - b) * yt(khi)) * (h ^ 2) _/ 6
CUBIC = y
out:
End Function

updating values of outer loop, For loop in another For loop

My Question: I want to use the last calculated values of the inner loop (i) for outer loop (j). But it starts taking wrong values when (j=2 and i=1 ). where i might have a mistake?
I am not showing any inputs , just need help in understanding the outer loop.
For j = 1 To 4
wf = (2 * j - 1) * wff
For i = 1 To 3
PZ = (1 - GP / GT) * pzi
p = fPpz(PZ, ppc, Tpr)
z = p / PZ
pavg = (p + pwf) / 2
Bg_p = Bg(z, T, p, psc, Tsc)
mug_p = (mugas(SGg, p, T)) / 1000
mug_pavg = (mugas(SGg, pavg, T)) / 1000
Z_pavg = fZ(pavg / ppc, Tpr)
Bg_pavg = Bg(Z_pavg, T, pavg, psc, Tsc)
Time2 = 1 + Time1
delTime = Time2 - Time1
PIndex = PIndn(kres, hres, mug_pavg, Bg_pavg, JDf)
dpdlf = pf(p, pwf, xf, PIndex, tauyhb, nhb, khb, wfg_new, Hf, wf, Bg_pavg, dpdlold,mug_pavg, Bg_p, mug_p)
dpdlnew = dpdlf
If dpdlf < 0 Then
dpdlnew = dpdlold
End If
qg = FrGasF(tauyhb, nhb, khb, wfg_new, Hf, wf, dpdlnew, mug_p)
GP = 2 * qg * delTime * 60 / Bg_p + GP
qhb = FrHBF(tauyhb, nhb, khb, wfg_new, Hf, wf, dpdlnew)
wfg_old = qhb / xf / Hf * delTime * 60 / 2
'=======For next iteration (i+1)======='
wfg_new = wfg_new + wfg_old
Time1 = Time2
dpdlold = dpdlnew
Next i
Next j
Should you be setting wfg_new = 0 in the outer j loop? Otherwise, j=2, i=1 is using the wfg_new value from j=1, i=3 when you call the pffunction