Can cython make things slower? - python

def htc_gnielinski_calc(
self, MFR_ref_in, rho_ref_in, mu_ref_in, cp_ref_in, k_ref_in
):
"""
Gnielinski Refrigerant Heat transfer model: Calculate HTC
"""
V_ref_in = MFR_ref_in / (rho_ref_in * pi * self.ID ** 2 / 4)
Re = (rho_ref_in * V_ref_in * self.ID) / mu_ref_in
if Re < 3000:
HTC_Gnielinski = (k_ref_in / self.ID) * 3.66
return HTC_Gnielinski
f = (1.58 * log(Re) - 3.28) ** (-2)
Pr_ref = mu_ref_in * cp_ref_in / k_ref_in
HTC_Gnielinski = (
(k_ref_in / self.ID)
* ((f / 2) * (Re - 1000) * Pr_ref)
/ (1 + 12.7 * (f / 2) ** 0.5 * (Pr_ref ** (2 / 3) - 1))
)
return HTC_Gnielinski
This is a equation solving heat transfer coefficient of refrigerant.
Importing cython to make my project run faster, actually it takes more time.
Here's my cython code
cpdef htc_gnielinski_calc(double MFR_ref_in, double rho_ref_in, double mu_ref_in, double cp_ref_in, double k_ref_in, double ID):
"""
Gnielinski Refrigerant Heat transfer model: Calculate HTC
"""
cdef double V_ref_in = MFR_ref_in / (rho_ref_in * pi * ID ** 2 / 4)
cdef double Re = (rho_ref_in * V_ref_in * ID) / mu_ref_in
cdef double HTC_Gnielinski, f, Pr_ref
if Re < 3000:
HTC_Gnielinski = (k_ref_in / ID) * 3.66
return HTC_Gnielinski
f = (1.58 * log(Re) - 3.28) ** (-2)
Pr_ref = mu_ref_in * cp_ref_in / k_ref_in
HTC_Gnielinski = (
(k_ref_in / ID)
* ((f / 2) * (Re - 1000) * Pr_ref)
/ (1 + 12.7 * (f / 2) ** 0.5 * (Pr_ref ** (2 / 3) - 1))
)
return HTC_Gnielinski
However, code below this make my project run faster. (Different equation)
cpdef htc_shah_cond_calc(
double MFR_ref_in,
double P_ref_in,
double x_ref_in,
double mu_ref_l_in,
double mu_ref_g_in,
double cp_ref_l_in,
double k_ref_l_in,
double rho_ref_l_in,
double rho_ref_g_in,
double ID,
double P_critical_ref
):
"Two-phase HTC"
cdef int theta = 0
cdef double G = MFR_ref_in / ((pi * ID ** 2) / 4)
cdef double P_r = P_ref_in / P_critical_ref
cdef double Z = (1 / x_ref_in - 1) ** 0.8 * P_r ** 0.4
cdef double Re_LS = (G * (1 - x_ref_in) * ID) / mu_ref_l_in # Reynolds number assuming liquid phase flowing alone
cdef double Pr_l = mu_ref_l_in * cp_ref_l_in / k_ref_l_in
cdef double h_LS = 0.023 * Re_LS ** 0.8 * Pr_l ** 0.4 * (k_ref_l_in / ID)
cdef double h_I = ( h_LS * (1 + 3.8 / (Z ** 0.95)) * (mu_ref_l_in / (14 * mu_ref_g_in)) ** (0.0058 + 0.557 * P_r) )
cdef double h_Nu = (1.32* Re_LS ** (-1 / 3)* (rho_ref_l_in* (rho_ref_l_in - rho_ref_g_in)* 9.80665* k_ref_l_in ** 3/ (mu_ref_l_in ** 2))** (1 / 3))
cdef double J_g = (x_ref_in * G) / ( 9.80665 * ID * rho_ref_g_in * (rho_ref_l_in - rho_ref_g_in) ) ** 0.5
cdef double HTC_Shah_Cond
if theta == 0:
if J_g >= (0.98 * (Z + 0.263) ** (-0.62)):
HTC_Shah_Cond = h_I # Regime 1 in horizontal tube
elif J_g <= (0.95 * (1.254 + 2.27 * Z ** (1.249)) ** (-1)):
HTC_Shah_Cond = h_Nu # Regime 3 in horizontal tube
elif J_g > (0.95 * (1.254 + 2.27 * Z ** (1.249)) ** (-1)) and J_g < (
0.98 * (Z + 0.263) ** (-0.62)
):
HTC_Shah_Cond = h_I + h_Nu # Regime 2 in horizontal tube
elif theta == 90:
if J_g >= (1 / (2.4 * Z + 0.73)):
HTC_Shah_Cond = h_I # Regime 1 in vertical tube
elif J_g <= (0.89 - 0.93 * exp(-0.087 * Z ** (-1.17))):
HTC_Shah_Cond = h_Nu # Regime 3 in vertical tube
elif J_g > (0.89 - 0.93 * exp(-0.087 * Z ** (-1.17))) and J_g < (
1 / (2.4 * Z + 0.73)
):
HTC_Shah_Cond = h_I + h_Nu # Regime 2 in vertical tube
return HTC_Shah_Cond
What would be the differences?
Are there any criteria for taking cython during optimization?

