UDF value error for complex itterative function - excel

I want to create an UDF for my excel worksheet. However when I select my input for my UDF I get a value error. Can someone help me?
Code and images below:
code:
Public Function pipediameter(Pth As Double) As Double
Dim D As Double
p = 150
Tv = 30
T = 55
k = 0.000045
vi = 4
a = (1.729 * (10 ^ (-6))) / ((1 + (T / 25)) ^ 1.165)
rho = 988
Cp = 4180
di = 1
i = 1
While i <= 10
v = 4 * Pth / (3.14 * di ^ 2 * Cp * rho * Tv)
Re = v * di / vi
B1 = (0.774 * Log(Re) - 1.41) / (1 + 1.32 * Sqr(k / di))
B2 = ((k * Re) / (3.7 * di)) + (2.51 * B1)
Y = (B1 - ((B1 + (2 * (Log(B2 / Re) / Log(10)))) / (1 + (2.18 / B2)))) ^ (-2)
D = (((8 * Y) / (p * rho)) * ((Pth) / (3.14 * Cp * Tv)) ^ 2) ^ 0.2
di = D
i = i + 1
Wend
pipediameter = D
End Function

Related

VBA - Trying to get the root of a function

I got this equation to solve in VBA:
Energy = y + (0.5) ^ 2 / ( 2 * 9.81 * y ^ 2 )
but I got the Energy value and want to solve for the y value.
In R is easy: uniroot. There is something like this?
The equation is a cubic polynomial with two positive roots and one negative. We can eliminate the negative one, and consider a direct evaluation of the two positive roots with the following VBA code in a Module.
Global Const g As Double = 9.81
Global Const PI As Double = 3.14159265358979
Public Function CalcE(ByVal y As Double) As Double
CalcE = y + (0.5) ^ 2 / (2# * g * y ^ 2)
End Function
Public Function SolveForY(ByVal E As Double, Optional ByVal second As Boolean = False) As Double
' Solve "E = y + (0.5) ^ 2 / ( 2 * g * y ^ 2 )" for "y"
If Not second Then
SolveForY = E / 3# - 2# * E * Sin(Asin((16# * E ^ 3# * g - 27#) / (16# * E ^ 3# * g)) / 3#) / 3#
Else
SolveForY = E / 3# + 2# * E * Sin(Asin((16# * E ^ 3 * g - 27#) / (16# * E ^ 3 * g)) / 3# + PI / 3#) / 3#
End If
End Function
and the supporting Asin() function defined in terms of Atn().
Public Function Asin(ByVal x) As Double
If Abs(x) < 1# Then
Asin = Atn(x / Sqr(1# - x * x))
Else
Asin = 2 * Atn(1) * Sgn(x)
End If
End Function
Some example results are
? SolveForY(1#)
0.12035578724076
? SolveForY(1#, True)
0.986917853921696
And checking the solutions
? CalcE(0.12035578724076)
0.999999999999997
? CalcE(0.986917853921696)
1
Consider using algebra:
Energy = y + (0.5)^2 / ( 2 * 9.81 * y^2 )
Energy = y + .25 / ( 19.62 * y^2 )
Energy * ( 19.62 * y^2 ) = y * ( 19.62 * y^2 ) + .25
Energy * 19.62 * y^2 = 19.62 * y^3 + .25
0 = 19.62 * y^3 - Energy * 19.62 * y^2 + .25
and apply standard techniques:
Previous Post
Here's a translation of John Alexiou's answer from VBA to Excel formulas. If energy in in cell B2 then the three solutions are given by:
=B2/3-2*B2*SIN((ASIN((16*B2^3*9.81-27)/(16*B2^3*9.81))+0*PI())/3)/3
=B2/3-2*B2*SIN((ASIN((16*B2^3*9.81-27)/(16*B2^3*9.81))+4*PI())/3)/3
=B2/3-2*B2*SIN((ASIN((16*B2^3*9.81-27)/(16*B2^3*9.81))+2*PI())/3)/3
The third equation gives negative y.

How do I use multithreading on this function for a np.meshgrid of values?

The following code generates numpy 2D lists of r and E values for the specified intervals.
r = np.linspace(3, 14, 10)
E = np.linspace(0.05, 0.75, 10)
r, E = np.meshgrid(r, E)
I am then using the following nested loop to generate output from the function ionisationGamma for each r and E interval value.
for ridx in trange(len(r)):
z = []
for cidx in range(len(r[ridx])):
z.append(ionisationGamma(r[ridx][cidx], E[ridx][cidx]))
Z.append(z)
Z = np.array(Z)
This loop gives me a 2D numpy array Z, which is my output and I am using it for a 3D graph. The problem with it is: it is taking ~6 hours to generate the output for all these intervals as there are so many values due to np.meshgrid. I have just discovered multi-threading in Python and wanted to know how I can implement this by using it. Any help is appreciated.
See below code for ionisationGamma
def ionisationGamma(r, E):
I = complex(0.1, 1.0)
a_soft = 1.0
omega = 0.057
beta = 0.0
dt = 0.1
steps = 10000
Nintervals = 60
N = 3000
xmin = float(-300)
xmax = -xmin
x = [0.0]*N
dx = (xmax - xmin) / (N - 1)
L = dx * N
dk = 2 * M_PI / L
propagator = None
in_, out_, psi0 = None, None, None
in_ = [complex(0.,0.)] * N
psi0 = [complex(0.,0.)] * N
out_ = [[complex(0.,0.)]*N for i in range(steps+1)]
overlap = exp(-r) * (1 + r + (1 / 3) * pow(r, 2))
normC = 1 / (sqrt(2 * (1 + overlap)))
gammai = 0.5
qi = 0.0 + (r / 2)
pi = 0.0
gammai1 = 0.5
gammai2 = 0.5
qi1 = 0.0 - (r / 2)
qi2 = 0.0 + (r / 2)
pi1 = 0.0
pi2 = 0.0
# split initial wavepacket
for i in range(N):
x[i] = xmin + i * dx
out_[0][i] = (normC) * ((pow(gammai1 / M_PI, 1. / 4.) * exp(complex(-(gammai1 / 2.) * pow(x[i] - qi1, 2.), pi1 * (x[i] - qi1)))) + (pow(gammai2 / M_PI, 1. / 4.) * exp(complex(-(gammai2 / 2.) * pow(x[i] - qi2, 2.), pi2 * (x[i] - qi2)))))
in_[i] = (normC) * ((pow(gammai1 / M_PI, 1. / 4.) * exp(complex(-(gammai1 / 2.) * pow(x[i] - qi1, 2.), pi1 * (x[i] - qi1)))) + (pow(gammai2 / M_PI, 1. / 4.) * exp(complex(-(gammai2 / 2.) * pow(x[i] - qi2, 2.), pi2 * (x[i] - qi2)))))
psi0[i] = in_[i]
for l in range(1, steps+1):
for i in range(N):
propagator = exp(complex(0, -potential(x[i], omega, beta, a_soft, r, E, dt, l) * dt / 2.))
in_[i] = propagator * in_[i];
in_ = np.fft.fft(in_, N)
for i in range(N):
k = dk * float(i if i < N / 2 else i - N)
propagator = exp(complex(0, -dt * pow(k, 2) / (2.)))
in_[i] = propagator * in_[i]
in_ = np.fft.ifft(in_, N)
for i in range(N):
propagator = exp(complex(0, -potential(x[i], omega, beta, a_soft, r, E, dt, l) * dt / 2.))
in_[i] = propagator * in_[i]
out_[l][i] = in_[i]
initialGammaCentre = 0.0
finalGammaCentre = 0.0
for i in range(500, 2500 +1):
initialGammaCentre += pow(abs(out_[0][i]), 2) * dx
finalGammaCentre += pow(abs(out_[steps][i]), 2) * dx
ionisationGamma = finalGammaCentre / initialGammaCentre
return ionisationGamma
def potential(x, omega, beta, a_soft, r, E, dt, l):
V = (-1. / sqrt((x - (r / 2)) * (x - (r / 2)) + a_soft * a_soft)) + ((-1. / sqrt((x + (r / 2)) * (x + (r / 2)) + a_soft * a_soft))) + E * x
return V
Since the question is about how to use multiprocessing, the following code will work:
import multiprocessing as mp
if __name__ == '__main__':
with mp.Pool(processes=16) as pool:
Z = pool.starmap(ionisationGamma, arguments)
Z = np.array(Z)
Where the arguments are:
arguments = list()
for ridx in range(len(r)):
for cidx in range(len(r[ridx])):
arguments.append((r[ridx][cidx], E[ridx][cidx]))
I am using starmap instead of map, since you have multiple arguments that you want to unpack. This will divide the arguments iterable over multiple cores, using the ionisationGamma function and the final result will be ordered.
However, I do feel the need to say that the main solution is not really the multiprocessing but the original function code. In ionisationGamma you are using several times the slow python for loops. And it would benefit your code a lot if you could vectorize those operations.
A second observation is that you are using many of those loops separately and it would be nice if you could separate that one big function into multiple smaller functions. Then you can time every function individually and speed up those that are too slow.

Z factor iteration using VBA

I created a form button with two subroutines.
1.Pressure Depth Calculations
2. Z factor calculation
Both are iterations. (1) is running properly while (2) does not execute.
Following are the codes:
Sub PressureDepthCalculation()
'Declaring Variables
Dim i, t, row As Integer
t = Range("B5").Value
row = 11
'For Loop
For i = t To 0 Step -100
'Depth caclculation
Range("A" & row).Value = i
'Pressure calculation
Range("B" & row).Value = Range("F5").Value + 0.052 * Range("F6") * i
row = row + 1
Next i
End Sub
Sub ZFactorCalculation()
'Z factor calculation
Dim r1, r2, r3, r4, r5, ppc, tpc, ppr, tpr, fr, dfr, ddfr, rhor As Double
Dim i, row,t As Integer
t = 1
row = 11
Range("D6").Value = 10.731
a1 = 0.3265
a2 = 1.07 * -1
a3 = 0.5339 * -1
a4 = 0.01569
a5 = 0.05165 * -1
a6 = 0.5475
a7 = 0.7361 * -1
a8 = 0.1844
a9 = 0.1056
a10 = 0.6134
a11 = 0.721
For i = t To 100
ppc = (4.6 + (0.1 * Range("H6").Value) - (0.258 * Range("H6").Value ^ 2) * 10.1325 * 14.7)
tpc = (99.3 + (180 * Range("H6").Value) - (6.94 * Range("H6").Value ^ 2) * 1.8)
ppr = 6760 / ppc
tpr = Range("B6").Value / tpc
rhor = 0.27 * ppr / tpr
r1 = (a1 + (a2 / tpr) + (a3 / tpr ^ 3) + (a4 / tpr ^ 4) + (a5 / tpr ^ 5))
r2 = ((0.27 * ppr) / tpr)
r3 = (a6 + (a7 / tpr) + (a8 / tpr ^ 2))
r4 = a9 * ((a7 / tpr) + (a8 / tpr ^ 2))
r5 = (a10 / tpr ^ 3)
fr = (r1 * rhor) - (r2 / rhor) + (r3 * rhor ^ 2) - (r4 * rhor ^ 5) + (r5 * (1 + (a11 * rhor ^ 2))) * (Exp(-a11 * rhor ^ 2)) + 1
dfr = (r1) + (r2 / rhor ^ 2) + (2 * r3 * rhor) - (5 * r4 * rhor ^ 4) + (2 * r5 * rhor * (Exp(-a11 * rhor ^ 2)) * ((1 + (2 * a11 * rhor ^ 3)) - (a11 * rhor ^ 2 * (1 + (a11 * rhor ^ 2)))))
ddfr = rhor - (fr / dfr)
If Abs(rhor - ddfr) <= 0.000000000001 Then
Range("I" & r).Value = (0.27 * ppr) / (rhor * tpr)
Else
rhor = ddfr
End If
Also when in (1) I calculate Range("B"&row).value, I want to use it to calculate ppc in place of 6760 in (2).
It appears that your second loop has no directive to continue the loop.
You can either do:
For i = t To 100 **Step someNumber** 'The -Step- argument was used in loop 1, but was omitted in loop 2
Or this:
For i = t To 100
'the rest of your code here
**Next i** 'will increment i by 1
Welcome to StackOverflow :)

Code takes forever to come up with a solution

What I basically want, is comparing a timevalue (t1 and tuit)(in hours) to determine which method to use to calculate 'S' and 'k' in a function called 'stijghoogteverlaging'. Then a fitted curve can be made with those values.
I tried multiple things, like putting 'return s' underneath both s-methods.
if t1[i] < tuit:
s = Q / (4 * np.pi * k * D) * exp1(S * r**2 / (4 * k * D * t))
return s
else:
s = Q / (4 * np.pi * k * D) * ((exp1(S * r**2 / (4 * k * D * t))) - (exp1(S * r**2 / (4 * k * D * (t - tuit)))))
return s
But then I got a wrong fitted curve as can be seen in the image below.
Now I tried putting only one 'return s', but then it takes forever to calculate and I have to interrupt the kernel.
data = read_csv("pompproef_data.csv", sep = ';')
pb1 = data.iloc[1:,1].values-1.87
pb2 = data.iloc[1:,2].values-1.86
t1 = data.iloc[1:,0].values / (60*24)
volume = 10/1000 #m3
duur = [128,136, 150, 137, 143, 141] #seconden
totaal = np.sum(duur)
debiet = (((len(duur) * volume)/totaal)) * (60*60*24) #m3/d
print(debiet)
print(t1)
print(pb1)
tuit = 15/(24*60)
D = 2.0
Q = debiet
def stijghoogteverlaging(t, k, S):
for i in range(len(t1)):
if t1[i] < tuit:
s = Q / (4 * np.pi * k * D) * exp1(S * r**2 / (4 * k * D * t))
else:
s = Q / (4 * np.pi * k * D) * ((exp1(S * r**2 / (4 * k * D * t))) - (exp1(S * r**2 / (4 * k * D * (t - tuit)))))
return s
r = 4.0 #afstand peilbuis1 tot put
poptpb1, pcovpb1 = curve_fit(stijghoogteverlaging, t1, pb1, p0=[100, 1e-25], maxfev = 10000000)
print('optimale waarde van k voor peilbuis1:', poptpb1[0])
print('optimale waarde van S voor peilbuis1:', poptpb1[1])
tijd = data.iloc[1:,0].values
t = np.linspace(0.00069*(24*60), 0.021*(24*60), 1000)
s1 = stijghoogteverlaging(t, poptpb1[0], poptpb1[1])
plt.plot(tijd, pb1, 'r.', label = 'Gemeten bij 4 meter')
plt.plot(t, s1, 'b', label = 'fitted bij 4 m')
Does anyone have a solution?
Used values for t1 and pb1:
Plot with a wrong fitted curve(time in minutes).
The function stijghoogteverlaging is performing a nonsense operation over and over:
def stijghoogteverlaging(t, k, S):
for i in range(len(t1)):
if t1[i] < tuit:
s = Q / (4 * np.pi * k * D) * exp1(S * r**2 / (4 * k * D * t))
else:
s = Q / (4 * np.pi * k * D) * ((exp1(S * r**2 / (4 * k * D * t))) - (exp1(S * r**2 / (4 * k * D * (t - tuit)))))
return s
You are iterating len(t1) times, and at each iteration, you are computing the full vectorized value of s each and every time. That means that you are computing len(t)**2 values per call, and using a Python for loop as your outer loop to do it. As a minor point, you are accessing the x-data as the global variable t1 instead of the local value t, which gets passed in.
Your function should probably look more like this:
def stijghoogteverlaging(t, k, S):
return np.where(t < tuit,
Q / (4 * np.pi * k * D) * exp1(S * r**2 / (4 * k * D * t)),
Q / (4 * np.pi * k * D) * ((exp1(S * r**2 / (4 * k * D * t))) - (exp1(S * r**2 / (4 * k * D * (t - tuit)))))
)
This computes len(t) * 2 values per call, not len(t)**2, and selects a value from the appropriate result for each value of t.

Arithmetic Asian Option Pricing

I keep getting the invalid procedure call or argument error on the definition of sigma2d line.
Any idea how to avoid this code error?
Private Sub CommandButton4_Click()
Application.Range("E19").value = ""
Application.Range("F19").value = ""
S0 = Application.Range("C5").value 'arithmetic average of underlying 1
K = Application.Range("C6").value 'strike
T = Application.Range("C10").value 'maturity
sigma = Application.Range("C8").value 'volatility
r = Application.Range("C8").value 'risk free rate
nsteps = Application.Range("C12").value 'no of timesteps
nsimulations = Application.Range("C13").value ' no of mc simulations
div = Application.Range("C9").value 'dividends
Randomize
Dim M1 As Double, M2 As Double, sigma2d As Double
Dim d1 As Double, d2 As Double, Nd1 As Double, Nd2 As Double
M1 = (Exp((r - div) * T) - 1) / (r - div) * T
v = (2 * Exp((2 * r) - (2 * div) + (sigma * sigma) * T)) * S0 * S0
w = (r - div + (sigma * sigma)) * (2 * r - 2 * q + (sigma * sigma)) * T * T
Z = 2 * S0 * S0 / ((r - div) * T * T)
y = (1 / 2 * (r - div) + sigma * sigma)
h = Exp((r - div) * T) / (r - div + (sigma * sigma))
M2 = (v / w) + Z * (y - h)
M3 = M1 * M1
sigma2d = Log(M2 / M3)
d1 = (Log(M1 / K) + (sigma2d * T) / 2) / sigma * Sqr(T)
d2 = d1 - sigma * Sqr(T)
callArith = Exp(-r * T) * (M1 * Nd1 - K * Nd2)
Application.Range("E19").value = Application.Max(ExactCall, 0)
Are you trying to do the log of a negative number? Set a breakpoint and check variables before that line. Maybe you have an error before that generating a negative.
First check the argument to the Log function is positive.
Failing that, it could be due to a missing reference in the project. This manifests itself in this curious way. Have a look at "Tools", "References" and see if there is one missing.
You can write sigma2d = Vba.Log(M2 / M3) instead but that's only really a short fix since missing references will cause you headaches elsewhere.
One more thing, why not create a function instead, passing in all the variables as function parameters? Your spreadsheet will be more stable if you do that.
(Also, at the end of your code, d1 definition is incorrect. You need brackets around sigma * Sqr(T)).
I think you need a pair of () or do "/T" as you are multiplying by T here:
M1 = (Exp((r - div) * T) - 1) / (r - div) * T

Resources