Using finite difference in python - python

I am trying to use Python with Numpy to solve a basic equation using the finite difference method. The code gives me the correct first value for a i.e it gives me a[1]; however, every other value after that is just zero?
I don't know what I'm doing wrong because it obviously works for the first value so how do I fix this?
Any ideas would be very helpful.
from numpy import *
import numpy as np
import matplotlib.pyplot as plt
import scipy as sp
from scipy.integrate import odeint
def solver(omega_m, dt):
#t_0, H_0, a_0, dt, n, T; always the same; omega's change
t_0 = 0.0004
a_0 = 0.001
H_0 = 1./13.7
T = 13.7
dt = float(dt)
n = int(round((T - t_0)/dt))
x = zeros(n+1)
t = linspace(t_0, T, n+1)
x[0] = a_0
for i in range (0, n):
x[i+1] = x[i] + (H_0 * ((omega_m)**(1./2.)) * ((x[i])**(-1./2.)) * dt)
return x, t
a, t = solver(omega_m =1, dt=0.001)
print a, t

Your function returns after the first iteration because your return statement is inside the for loop. You should dedent the return statement so your loop does not terminate prematurely:
for i in range (0, n):
x[i+1] = x[i] + (H_0 * ((omega_m)**(1./2.)) * ((x[i])**(-1./2.)) * dt)
return x, t

Related

How to stop python from automatically converting a float number to a complex number?

I'm creating a function to compute Newton's method. However, the functions f and df keep returning a complex number. I am getting a type error: "can't convert complex to float" at the line where I am defining f.
How do I stop python from returning a complex number and not a float?
from cmath import cos, sin
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
def Newtons_method(f, df, x0, i_max, eps_x, eps_f):
errors = []
conv = 0
xk = x0
for k in range(i_max):
res = f(xk)
approx_err = -1.0 * res / df(xk)
errors.append([k, abs(approx_err), res])
if (abs(approx_err) < eps_x) and (res < eps_f):
array_errors = np.array(errors)
conv = 1
return xk, array_errors, conv
der = df(xk)
if der == 0:
print("Derivative is zero")
return None
xk = xk + approx_err
print("Exceeded maximum iterations")
return None
def f(x):
return float(x * x - 2 * x * cos(x) + cos(x) * cos(x))
def df(x):
return float(2 * x - 2 * (cos(x) - x * sin(x)) - sin(2 * x))
x0 = 1.0
i_max = 50
eps_x = 1.0e-12
eps_f = 1.0
solution_Newton, record_Newton, flag_Newton = Newtons_method(f, df, x0, i_max, eps_x, eps_f)
x = record_Newton[:,0]
y = record_Newton[:,1]
plt.plot(x, y)
plt.yscale("log")
plt.title("Newton's method")
plt.xlabel("Number of iterations")
plt.ylabel("Approximate errors")
plt.show()
I think it is because you are using cmath instead of math:
>>> import cmath
>>> import math
>>> cmath.cos(0)
(1-0j)
>>> math.cos(0)
1.0
The help for cmath starts by explaining that it is for complex numbers:
Help on built-in module cmath:
NAME
cmath
DESCRIPTION
This module provides access to mathematical functions for complex
numbers.
If you don't want to be working with complex numbers, simply replace cmath with math and your error should go away. When I do that your code runs and produces this figure instead of spitting out a TypeError:

How to compute the derivative graph of a Python plot

