As mentioned above, the function below works, however its very slow. I am very interested in using faster/optimised numpy (or other) vectorized alternatives. I have not posted the entire script here due to it being too large.
My specific question is - are there suitable numpy (or other) functions that I can use to 1) reduce run time and 2) reduce code volume of this function, specifically the for loop?
Edit: mass, temp, U and dpdh are functions that carry out simple algebraic calculations and return constants
def my_system(t, y, n, hIn, min, mAlumina, cpAlumina, sa, V):
dydt = np.zeros(3 * n) #setting up zeros array for solution (solving for [H0,Ts0,m0,H1,Ts1,m1,H2,Ts2,m2,..Hn,Tsn,mn])
# y = [h_0, Ts_0, m_0, ... h_n, Ts_n, m_n]
# y[0] = hin
# y[1] = Ts0
# y[2] = minL
i=0
## Using thermo
T = temp(y[i],P) #initial T
m = mass(y[i],P) #initial m
#initial values
dydt[i] = (min * (hIn - y[i]) + (U(hIn,P,min) * sa * (y[i + 1] - T))) / m # dH/dt (eq. 2)
dydt[i + 1] = -(U(hIn,P,min) * sa * (y[i + 1] - T)) / (mAlumina * cpAlumina) # dTs/dt from eq.3
dmdt = dydt[i] * dpdh(y[i], P) * V # dm/dt (holdup variation) eq. 4b
dydt[i + 2] = min - dmdt # mass flow out (eq.4a)
for i in range(3, 3 * n, 3): #starting at index 3, and incrementing by 3 because we are solving for 'triplets' [h,Ts,m] in each loop
## Using thermo
T = temp(y[i],P)
m = mass(y[i],P)
# [h, TS, mdot]
dydt[i] = (dydt[i-1] * (y[i - 3] - y[i]) + (U(y[i-3], P, dydt[i-1]) * sa * (y[i + 1] - T))) / m # dH/dt (eq.2), dydt[i-1] is the mass of the previous tank
dydt[i + 1] = -(U(y[i-3], P, dydt[i-1]) * sa * (y[i + 1] - T)) / (mAlumina * cpAlumina) # dTs/dt eq. (3)
dmdt = dydt[i] * dpdh(y[i], P) * V # Equation 4b
dydt[i + 2] = dydt[i-1] - dmdt # Equation 4a
return dydt
The functions mass, temp, U, and dpdh used inside the my_system function all take numbers as input, perform some simple algebraic operation and return a number (no need to optimise these I am just providing them for further context)
def temp(H,P):
# returns temperature given enthalpy (after processing function)
T = flasher.flash(H=H, P=P, zs=zs, retry=True).T
return T
def mass(H, P):
# returns mass holdup in mol
m = flasher.flash(H=H, P=P, zs=zs, retry=True).rho()*V
return m
def dpdh(H, P):
res = flasher.flash(H=H, P=P, zs=zs, retry=True)
if res.phase_count == 1:
if res.phase == 'L':
drho_dTf = res.liquid0.drho_dT()
else:
drho_dTf = res.gas.drho_dT()
else:
drho_dTf = res.bulk._equilibrium_derivative(of='rho', wrt='T', const='P')
dpdh = drho_dTf/res.dH_dT_P()
return dpdh
def U(H,P,m):
# Given T, P, m
air = Mixture(['nitrogen', 'oxygen'], Vfgs=[0.79, 0.21], H=H, P=P)
mu = air.mu*1000/mWAir #mol/m.s
cp = air.Cpm #J/mol.K
kg = air.k #W/m.K
g0 = m/areaBed #mol/m2.s
a = sa*n/vTotal #m^2/m^3 #QUESTIONABLE
psi = 1
beta = 10
pr = (mu*cp)/kg
re = (6*g0)/(a*mu*psi)
hfs = ((2.19*(re**1/3)) + (0.78*(re**0.619)))*(pr**1/3)*(kg)/diameterParticle
h = 1/((1/hfs) + ((diameterParticle/beta)/kAlumina))
return h
Reference Image:
enter image description here
For improving the speed, you can see Numba, which is useable if you use NumPy a lot but not every code can be used with Numba. Apart from that, the formulation of the equation system is confusing. You are solving 3 equations and adding the result to a single dydt list by 3 elements each. You can simply create three lists, solve each equation and add them to their respective list. For this, you need to re-write my_system as:
import numpy as np
def my_system(t, RHS, hIn, Ts0, minL, mAlumina, cpAlumina, sa, V):
# get initial boundary condition values
y1 = RHS[0]
y2 = RHS[1]
y3 = RHS[2]
## Using thermo
T = # calculate T
m = # calculate m
# [h, TS, mdot] solve dy1dt for h, dy2dt for TS and dy3dt for mdot
dy1dt = # dH/dt (eq.2), y1 corresponds to initial or previous value of dy1dt
dy2dt = # dTs/dt eq. (3), y2 corresponds to initial or previous value of dy2dt
dmdt = # Equation 4b
dy3dt = # Equation 4a, y3 corresponds to initial or previous value of dy3dt
# Left-hand side of ODE
LHS = np.zeros([3,])
LHS[0] = dy1dt
LHS[1] = dy2dt
LHS[2] = dy3dt
return LHS
In this function, you can pass RHS as a list with initial values ([dy1dt, dy2dt, dy3dt]) which will be unpacked as y1, y2, and y3 respectively and use them for respective differential equations. The solved equations (next values) will be saved to dy1dt, dy2dt, and dy3dt which will be returned as a list LHS.
Now you can solve this using scipy.integrate.odeint. Therefore, you can leave the for loop structure and solve the equations by using this method as follows:
hIn = #some val
Ts0 = #some val
minL = #some val
mAlumina = #some vaL
cpAlumina = #some val
sa = #some val
V = #some val
P = #some val
## Using thermo
T = temp(hIn,P) #initial T
m = mass(hIn,P) #initial m
#initial values
y01 = # calculate dH/dt (eq. 2)
y02 = # calculate dTs/dt from eq.3
dmdt = # calculate dm/dt (holdup variation) eq. 4b
y03 = # calculatemass flow out (eq.4a)
n = # time till where you want to solve the equation system
y0 = [y01, y02, y03]
step_size = 1
t = np.linspace(0, n, int(n/step_size)) # use that start time to which initial values corresponds
res = odeint(my_sytem, y0, t, args=(hIn, Ts0, minL, mAlumina, cpAlumina, sa, V,), tfirst=True)
print(res[:,0]) # print results for dH/dt
print(res[:,1]) # print results for dTs/dt
print(res[:,2]) # print results for Equation 4a
Here, I have passed all the initial values as y0 and chosen a step size of 1 which you can change as per your need.
I'm trying to approximate the European call option price of the Black-Scholes model (PDE) by the explicit finite difference method in python. For reference, the exact solution using the Black-Scholes formula is 10.247013813310648
Here is a link about the PDE Black-Scholes Equation and the discretized version of the equation can be found here Explicit finite difference method for Black-Scholes model
Can anyone point out why I'm not getting an approximation?
import numpy as np
# Terminal time
T = 0.25
# Strike price
K = 10
# risk free rate
r = 0.1
# volatility (systemic/market risk)
sigma = 0.4
# initial asset value
S0 = 20
# Assume an upper limit for the underlying stock that is 3 - 4 times the exercise price
S_max = 3 * K
# Number of space intervals
M = 200
# space mesh and space step
space_mesh, space_step = np.linspace(0, S_max, M, retstep=True)
# Stability condition
stability_cond = 1 / ( sigma**2 * (M-1) + 0.5* r )
# Find the number of time intervals and time steps that satisfy the stability condition
for percentage in np.arange(.99, .0001, -.0001):
time_step = np.round(percentage * stability_cond, 6)
N = T / time_step
if N.is_integer():
print("Number of time intervals = ", N," ", "time step = ", time_step)
# Choose number of time intervals
N = 2000
# time mesh
time_mesh, time_step = np.linspace(0, T, N, retstep= True)
# time step
time_step = np.round(time_step, 6)
# unknown u at new time level
u = np.zeros(M)
# u at the previous time level
u_prev = np.zeros(M)
# initial condition
for m in range(0, M):
u_prev[m] = np.maximum(space_mesh[m] - K, 0)
# Explicit finite difference scheme
for n in range(0, N):
for m in range(1,M-1):
a = 0.5 * time_step * ( sigma**2 *m**2 - r * m )
b = 1 - time_step * ( sigma**2 * m**2 + r )
c = 0.5 * time_step * ( sigma**2 * m**2 + r * m)
# The discretized version of the Black-Scoles PDE
u[m] = a * u_prev[m-1] + b* u_prev[m] + c * u_prev[m+1]
# insert boundry conditions
u[0] = 0
u[M-1] = S_max
# update u_prev before next iteration
u_prev[:] = u
I've created a fatorial function that would help me calculate the taylor expansion of sine. There are no evident mistakes in my code, but the returned value is wrong. That's my code:
PI = 3.14159265358979323846
def fatorial(n):
fatorial = 1
for i in range(1,n+1,1):
fatorial = fatorial * i
return fatorial
def seno(theta):
n = 1
k = 3
eps = 10**-10
theta = (theta*PI)/180
x = ((-1)**n)*((theta)**k)
y = fatorial(k)
while x/y > eps or x/y < -eps:
theta = theta + (x/y)
n = n + 1
k = k + 2
x = ((-1)**n) * ((theta)**k)
y = fatorial(k)
return theta
You are summing theta in the while-loop and therefore using an adjusted angle for the next elements of the Taylor series. Just add a thetas (for the sum) like so (I have taken the liberty to add some performance improvements, no need to recalculate the full factorial, also calculated first elements explicitly and changed the limit check to avoid recalculations of x/y):
import math
PI = 3.14159265358979323846
def fatorial(n):
fatorial = 1
for i in range(1,n+1,1):
fatorial = fatorial * i
return fatorial
def seno(theta):
n = 1
k = 3
#eps = 10**-10
theta = (theta*PI)/180
thetas = theta # <- added this
x = -theta*theta*theta
y = 6
#while x/y > eps or x/y < -eps:
while k < 14:
thetas = thetas + (x/y) # sum thetas
n = n + 1
k = k + 2
#x = ((-1)**n) * ((theta)**k)
x = ((-1)**(n%2)) * ((theta)**k) # but use the original value for the series
#y = fatorial(k)
y *= k * (k-1)
return thetas # return the sum
if __name__ == '__main__':
print(seno(80), math.sin(8*PI/18))
this results in
0.984807753125684 0.984807753012208
Why when I write expX = s0 * np.exp(XY), I end up with the following error ? Length of passed values is 1440, index implies 1.
I want the value S0 to be multiply by the first XY and the result to become the next S0 so that it can be multiply by the next XY, and so on on the 1440 value.
Thanks
T = 1 #len(index_future)
N = 1440 #number of time steps (min in
1 day)
dt = T / N #time steps
mu = returns_log.mean(axis= 0)/N #mean return
t = np.linspace(0, T, N)
#print(mu)
#sys.exit()
sigma = returns_log.std(axis= 0) #total vol
#print(sigma)
#S0 = close.tail(1) #last return observed
S0 = close['ETHJPY'].sample()
#print (S0)
#sys.exit()
dt = sigma/np.sqrt(N) #local volatility
print(dt)
W = np.cumsum(d[0])*np.sqrt(dt[0])
print(W)
np.savetxt("checkkk.csv", W, delimiter=",")
#sys.exit()
Y= (sigma[0]*W) #random shocks
X = (mu[0] - 0.5 * sigma[0]**2) * t #drift
XY = sum(X,Y).T#[np.newaxis] #drift + random shocks
#expX = np.exp(XY['ETHJPY'])
expX = s0 * np.exp(XY)
I have written a program to solve the Heat Equation (u_t = k * u_xx) numerically by method of Finite Differences.
For my problem, u is function of x and t, where 0 < x < L and t > 0. I have specified L = 1 (the length of the rod) and the terminal time T = 10 seconds for my problem, so I would like for the graph to be displayed on the domain (x,t) \in {(0,1) x (0, 10)}. However, my axes just don't make sense. It is plotting the x-axis from values of 0 - 40 and the t-axis is showing -0.25 - 0.00.
How can I edit my code so that when I plot u which depends on x, t the graph will display for values of x ranging from 0 - 1 and t ranging from 0 - 10 seconds??
Thanks in advance for any and all help. it is very greatly appreciated. Here is the code I am working with:
## This program is to implement a Finite Difference method approximation
## to solve the Heat Equation, u_t = k * u_xx,
## in 1D w/out sources & on a finite interval 0 < x < L. The PDE
## is subject to B.C: u(0,t) = u(L,t) = 0,
## and the I.C: u(x,0) = f(x).
import numpy as np
import matplotlib.pyplot as plt
# Parameters
L = 1 # length of the rod
T = 10 # terminal time
N = 40 # spatial values
M = 1600 # time values/hops; (M ~ N^2)
s = 0.25 # s := k * ( (dt) / (dx)^2 )
# uniform mesh
x_init = 0
x_end = L
dx = float(x_end - x_init) / N
x = np.arange(x_init, x_end, dx)
x[0] = x_init
# time discretization
t_init = 0
t_end = T
dt = float(t_end - t_init) / M
t = np.arange(t_init, t_end, dt)
t[0] = t_init
# time-vector
for m in xrange(0, M):
t[m] = m * dt
# spatial-vector
for j in xrange(0, N):
x[j] = j * dx
# definition of the solution u(x,t) to u_t = k * u_xx
u = np.zeros((N, M+1)) # array to store values of the solution
# Finite Difference Scheme:
u[:,0] = x * (x - 1) #initial condition
for m in xrange(0, M):
for j in xrange(1, N-1):
if j == 1:
u[j-1,m] = 0 # Boundary condition
elif j == N-1:
u[j+1,m] = 0 # Boundary Condition
else:
u[j,m+1] = u[j,m] + s * ( u[j+1,m] -
2 * u[j,m] + u[j-1,m] )
# for graph
print u, x, t
plt.plot(u)
plt.title('Finite Difference Approx. to Heat Equation')
plt.xlabel('x-axis')
plt.ylabel('time (seconds)')
plt.axis()
plt.show()
It appears that whatever displays for the x-axis reflects the number of step sizes in space that I take (N = 40) for my code. I thought np.arange(x_init, x_end, dx) would return evenly spaced values within the interval (x_init, x_end) with step size dx? So what am I doing wrong? Thanks again.
You have some issues with your code as your u turns out to be 40x1601 and not 40x1600. However, I think the plot you may be after (after correcting u) is
corrected_u = u[:,:-1:]
plt.pcolor(t, x, corrected_u)