Related

Numerical instability in python

I am trying to make several plots for a project of mine using the following code:
import pprint
import scipy
import scipy.linalg # SciPy Linear Algebra Library
import numpy as np
from scipy.linalg import lu , lu_factor, lu_solve
from scipy.integrate import quad
import matplotlib.pyplot as plt
#Solving the equations for the Prandtl case
K = 100
alpha = 0.1
visc = 5
diff = 5
N = 0.01
L = 5000
height = 250
subdivisions = 100
tick = 10
points = np.arange(0,L/2+tick,tick)
def H(y):
return ( height * (1 + np.cos(2 * np.pi * y/L)) )
def Bsfc(y):
return 0.1
final_system = []
b=[]
for q in range(-K,K+1):
equation1 = []
equation2 = []
equation3 = []
Aki = []
Cki = []
Dki = []
for k in range(-K,K+1):
R = 2 * N**2 * np.cos(alpha)**2 / (visc * diff) * (k * np.pi / L)**2
Q = N**2 * np.sin(alpha)**2 / (3 * visc * diff)
S1 = abs(R + np.sqrt(Q**3 + R**2) )**(1/3)
S2 = - abs( np.sqrt(Q**3 + R**2) -R )**(1/3)
phi = np.sqrt(S1**2 + S2**2 - S1*S2)
Lk = np.arccos(- (S1 + S2)/ (2 * phi) )
m1 = - np.sqrt(S1 + S2)
m2 = - np.sqrt(phi) * np.exp(1j * Lk/2)
m3 = m2.conjugate()
def f1r(y):
return (np.exp(m1 * H(y)) * np.cos(2 * (q - k) * np.pi * y / L) ).real
def f1i(y):
return (np.exp(m1 * H(y)) * np.cos(2 * (q - k) * np.pi * y / L) ).imag
gamma1 = 2/L * (quad(f1r,0,L/2,limit=subdivisions)[0] + quad(f1i,0,L/2,limit=subdivisions)[0]*1j)
def f2r(y):
return (np.exp(m2 * H(y)) * np.cos(2 * (q - k) * np.pi * y / L) ).real
def f2i(y):
return (np.exp(m2 * H(y)) * np.cos(2 * (q - k) * np.pi * y / L) ).imag
gamma2 = 2/L * (quad(f2r,0,L/2,limit=subdivisions)[0] + quad(f2i,0,L/2,limit=subdivisions)[0]*1j)
if k == 0:
equation1.append(2 * gamma2.real)
Cki.append(k)
equation1.append(-2 * gamma2.imag)
Dki.append(k)
else:
equation1.append(gamma1)
Aki.append(k)
equation1.append(2 * gamma2.real)
Cki.append(k)
equation1.append(-2 * gamma2.imag)
Dki.append(k)
if q != 0:
if k == 0:
equation2.append(0)
equation2.append(0)
else:
equation2.append(k * gamma1 / (m1**3) )
equation2.append(2 * k * (gamma2 / (m2**3) ).real)
equation2.append(-2 * k * (gamma2 / (m2**3) ).imag)
if k == 0:
equation3.append(2 * (m2**2 * gamma2).real)
equation3.append(-2 * (m2**2 * gamma2).imag)
else:
equation3.append(m1**2 * gamma1)
equation3.append(2 * (m2**2 * gamma2).real)
equation3.append(-2 * (m2**2 * gamma2).imag)
final_system.append(equation1)
def f4r(y):
return (Bsfc(y) * np.cos(2 * q * np.pi * y / L) ).real
def f4i(y):
return (Bsfc(y) * np.cos(2 * q * np.pi * y / L) ).imag
b.append(2/L * (quad(f4r,0,L/2,limit=subdivisions)[0] + quad(f4i,0,L/2,limit=subdivisions)[0]*1j))
if q != 0:
final_system.append(equation2)
b.append(0)
final_system.append(equation3)
b.append(0)
final_system = np.array(final_system)
b=np.array(b)
#LU solver
P, Ls, U = scipy.linalg.lu(final_system)
Bl = np.linalg.inv(P) # b
Z = np.linalg.solve(Ls,Bl)
X = np.linalg.solve(U,Z)
print (np.allclose(final_system # X, b))
#Getting the values for Ak, Ck and Dk
strings = []
for k in range(-K,K+1):
if k != 0:
strings.append('A')
strings.append('R')
strings.append('I')
Ak = []
Rk = []
Ik = []
for k in range(0,len(X)):
if 'A' in strings[k]:
Ak.append(X[k])
if 'R' in strings[k]:
Rk.append(X[k])
if 'I' in strings[k]:
Ik.append(X[k])
Ck=[]
for k in range(0,len(Rk)):
Ck.append(Rk[k] + Ik[k] * 1j)
Ck = np.array(Ck)
Dk = Ck.conjugate()
Ak = np.array(Ak)
#Getting the Buoyancy value
z = np.arange(0,2010,10)
y = np.arange(-L,L+10,10)
Y,Z = np.meshgrid(y,z)
B = np.ones_like(Y)*[0]
for k in range(-K,K+1):
R = 2 * N**2 * np.cos(alpha)**2 / (visc * diff) * (k * np.pi / L)**2
Q = N**2 * np.sin(alpha)**2 / (3 * visc * diff)
S1 = abs(R + np.sqrt(Q**3 + R**2) )**(1/3)
S2 = - abs( np.sqrt(Q**3 + R**2) -R )**(1/3)
phi = np.sqrt(S1**2 + S2**2 - S1*S2)
Lk = np.arccos(- (S1 + S2)/ (2 * phi) )
m1 = - np.sqrt(S1 + S2)
m2 = -np.sqrt(phi) * np.exp(1j * Lk/2)
m3 = m2.conjugate()
if k != 0:
B = B + ( Ak[Aki.index(k)] * np.exp(m1 * Z) * np.exp(2j * (k) * np.pi * Y / L) )
B = B + ( ( Ck[Cki.index(k)] * np.exp(m2 * Z) + Dk[Dki.index(k)] * np.exp(m3 * Z) ) * np.exp(2j * (k) * np.pi * Y / L) )
for k in range(0,B.shape[0]):
for t in range(0,B.shape[1]):
if Z[k][t] < H(Y[k][t]):
B[k][t] = np.nan
if Z[k][t] == H(Y[k][t]):
print (B[k][t], "B value at the ground")
if abs(Z[k][t] - H(Y[k][t])) < 0.1:
if B[k][t] > 0.101:
print (B[k][t],'error -------------------------------------------------')
# print (B[k][t], Z[k][t], H(Y[k][t]), Y[k][t], '-----------------------------------------------------------------------------' )
Bp = Bsfc(Y) * np.exp(-Z * np.sqrt(N * np.sin(alpha) ) / (4*visc*diff)**(1/4) ) * np.cos(np.sqrt(N*np.sin(alpha)) /((4*visc*diff)**(1/4))*Z )
##Plotting the buoyancy
fig = plt.figure(figsize=(10,10)) # create a figure
plt.rcParams.update({'font.size':16})
plt.title('Buoyancy')
plt.contourf(Y,Z,B,np.arange(-0.2,0.201,0.001),cmap='seismic')
#plt.contourf(Y,Z,B,cmap='seismic')
plt.colorbar(label='1/s')
plt.xlabel("Y axis")
plt.ylabel("Height")
plt.xlim([-L,L])
plt.ylim([0,1500])
plt.show()
The following plot shows a run that yielded a good result:
Buoyancy
However, when I increase the "height" parameter, I start getting unstable results, which I suspect occurs because of numerical instabilities:
Buoyancy unstable
Is there a way to increase numerical precision in python? I have experimented a bit with numpy.double, but with unsuccessful results so far.
Thanks
I guess you'll find your answer here on Stackoverflow
In the standard library, the decimal module may be what you're looking
for. Also, I have found mpmath to be quite helpful...

How do I convert C program to python

I have been trying to import a C code in python file. But its not working. So I've decided to convert the C program into Python so that importing the function will be easier.
The C code I want to convert is given below. (I got this from github)
#include "Python.h"
#include "numpy/arrayobject.h"
#include <math.h>
# define CUBE(x) ((x) * (x) * (x))
# define SQR(x) ((x) * (x))
static PyObject *interp3_tricubic(PyObject *self, PyObject *args);
float TriCubic (float px, float py, float pz, float *volume, int xDim, int yDim, int zDim);
// what function are exported
static PyMethodDef tricubicmethods[] = {
{"_interp3_tricubic", interp3_tricubic, METH_VARARGS},
{NULL, NULL}
};
// This function is essential for an extension for Numpy created in C
void inittricubic() {
(void) Py_InitModule("tricubic", tricubicmethods);
import_array();
}
// the data should be FLOAT32 and should be ensured in the wrapper
static PyObject *interp3_tricubic(PyObject *self, PyObject *args)
{
PyArrayObject *volume, *result, *C, *R, *S;
float *pr, *pc, *ps;
float *pvol, *pvc;
int xdim, ydim, zdim;
// We expect 4 arguments of the PyArray_Type
if(!PyArg_ParseTuple(args, "O!O!O!O!",
&PyArray_Type, &volume,
&PyArray_Type, &C,
&PyArray_Type, &R,
&PyArray_Type, &S)) return NULL;
if ( NULL == volume ) return NULL;
if ( NULL == C ) return NULL;
if ( NULL == R ) return NULL;
if ( NULL == S ) return NULL;
// result matrix is the same size as C and is float
result = (PyArrayObject*) PyArray_ZEROS(PyArray_NDIM(C), C->dimensions, NPY_FLOAT, 0);
// This is for reference counting ( I think )
PyArray_FLAGS(result) |= NPY_OWNDATA;
// massive use of iterators to progress through the data
PyArrayIterObject *itr_v, *itr_r, *itr_c, *itr_s;
itr_v = (PyArrayIterObject *) PyArray_IterNew(result);
itr_r = (PyArrayIterObject *) PyArray_IterNew(R);
itr_c = (PyArrayIterObject *) PyArray_IterNew(C);
itr_s = (PyArrayIterObject *) PyArray_IterNew(S);
pvol = (float *)PyArray_DATA(volume);
xdim = PyArray_DIM(volume, 0);
ydim = PyArray_DIM(volume, 1);
zdim = PyArray_DIM(volume, 2);
while(PyArray_ITER_NOTDONE(itr_v)) {
pvc = (float *) PyArray_ITER_DATA(itr_v);
pr = (float *) PyArray_ITE R_DATA(itr_r);
pc = (float *) PyArray_ITER_DATA(itr_c);
ps = (float *) PyArray_ITER_DATA(itr_s);
*pvc = TriCubic(*pc, *pr, *ps, pvol, xdim, ydim, zdim);
PyArray_ITER_NEXT(itr_v);
PyArray_ITER_NEXT(itr_r);
PyArray_ITER_NEXT(itr_c);
PyArray_ITER_NEXT(itr_s);
}
return result;
}
/*
* TriCubic - tri-cubic interpolation at point, p.
* inputs:
* px, py, pz - the interpolation point.
* volume - a pointer to the float volume data, stored in x,
* y, then z order (x index increasing fastest).
* xDim, yDim, zDim - dimensions of the array of volume data.
* returns:
* the interpolated value at p.
* note:
* rudimentary range checking is done in this function.
*/
float TriCubic (float px, float py, float pz, float *volume, int xDim, int yDim, int zDim)
{
int x, y, z;
int i, j, k;
float dx, dy, dz;
float *pv;
float u[4], v[4], w[4];
float r[4], q[4];
float vox = 0;
int xyDim;
xyDim = xDim * yDim;
x = (int) px, y = (int) py, z = (int) pz;
// necessary evil truncating at dim-2 because tricubic needs 2 more values
// which is criminal near edges
// future work includes doing trilinear for edge cases
// range checking is extremely important here
if (x < 3 || x > xDim-3 || y < 3 || y > yDim-3 || z < 3 || z > zDim-3)
return (0);
dx = px - (float) x, dy = py - (float) y, dz = pz - (float) z;
pv = volume + (x - 1) + (y - 1) * xDim + (z - 1) * xyDim;
/* factors for Catmull-Rom interpolation */
u[0] = -0.5 * CUBE (dx) + SQR (dx) - 0.5 * dx;
u[1] = 1.5 * CUBE (dx) - 2.5 * SQR (dx) + 1;
u[2] = -1.5 * CUBE (dx) + 2 * SQR (dx) + 0.5 * dx;
u[3] = 0.5 * CUBE (dx) - 0.5 * SQR (dx);
v[0] = -0.5 * CUBE (dy) + SQR (dy) - 0.5 * dy;
v[1] = 1.5 * CUBE (dy) - 2.5 * SQR (dy) + 1;
v[2] = -1.5 * CUBE (dy) + 2 * SQR (dy) + 0.5 * dy;
v[3] = 0.5 * CUBE (dy) - 0.5 * SQR (dy);
w[0] = -0.5 * CUBE (dz) + SQR (dz) - 0.5 * dz;
w[1] = 1.5 * CUBE (dz) - 2.5 * SQR (dz) + 1;
w[2] = -1.5 * CUBE (dz) + 2 * SQR (dz) + 0.5 * dz;
w[3] = 0.5 * CUBE (dz) - 0.5 * SQR (dz);
for (k = 0; k < 4; k++)
{
q[k] = 0;
for (j = 0; j < 4; j++)
{
r[j] = 0;
for (i = 0; i < 4; i++)
{
r[j] += u[i] * *pv;
pv++;
}
q[k] += v[j] * r[j];
pv += xDim - 4;
}
vox += w[k] * q[k];
pv += xyDim - 4 * xDim;
}
return vox;
}
I have tried to convert this code to python. But the output I got is wrong. The python code I created is added below.
import numpy as N
import math
import scipy
global result
def interp3_tricubic(volume, C, R, S):
if volume is None :
result = 0
elif C is None:
result = 0
elif R is None:
result = 0
elif S is None:
result = 0
else:
result = N.zeros(len(C), dtype=('float'))
tri_v = N.array(volume, dtype=("float"))
tri_r = N.array(R, dtype=("float"))
tri_c = N.array(C, dtype=("float"))
tri_s = N.array(S, dtype=("float"))
tri_vol = N.array(volume, dtype=("float"))
xDim = volume.shape[0]
yDim = volume.shape[1]
zDim = volume.shape[2]
for i in range(len(C)):
tri_v = TriCubic(tri_c[i], tri_r[i], tri_s[i], volume, xDim, yDim, zDim)
i = i + 1
# print(tri_v, "tri_v")
return tri_v
def TriCubic ( px, py, pz, volume, xDim, yDim, zDim):
xyDim = xDim * yDim
x = px.astype(int)
y = py.astype(int)
z = pz.astype(int)
dx = px - x
dy = py - y
dz = pz - z
pv = volume + (x - 1) + (y - 1) * xDim + (z - 1) * xyDim;
def cube(num):
return num * num * num
def sqrt(num):
return num * num
u = N.array([0,0,0,0], dtype=('float'))
v = N.array([0,0,0,0], dtype=('float'))
w = N.array([0,0,0,0], dtype=('float'))
vox = N.zeros_like(volume, dtype=('float'))
u[0] = -0.5 * cube (dx) + sqrt (dx) - 0.5 * dx;
u[1] = 1.5 * cube (dx) - 2.5 * sqrt (dx) + 1;
u[2] = -1.5 * cube (dx) + 2 * sqrt (dx) + 0.5 * dx;
u[3] = 0.5 * cube (dx) - 0.5 * sqrt (dx);
v[0] = -0.5 * cube (dy) + sqrt (dy) - 0.5 * dy;
v[1] = 1.5 * cube (dy) - 2.5 * sqrt (dy) + 1;
v[2] = -1.5 * cube (dy) + 2 * sqrt (dy) + 0.5 * dy;
v[3] = 0.5 * cube (dy) - 0.5 * sqrt (dy);
w[0] = -0.5 * cube (dz) + sqrt (dz) - 0.5 * dz;
w[1] = 1.5 * cube (dz) - 2.5 * sqrt (dz) + 1;
w[2] = -1.5 * cube (dz) + 2 * sqrt (dz) + 0.5 * dz;
w[3] = 0.5 * cube (dz) - 0.5 * sqrt (dz);
k = 0
j = 0
i = 0
q = [0,0,0,0]
r = [0,0,0,0]
for k in range(4):
for j in range(4):
for i in range(4):
r[j] += u[i] * pv[i]
i = i+1
q[k] += v[j] * r[j]
pv += xDim - 4
j = j+1
vox += w[k] * q[k]
pv += xyDim - 4 * xDim
k = k+1
return vox
I am confused on the meaning of some lines.
Like these lines...
static PyObject *interp3_tricubic(PyObject *self, PyObject *args);
itr_v = (PyArrayIterObject *) PyArray_IterNew(result);
r[j] += u[i] * *pv;
Please help me correct the code. I am stuck!

Random number generators C++ and Python

I have programmed a model in both C++ and Python. This model has a noisy-input component, which I can replace with this C++:
double doubleRand() {
thread_local std::mt19937 generator(std::random_device{}());
std::normal_distribution<double> distribution(0.0, 1.0);
return distribution(generator);
}
Or this Python:
Inoise = (np.random.normal(0, 1) * knoise * np.sqrt(gNa * A))
IIon = ((iNa + iK + iL) * A) + Inoise #
# Compute change of voltage
v[i + 1] = (vT + ((-IIon + IStim) / C) * dt)[0]
The following is very strange:
If I omit the noisy component (Inoise=0), then both models (C++ as well as Python) give exactly the same result. If I only introduce the noisy component (Istim=0), then both models give results (i.e. natural fluctuations that hardly differ from each other at 1000 runs). However, if I choose Istim=0.000001 and add noise, then the results differ by 30%. How is that possible?
Here is the full code. C++:
#include<math.h>
#include<iostream>
#include<random>
#include<vector>
#include<algorithm>
#include<fstream>
#include<omp.h>
#include <iomanip>
#include <assert.h>
// parameters
constexpr double v_Rest = -65.0;
constexpr double gNa = 1200.0;
constexpr double gK = 360.0;
constexpr double gL = 3.0;
constexpr double vNa = 115.0;
constexpr double vK = -12.0;
constexpr double vL = 10.6;
constexpr double c = 1.0;
constexpr double knoise = 0.0005;
bool print = false;
bool bisection = false;
bool test = true;
// stepsize PFs
constexpr int steps = 5;
double store[steps];
int prob[steps];
double step[steps];
// time constants
constexpr double t_end = 1.0;
constexpr double delay = 0.1;
constexpr double duration = 0.1;
constexpr double dt = 0.0025;
constexpr int t_steps = t_end/dt;
constexpr int runs = 1000;
double voltage[t_steps];
double doubleRand() {
thread_local std::mt19937 engine(std::random_device{}());
std::normal_distribution<double> distribution(0.0, 1.0);
return distribution(engine);
}
double alphaM(const double v){ return 12.0 * ((2.5 - 0.1 * (v)) / (exp(2.5 - 0.1 * (v)) - 1.0)); }
double betaM(const double v){ return 12.0 * (4.0 * exp(-(v) / 18.0)); }
double betaH(const double v){ return 12.0 * (1.0 / (exp(3.0 - 0.1 * (v)) + 1.0)); }
double alphaH(const double v){ return 12.0 * (0.07 * exp(-(v) / 20.0)); }
double alphaN(const double v){ return 12.0 * ((1.0 - 0.1 * (v)) / (10.0 * (exp(1.0 - 0.1 * (v)) - 1.0))); }
double betaN(const double v){ return 12.0 * (0.125 * exp(-(v) / 80.0)); }
double HH_model(const double I, const double area_factor){
const double A = 1.0e-8 * area_factor;
const double C = c*A;
const double v0 = 0.0;
const double m0 = alphaM(v0)/(alphaM(v0)+betaM(v0));
const double h0 = alphaH(v0)/(alphaH(v0)+betaH(v0));
const double n0 = alphaN(v0)/(alphaN(v0)+betaN(v0));
int count = 0;
for(int j=0; j<runs; j++){
double vT = v0;
double mT = m0;
double hT = h0;
double nT = n0;
for(int i=0; i<t_steps; i++){
double IStim = 0.0;
if ((delay / dt <= (double)i) && ((double)i <= (delay + duration) / dt))
IStim = I;
mT = (mT + dt * alphaM(vT)) / (1.0 + dt * (alphaM(vT) + betaM(vT)));
hT = (hT + dt * alphaH(vT)) / (1.0 + dt * (alphaH(vT) + betaH(vT)));
nT = (nT + dt * alphaN(vT)) / (1.0 + dt * (alphaN(vT) + betaN(vT)));
const double iNa = gNa * pow(mT, 3.0) * hT * (vT - vNa);
const double iK = gK * pow(nT, 4.0) * (vT - vK);
const double iL = gL * (vT-vL);
const double Inoise = (doubleRand() * knoise * sqrt(gNa * A));
const double IIon = ((iNa + iK + iL) * A) + Inoise;
vT += ((-IIon + IStim) / C) * dt;
voltage[i] = vT;
if(vT > 60.0) {
count++;
break;
}
}
}
return count;
}
int main(){
std::cout << HH_model(1.0e-6,1) << std::endl;
}
}
Python:
import matplotlib.pyplot as py
import numpy as np
import scipy.optimize as optimize
from tqdm import tqdm
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
# HH parameters
v_Rest = -65 # in mV
gNa = 1200 # in mS/cm^2
gK = 360 # in mS/cm^2
gL = 0.3*10 # in mS/cm^2
vNa = 115 # in mV
vK = -12 # in mV
vL = 10.6 # in mV
#Number of runs
runs = 1000
c = 1 # in uF/cm^2
def alphaM(v): return 12 * ((2.5 - 0.1 * (v)) / (np.exp(2.5 - 0.1 * (v)) - 1))
def betaM(v): return 12 * (4 * np.exp(-(v) / 18))
def betaH(v): return 12 * (1 / (np.exp(3 - 0.1 * (v)) + 1))
def alphaH(v): return 12 * (0.07 * np.exp(-(v) / 20))
def alphaN(v): return 12 * ((1 - 0.1 * (v)) / (10 * (np.exp(1 - 0.1 * (v)) - 1)))
def betaN(v): return 12 * (0.125 * np.exp(-(v) / 80))
def HH_model(I,area_factor):
count = 0
t_end = 1 # in ms
delay = 0.1 # in ms
duration = 0.1 # in ms
dt = 0.0025 # in ms
area_factor = area_factor
I = I
C = c*A # uF
for j in tqdm(range(0, runs), total=runs):
# Introduction of equations and channels
# compute the timesteps
t_steps= t_end/dt+1
# Compute the initial values
v0 = 0
m0 = alphaM(v0)/(alphaM(v0)+betaM(v0))
h0 = alphaH(v0)/(alphaH(v0)+betaH(v0))
n0 = alphaN(v0)/(alphaN(v0)+betaN(v0))
# Allocate memory for v, m, h, n
v = np.zeros((int(t_steps), 1))
m = np.zeros((int(t_steps), 1))
h = np.zeros((int(t_steps), 1))
n = np.zeros((int(t_steps), 1))
# Set Initial values
v[:, 0] = v0
m[:, 0] = m0
h[:, 0] = h0
n[:, 0] = n0
### Noise component
knoise= 0.0005 #uA/(mS)^1/2
### --------- Step3: SOLVE
for i in range(0, int(t_steps)-1, 1):
# Get current states
vT = v[i]
mT = m[i]
hT = h[i]
nT = n[i]
# Stimulus current
IStim = 0
if delay / dt <= i <= (delay + duration) / dt:
IStim = I # in uA
else:
IStim = 0
# Compute change of m, h and n
m[i + 1] = (mT + dt * alphaM(vT)) / (1 + dt * (alphaM(vT) + betaM(vT)))
h[i + 1] = (hT + dt * alphaH(vT)) / (1 + dt * (alphaH(vT) + betaH(vT)))
n[i + 1] = (nT + dt * alphaN(vT)) / (1 + dt * (alphaN(vT) + betaN(vT)))
# Ionic currents
iNa = gNa * m[i + 1] ** 3. * h[i + 1] * (vT - vNa)
iK = gK * n[i + 1] ** 4. * (vT - vK)
iL = gL * (vT-vL)
Inoise = (np.random.normal(0, 1) * knoise * np.sqrt(gNa * A))
IIon = ((iNa + iK + iL) * A) + Inoise #
# Compute change of voltage
v[i + 1] = (vT + ((-IIon + IStim) / C) * dt)[0] # in ((uA / cm ^ 2) / (uF / cm ^ 2)) * ms == mV
# adjust the voltage to the resting potential
v = v + v_Rest
# test if there was a spike
if max(v[:]-v_Rest) > 60:
count += 1
return count
You've messed up indents in the Python code. These lines
m[i + 1] = (mT + dt * alphaM(vT)) / (1 + dt * (alphaM(vT) + betaM(vT)))
h[i + 1] = (hT + dt * alphaH(vT)) / (1 + dt * (alphaH(vT) + betaH(vT)))
n[i + 1] = (nT + dt * alphaN(vT)) / (1 + dt * (alphaN(vT) + betaN(vT)))
do not execute when condition delay / dt <= i <= (delay + duration) / dt is True
After indentation is fixed the Python code produces 866, which nearly matches 876 - result of C++ code.

Understanding timesteps in scipy.integrate.odeint

I am trying to solve a PDE using odeint and the method of lines. My code is definitely wrong - and I'm trying to figure out where it is going wrong.
I am calling the ode solver using odeint(odefunc,y0,tspan) where tspan = np.linspace(0.0, 0.5, 5) & y0 = 1.0*np.ones(3).
I tried printing t within odefunc and am confused by the output. Despite the fact that I am solving up to t=0.5, the last t-value to print is 0.015081203121127767. The number of outputs matches tspan, but I cannot see how it could possibly be solving up to t = 0.5 when the last time in the de function is 0.015. What am I missing?
My DE is time dependent - so this is making it very hard to figure out where things are going wrong because I don't seem to be seeing the times where everything fails.
ETA - this is failing, but running this without some of the irrelevant stuff I am getting the warning ODEintWarning: Excess work done on this call (perhaps wrong Dfun type). Run with full_output = 1 to get quantitative information., which I'm assuming is part of the issue - but it doesn't appear to be halting the code.
MWE
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
import math
import sys
plt.interactive(False)
sigma = 2320
rho = 1000
gravity = 9.81 # [m/s^2]
g = gravity*3600*3600 # [m/hour^2]
S = 0.01
settlingVelocity = 0.02 # [m/s]
ws = settlingVelocity*3600 # [m/hour]
n = 0.04 # [SI]
J = 400 # [Ws/m]
k = 0.02
Cstar = 0.2 * sigma # [kg/m^3]
W = 2 # [m]
D0 = 1.2
Lw = 20
L = 100
tend = 0.5 # in hours
tspan = np.linspace(0.0, tend, 5)
def d(t): # metres
if t < 50: # hours
return 0.5
else:
return 0.05
def Q(t):
return 3600 * (math.sqrt(S)/n)*((W*d(t))**(5/3))/((2*d(t) + W)**(2/3))
def h(t):
return d(t)/2
def beta(t):
return (sigma - rho) * g * h(t)/sigma
def Omega(t):
return rho * g * S * Q(t) # [W/m]
def PsiTime(t):
return rho * g * Q(t) * (D0 - d(t))/(Lw)
N = 10
X = np.linspace(0, L, N)
delX = L/ (N-1)
def odefunc(y, t):
def zetaEh(t):
return k * (PsiTime(t) + Omega(t)) / (J + beta(t))
def zetaEW(t):
return (2*d(t)/(W + 2*d(t))) * k * Omega(t)/(J + beta(t))
def zetaR(t):
return (W/(W + 2*d(t))) * k*Omega(t)/(beta(t))
def zetaEF(t,i):
return (W/(W + 2*d(t))) * k * Omega(t) / (J + beta(t))
C = y[:N]
M = y[N:]
print("time: ", t)
dCdt = np.zeros(X.shape)
dMdt = np.zeros(X.shape)
dCdt[0] = ( # forward difference for dCdx
-Q(t) / (W*d(t)) * (C[1] - C[0]) / delX
+ (zetaEh(t) / (W * d(t))) * ((Cstar - C[0]) / Cstar)
- (ws * C[0] * (beta(t))) / (d(t) * (J + beta(t)))
)
dMdt[0] = 0
# gully channel
for i in range (1, N-1): # central difference
if M[i] + W *C[i] * ws - zetaR(t) * (Cstar - C[i]) / Cstar < 0:
reMass = M[i] + W * C[i] * ws
dCdt[i] = (
-Q(t) / (W*d(t)) * (C[i+1] - C[i - 1]) / (2*delX)
+ 1 / (W * d(t)) * ((zetaEW(t) + zetaEF(t,i)) * (Cstar - C[i]) / Cstar
+ reMass * (1 - (beta(t))/ (J + beta(t))))
- C[i] * ws/d(t)
)
dMdt[i] = -M[i]
else:
dCdt[i] = (
-Q(t) / (W*d(t)) * (C[i+1] - C[i - 1]) / (2*delX)
+ 1 / (W * d(t)) * (zetaEW(t) + zetaR(t)) * (Cstar - C[i]) / Cstar
- C[i] * ws / d(t)
)
dMdt[i] = W * C[i] * ws - zetaR(t) * (Cstar - C[i]) / Cstar
# Final node - backward difference
if M[N-1] + W * C[N-1] * ws - zetaR(t) * (Cstar - C[N-1]) / Cstar < 0:
reMass = M[N-1] + W * C[N-1] * ws
dCdt[N-1] = (
-Q(t) / (W * d(t)) * (C[N-1] - C[N-2]) / delX
+ 1 / (W * d(t)) * ((zetaEW(t) + zetaEF(t, i)) * (Cstar - C[N-1]) / Cstar
+ reMass * (1 - (beta(t)) / (J + beta(t))))
- C[i] * ws / d(t)
)
dMdt[N-1] = -M[N-1]
else:
dCdt[N-1] = (
-Q(t) / (W * d(t)) * (C[N-2] - C[N - 1]) / delX
+ 1 / (W * d(t)) * (zetaEW(t) + zetaR(t)) * (Cstar - C[N-1]) / Cstar
- C[N-1] * ws / d(t)
)
dMdt[N-1] = W * C[N-1] * ws - zetaR(t) * (Cstar - C[N-1]) / Cstar
dydt = np.ravel([dCdt, dMdt])
return dydt
init_C = 0.0 * np.ones(X.shape)
init_M = 0.0 * np.ones(X.shape)
init= np.ravel([init_C, init_M])
sol = odeint(odefunc, init, tspan)
conc = sol[:, :N]

How to Check if Jacobian is Correct

I am happily integrating a simple ODE with scipy.odeint. This is my integration fuction:
def f(self, x, t_0):
h = x[0:3]
f = x[3:6]
der = []
der.append((self.q_0 - f[0] * self.u[0] * self.k[0] * numpy.sign(h[0] - h[1]) * numpy.sqrt(abs(h[0] - h[1]))) / self.A[0])
der.append((f[0] * self.u[0] * self.k[0] * numpy.sign(h[0] - h[1]) * numpy.sqrt(abs(h[0] - h[1])) -
f[1] * self.u[1] * self.k[1] * numpy.sign(h[1]) * numpy.sqrt(abs(h[1]))) / self.A[1])
der.append((f[1] * self.u[1] * self.k[1] * numpy.sign(h[1] - h[2]) * numpy.sqrt(abs(h[1] - h[2])) -
f[2] * self.u[2] * self.k[2] * numpy.sign(h[2]) * numpy.sqrt(abs(h[2]))) / self.A[2])
der.append(0)
der.append(0)
der.append(0)
return numpy.array(der)
Everything works fine if I call scipy.integrate.odeint except I decided to provide the Jacobian so I can do the integration faster. This is the Jacobian which I figured out by hand:
def F(self, x, t_0):
h = x[0:3]
f = x[3:6]
u = self.u
k = self.k
A = self.A
result = numpy.zeros((6, 6))
sqrt_diff_h0_h1 = numpy.sign(h[0] - h[1]) * numpy.sqrt(abs(h[0] - h[1]))
sqrt_diff_h1_h2 = numpy.sign(h[1] - h[2]) * numpy.sqrt(abs(h[1] - h[2]))
sqrt_diff_h1 = numpy.sign(h[1]) * numpy.sqrt(abs(h[1]))
sqrt_diff_h2 = numpy.sign(h[2]) * numpy.sqrt(abs(h[2]))
result[0][0] = -(u[0] * f[0] * k[0]) / (2 * A[0] * sqrt_diff_h0_h1)
result[0][1] = (u[0] * f[0] * k[0]) / (2 * A[0] * sqrt_diff_h0_h1)
result[0][3] = -(u[0] * k[0] * sqrt_diff_h0_h1) / A[0]
result[1][0] = (u[0] * f[0] * k[0]) / (2 * A[1] * sqrt_diff_h0_h1)
result[1][1] = -((u[0] * f[0] * k[0] * A[1]) / (2 * sqrt_diff_h0_h1)) - \
((u[1] * f[1] * k[1] * A[1]) / (2 * sqrt_diff_h1))
result[1][3] = (u[0] * k[0] * sqrt_diff_h0_h1) / A[1]
result[1][4] = -(u[1] * k[1] * sqrt_diff_h1) / A[1]
result[2][1] = (u[1] * f[1] * k[1]) / (2 * A[2] * sqrt_diff_h1_h2)
result[2][2] = -((u[1] * f[1] * k[1] * A[2]) / (2 * sqrt_diff_h1_h2)) - \
((u[2] * f[2] * k[2] * A[2]) / (2 * sqrt_diff_h2))
result[2][4] = (u[1] * k[1] * sqrt_diff_h1_h2) / A[2]
result[2][5] = -(u[2] * k[2] * sqrt_diff_h2) / A[2]
return result
If I supply F to scipy.odeint.integrate, it actually takes more time. This, of course, doesn't mean much but I am wondering how to determine if the Jacobian is correct? I need it for other purposes of linearization.

Categories

Resources