I am working on the following code, which solves a system of coupled differential equations. I have been able to solve them, and I plotted one of them. I am curious how to compute and plot the derivative of this graph numerically (I know the derivative is given in the first function, but suppose I didn't have that). I was thinking that I could use a for-loop, but is there a faster way?
import numpy as np
from scipy.integrate import odeint
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
import math
def hiv(x,t):
kr1 = 1e5
kr2 = 0.1
kr3 = 2e-7
kr4 = 0.5
kr5 = 5
kr6 = 100
h = x[0] # Healthy Cells -- function of time
i= x[1] #Infected Cells -- function of time
v = x[2] # Virus -- function of time
p = kr3 * h * v
dhdt = kr1 - kr2*h - p
didt = p - kr4*i
dvdt = -p -kr5*v + kr6*i
return [dhdt, didt, dvdt]
print(hiv([1e6, 0, 100], 0))
x0 = [1e6, 0, 100] #initial conditions
t = np.linspace(0,15,1000) #time in years
x = odeint(hiv, x0, t) #vector of the functions H(t), I(t), V(t)
h = x[:,0]
i = x[:,1]
v = x[:,2]
plt.semilogy(t,h)
plt.show()

How to evaluate this line integral (Python-Sympy)

The idea is to compute the line integral of the following vector field and curve:
This is the code I have tried:
import numpy as np
from sympy import *
from sympy import Curve, line_integrate
from sympy.abc import x, y, t
C = Curve([cos(t) + 1, sin(t) + 1, 1 - cos(t) - sin(t)], (t, 0, 2*np.pi))
line_integrate(y * exp(x) + x**2 + exp(x) + z**2 * exp(z), C, [x, y, z])
But the ValueError: Function argument should be (x(t), y(t)) but got [cos(t) + 1, sin(t) + 1, -sin(t) - cos(t) + 1] comes up.
How can I compute this line integral then?
I think that maybe this line integral contains integrals that don't have exact solution. It is also fine if you provide a numerical approximation method.
Thanks
In this case you can compute the integral using line_integrate because we can reduce the 3d integral to a 2d one. I'm sorry to say I don't know python well enough to write the code, but here's the drill:
If we write
C(t) = x(t),y(t),z(t)
then the thing to notice is that
z(t) = 3 - x(t) - y(t)
and so
dz = -dx - dy
So, we can write
F.dr = Fx*dx + Fy*dy + Fz*dz
= (Fx-Fz)*dx + (Fy-Fz)*dy
So we have reduced the problem to a 2d problem: we integrate
G = (Fx-Fz)*i + (Fx-Fz)*j
round
t -> x(t), y(t)
Note that in G we need to get rid of z by substituting
z = 3 - x - y
The value error you receive does not come from your call to the line_integrate function; it comes because according to the source code for the Curve class, only functions in 2D Euclidean space are supported. This integral can still be computed without using sympy according to this research blog that I found by simply searching for a workable method on Google.
The code you need looks like this:
import autograd.numpy as np
from autograd import jacobian
from scipy.integrate import quad
def F(X):
x, y, z = X
return [y * np.exp(x), x**2 + np.exp(x), z**2 * np.exp(z)]
def C(t):
return np.array([np.cos(t) + 1, np.sin(t) + 1, 1 - np.cos(t) - np.sin(t)])
dCdt = jacobian(C, 0)
def integrand(t):
return F(C(t)) # dCdt(t)
I, e = quad(integrand, 0, 2 * np.pi)
The variable I then stores the numerical solution to your question.
You can define a function:
import sympy as sp
from sympy import *
def linea3(f,C):
P = f[0].subs([(x,C[0]),(y,C[1]),(z,C[2])])
Q = f[1].subs([(x,C[0]),(y,C[1]),(z,C[2])])
R = f[2].subs([(x,C[0]),(y,C[1]),(z,C[2])])
dx = diff(C[0],t)
dy = diff(C[1],t)
dz = diff(C[2],t)
m = integrate(P*dx+Q*dy+R*dz,(t,C[3],C[4]))
return m
Then use the example:
f = [x**2*z**2,y**2*z**2,x*y*z]
C = [2*cos(t),2*sin(t),4,0,2*sp.pi]

Multiprocessing python function for numerical calculations

Hoping to get some help here with parallelising my python code, I've been struggling with it for a while and come up with several errors in whichever way I try, currently running the code will take about 2-3 hours to complete, The code is given below;
import numpy as np
from scipy.constants import Boltzmann, elementary_charge as kb, e
import multiprocessing
from functools import partial
Tc = 9.2
x = []
g= []
def Delta(T):
'''
Delta(T) takes a temperature as an input and calculates a
temperature dependent variable based on Tc which is defined as a
global parameter
'''
d0 = (pi/1.78)*kb*Tc
D0 = d0*(np.sqrt(1-(T**2/Tc**2)))
return D0
def element_in_sum(T, n, phi):
D = Delta(T)
matsubara_frequency = (np.pi * kb * T) * (2*n + 1)
factor_d = np.sqrt((D**2 * cos(phi/2)**2) + matsubara_frequency**2)
element = ((2 * D * np.cos(phi/2))/ factor_d) * np.arctan((D * np.sin(phi/2))/factor_d)
return element
def sum_elements(T, M, phi):
'''
sum_elements(T,M,phi) is the most computationally heavy part
of the calculations, the larger the M value the more accurate the
results are.
T: temperature
M: number of steps for matrix calculation the larger the more accurate the calculation
phi: The phase of the system can be between 0- pi
'''
X = list(np.arange(0,M,1))
Y = [element_in_sum(T, n, phi) for n in X]
return sum(Y)
def KO_1(M, T, phi):
Iko1Rn = (2 * np.pi * kb * T /e) * sum_elements(T, M, phi)
return Iko1Rn
def main():
for j in range(1, 92):
T = 0.1*j
for i in range(1, 314):
phi = 0.01*i
pool = multiprocessing.Pool()
result = pool.apply_async(KO_1,args=(26000, T, phi,))
g.append(result)
pool.close()
pool.join()
A = max(g);
x.append(A)
del g[:]
My approach was to try and send the KO1 function into a multiprocessing pool but I either get a Pickling error or a too many files open, Any help is greatly appreciated, and if multiprocessing is the wrong approach I would love any guide.
I haven't tested your code, but you can do several things to improve it.
First of all, don't create arrays unnecessarily. sum_elements creates three array-like objects when it can use just one generator. First, np.arange creates a numpy array, then the list function creates a list object and and then the list comprehension creates another list. The function does 4 times the work it should.
The correct way to implement it (in python3) would be:
def sum_elements(T, M, phi):
return sum(element_in_sum(T, n, phi) for n in range(0, M, 1))
If you use python2, replace range with xrange.
This tip will probably help you in any python script you'll write.
Also, try to utilize multiprocessing better. It seems what you need to do is to create a multiprocessing.Pool object once, and use the pool.map function.
The main function should look like this:
def job(args):
i, j = args
T = 0.1*j
phi = 0.01*i
return K0_1(26000, T, phi)
def main():
pool = multiprocessing.Pool(processes=4) # You can change this number
x = [max(pool.imap(job, ((i, j) for i in range(1, 314)) for j in range(1, 92)]
Notice that I used a tuple in order to pass multiple arguments to job.
This is not an answer to the question, but if I may, I would propose how to speed up the code using simple numpy array operations. Have a look at the following code:
import numpy as np
from scipy.constants import Boltzmann, elementary_charge as kb, e
import time
Tc = 9.2
RAM = 4*1024**2 # 4GB
def Delta(T):
'''
Delta(T) takes a temperature as an input and calculates a
temperature dependent variable based on Tc which is defined as a
global parameter
'''
d0 = (np.pi/1.78)*kb*Tc
D0 = d0*(np.sqrt(1-(T**2/Tc**2)))
return D0
def element_in_sum(T, n, phi):
D = Delta(T)
matsubara_frequency = (np.pi * kb * T) * (2*n + 1)
factor_d = np.sqrt((D**2 * np.cos(phi/2)**2) + matsubara_frequency**2)
element = ((2 * D * np.cos(phi/2))/ factor_d) * np.arctan((D * np.sin(phi/2))/factor_d)
return element
def KO_1(M, T, phi):
X = np.arange(M)[:,np.newaxis,np.newaxis]
sizeX = int((float(RAM) / sum(T.shape))/sum(phi.shape)/8) #8byte
i0 = 0
Iko1Rn = 0. * T * phi
while (i0+sizeX) <= M:
print "X = %i"%i0
indices = slice(i0, i0+sizeX)
Iko1Rn += (2 * np.pi * kb * T /e) * element_in_sum(T, X[indices], phi).sum(0)
i0 += sizeX
return Iko1Rn
def main():
T = np.arange(0.1,9.2,0.1)[:,np.newaxis]
phi = np.linspace(0,np.pi, 361)
M = 26000
result = KO_1(M, T, phi)
return result, result.max()
T0 = time.time()
r, rmax = main()
print time.time() - T0
It runs a bit more than 20sec on my PC. One has to be careful not to use too much memory, that is why there is still a loop with a bit complicated construction to use only pieces of X. If enough memory is present, then it is not necessary.
One should also note that this is just the first step of speeding up. Much improvement could be reached still using e.g. just in time compilation or cython.

Differential equations in Python: Single time step

I would like to be able to do something else in each time step during solving an ODE (using scipy's integrate). Is there a way to do so? Can I somehow write my own time loop and just call a single e.g. Runge-Kutta step by myself? Is there a routine in python or would I have to come up with my own? I think there must be one since odeint etc have to use such a function. So the question is, how do I access them?
So it should looking something along these lines:
from scipy.integrate import *
from pylab import *
def deriv(y, t):
a = -2.0
b = -0.1
return array([y[1], a*y[0]+b*y[1]])
time = linspace(0.0, 10.0, 1000)
dt = 10.0/(1000-1)
yinit = array([0.0005, 0.2])
for t in time:
# doSomething, write into a file or whatever
y[t] = yinit
yinit = RungeKutta(deriv, yinit, t, dt, varargs)
I now came along with this:
from pylab import *
from scipy.integrate import *
def RHS(t, x):
return -x
min_t = 0.0
max_t = 10.0
num_t = 1e2
grid_t = linspace(min_t, max_t, num_t)
grid_dt = (max_t - min_t)/(num_t - 1)
y = zeros(num_t, dtype=complex)
y[0] = complex(1.0, 0.0)
solver = complex_ode(RHS)
solver.set_initial_value(y[0], grid_t[0]).set_integrator('dopri5')
for idx in range(1, int(num_t)):
solver.integrate(solver.t + grid_dt)
y[idx] = solver.y[0]
In here I can do whatever I want during the integration.

Categories

Resources