Suggestions on matlab/python conversion [closed] - python

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I'm trying to translate the matlab code below to a python code. The code calculates numerical the para-state of a deuterium molecule and then plots the result. When I try to translate it to python, it seems that I get stuck in a nested for-loop which calculates a sum. I have been searching on the internet the past days yet without success.
Because it's a physics code I will mention some aspect from the code. So first we calculate the partition function (Z). After that there is a calculation of the energy which is a partial derivative of ln(Z) to beta. From this we can calculate the specific heat (approximately) as the derivative of energy to temperature.
So the matlab code looks like this:
epsilon = 0.0038*1.60217662*10^-19;
k = 1.38*10^-23;
T = 1:.1:2000;
beta = 1./(k*T);
%partitionfunction
clear Z Zodd;
for i = 1:length(T)
clear p;
for s = 1:2:31;
a = 2*s+1;
b = s^2+s;
p(s) = 3*a*exp(-b*epsilon*beta(i));
end
Zodd(i) = sum(p);
end
%energy
ln_Zodd = log(Zodd);
for i = 1 : (length(T)-1)
Epara(i) = -(ln_Zodd(i+1)-ln_Zodd(i))/(beta(i+1)-beta(i));
end
%heat capacity
for i = 1 : (length(T)-2)
Cpara(i) = (Epara(i+1)-Epara(i))/(T(i+1)-T(i));
end
%plot
x = k*T/epsilon;
plot(x(1:6000),Cpara(1:6000)/k, 'r');
axis([0 7 0 1.5]);
ylabel('C_v/k');
xlabel('kT/eps');
The corresponding python code:
import numpy as np
import matplotlib.pyplot as plt
import math
epsilon=0.0038*1.60217662*10**-19
k = 1.38*10**-23
T = np.arange(1,2000,0.1)
beta = 1/(k*T)
#partitionfunction
for i in np.arange(1,len(T)):
for s in np.arange(1,31,2):
p[s] = 3*(2*s+1)*math.exp(-(s**2+s)*epsilon*beta(i))
Zodd[i] = sum(p)
#energy
ln_Zodd = math.log(Zodd)
for i in np.arange(1,(len(T) - 1)):
Epara[i]=- (ln_Zodd(i + 1) - ln_Zodd(i)) / (beta(i + 1) - beta(i))
#heat capacity
for i in np.arange(1,(len(T) - 2)):
Cpara[i]=(Epara(i + 1) - Epara(i)) / (T(i + 1) - T(i))
#plot
x = k*T/epsilon
plt.plot(x(np.arange(1,6000)),Cpara(np.arange(1,6000)) / k,'r')
plt.axis([0, 7, 0, 1.5])
plt.ylabel('C_v/k')
plt.xlabel('kT/eps')
plt.show()
This should be the easiest way to calculate (approximate) this problem because the analytic expression is way more involved. I'm new to python so any suggestions or corrections are appreciated.

I agree with #rayryeng that this question is off-topic. However, as I'm interested in matlab, python, and theoretical physics, I took the time to look through your code.
There are multiple syntactical problems with it, and multiple semantical ones as well. Arrays should always be accessed by [] in python, often you try to use (). And the natural indexing of arrays starts from 0, unlike matlab.
Here's a syntactically and semantically corrected version of your original code:
import numpy as np
import matplotlib.pyplot as plt
#import math #use np.* if you have it already imported
epsilon=0.0038*1.60217662*10**-19
k = 1.38*10**-23
T = np.arange(1,2000,0.1)
beta = 1.0/(k*T) #changed to 1.0 for safe measure; redundant
#partitionfunction
svec=np.arange(1,31,2)
p=np.zeros(max(svec)) #added pre-allocation
Zodd=np.zeros(len(T)) #added pre-allocation
for i in np.arange(len(T)): #changed to index Zodd from 0
for s in svec: #changed to avoid magic numbers
p[s-1] = 3*(2*s+1)*np.exp(-(s**2+s)*epsilon*beta[i]) #changed to index p from 0; changed beta(i) to beta[i]; changed to np.exp
Zodd[i] = sum(p)
#energy
ln_Zodd = np.log(Zodd) #changed to np.log
Epara=np.zeros(len(T)-2) #added pre-allocation
for i in np.arange(len(T) - 2): #changed to index Epara from 0
Epara[i]=- (ln_Zodd[i + 1] - ln_Zodd[i]) / (beta[i + 1] - beta[i]) #changed bunch of () to []
#heat capacity
Cpara=np.zeros(len(T)-3) #added pre-allocation
for i in np.arange(len(T) - 3): #changed to index Cpara from 0
Cpara[i]=(Epara[i + 1] - Epara[i]) / (T[i + 1] - T[i])
#plot
x = k*T/epsilon
plt.plot(x[:6000],Cpara[:6000] / k,'r') #fixed and simplified array indices
plt.axis([0, 7, 0, 1.5])
plt.ylabel('C_v/k')
plt.xlabel('kT/eps')
plt.show()
Take the time to look through the comments I made, they are there to instruct you. If something is not clear, please ask for clarification:)
However, this code is far from efficient. Especially your double loop takes a long time to run (which might explain why you think it hung). So I also made it very numpy-based.
Here's the result:
import numpy as np
import scipy.constants as consts
import matplotlib.pyplot as plt
epsilon=0.0038*consts.eV #changed eV
k = consts.k #changed
T = np.arange(1,2000,0.1)
beta = 1.0/(k*T) #changed to 1.0 for safe measure; redundant
#partitionfunction
s=np.arange(1,31,2)[:,None]
Zodd = (3*(2*s+1)*np.exp(-(s**2+s)*epsilon*beta)).sum(axis=0)
#energy
ln_Zodd = np.log(Zodd) #changed to np.log
#Epara = - (ln_Zodd[1:]-ln_Zodd[:-1])/(beta[1:]-beta[:-1]) #manual version
Epara = - np.diff(ln_Zodd)/np.diff(beta)
#heat capacity
Cpara=np.diff(Epara)/np.diff(T)[:-1]
#plot
x = k*T/epsilon
plt.plot(x[:len(Cpara)],Cpara / k,'r') #fixed and simplified array indices
plt.axis([0, 7, 0, 1.5])
plt.ylabel('C_v/k')
plt.xlabel('kT/eps')
plt.show()
Again, please review the changes made. I made use of the scipy.constants module to import physical constants to high precision. I also made use of array broadcasting, which allowed me to turn your double loop into a sum of a matrix along one of its dimensions (just like how you should have done it in matlab; your original matlab code is also far from efficient).
Here's the common result:
You can see that it seems right: at high temperature you get the Dulong--Petit behaviour, and at T->0 we get the zero limit in accordance with the third law of thermodynamics. The heat capacity decays exponentially, but this should make sense since you have a finite energy gap.

Related

Plancks Law, Frequency figures

I want to plot the frequency version of planck's law. I first tried to do this independently:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
%matplotlib inline
# Planck's Law
# Constants
h = 6.62607015*(10**-34) # J*s
c = 299792458 # m * s
k = 1.38064852*(10**-23) # J/K
T = 20 # K
frequency_range = np.linspace(10**-19,10**19,1000000)
def plancks_law(nu):
a = (2*h*nu**3) / (c**2)
e_term = np.exp(h*nu/(k*T))
brightness = a /(e_term - 1)
return brightness
plt.plot(frequency_range,plancks_law(frequency_range))
plt.gca().set_xlim([1*10**-16 ,1*10**16 ])
plt.gca().invert_xaxis()
This did not work, I have an issue with scaling somehow. My next idea was to attempt to use this person's code from this question: Plancks Formula for Blackbody spectrum
import matplotlib.pyplot as plt
import numpy as np
h = 6.626e-34
c = 3.0e+8
k = 1.38e-23
def planck_f(freq, T):
a = 2.0*h*(freq**3)
b = h*freq/(k*T)
intensity = a/( (c**2 * (np.exp(b) - 1.0) ))
return intensity
# generate x-axis in increments from 1nm to 3 micrometer in 1 nm increments
# starting at 1 nm to avoid wav = 0, which would result in division by zero.
wavelengths = np.arange(1e-9, 3e-6, 1e-9)
frequencies = np.arange(3e14, 3e17, 1e14, dtype=np.float64)
intensity4000 = planck_f(frequencies, 4000.)
plt.gca().invert_xaxis()
This didn't work, because I got a divide by zero error. Except that I don't see where there is a division by zero, the denominator shouldn't ever be zero since the exponential term shouldn't ever be equal to one. I chose the frequencies to be the conversions of the wavelength values from the example code.
Can anyone help fix the problem or explain how I can get planck's law for frequency instead of wavelength?
You can not safely handle such large numbers; even for comparably "small" values of b = h*freq/(k*T) your float64 will overflow, e.g np.exp(709.)=8.218407461554972e+307 is ok, but np.exp(710.)=inf. You'll have to adjust your units (exponents) accordingly to avoid this!
Note that this is also the case in the other question you linked to, if you insert print( np.exp(b)[:10] ) within the definition of planck(), you can examine the first ten evaluated b's and you'll see the overflow in the first few occurrences. In any case, simply use the answer posted within the other question, but convert the x-axis in plt.plot(wavelengths, intensity) to frequency (i hope you know how to get from one to the other) :-)

Precision Matlab and Python (numpy)

I'm converting a Matlab script to Python and I am getting different results in the 10**-4 order.
In matlab:
f_mean=f_mean+nanmean(f);
f = f - nanmean(f);
f_t = gradient(f);
f_tt = gradient(f_t);
if n_loop==1
theta = atan2( sum(f.*f_tt), sum(f.^2) );
end
theta = -2.2011167e+03
In Python:
f_mean = f_mean + np.nanmean(vel)
vel = vel - np.nanmean(vel)
firstDerivative = np.gradient(vel)
secondDerivative = np.gradient(firstDerivative)
if numberLoop == 1:
theta = np.arctan2(np.sum(vel * secondDerivative),
np.sum([vel**2]))
Although first and secondDerivative give the same results in Python and Matlab, f_mean is slightly different: -0.0066412 (Matlab) and -0.0066414 (Python); and so theta: -0.4126186 (M) and -0.4124718 (P). It is a small difference, but in the end leads to different results in my scripts.
I know some people asked about this difference, but always regarding std, which I get, but not regarding mean values. I wonder why it is.
One possible source of the initial difference you describe (between means) could be numpy's use of pairwise summation which on large arrays will typically be appreciably more accurate than the naive method:
a = np.random.uniform(-1, 1, (10**6,))
a = np.r_[-a, a]
# so the sum should be zero
a.sum()
# 7.815970093361102e-14
# use cumsum to get naive summation:
a.cumsum()[-1]
# -1.3716805469243809e-11
Edit (thanks #sascha): for the last word and as a "provably exact" reference you could use math.fsum:
import math
math.fsum(a)
# 0.0
Don't have matlab, so can't check what they are doing.

solving 1D Schrödinger equation with Numerov method (python)

Good evening.
I'm currently trying to solve the 1D Schrödinger eq. (time independent) with the Numerov method. The derivation of the method is clear to me but I have some problems with the implementation. I tried to look for solutions on google, and there are some (like this one or this one), but I don't really understand what they are doing in their codes...
The Problem:
With some math you can get the equation to this form:
where . For the beginning I'd like to look at the potential V(x)=1 if -a<x<a.
Since I don't have values for the energy or the first values of Psi (which are needed to start the algorithm) I just guessed some...
The code looks like this:
import numpy as np
import matplotlib.pyplot as plt
from scipy.constants import hbar
m= 1e-27
E= 0.5
def numerov_step(psi_1,psi_2,k1,k2,k3,h):
#k1=k_(n-1), k2=k_n, k3=k_(n+1)
#psi_1 = psi_(n-1) and psi_2=psi_n
m = 2*(1-5/12. * h**2 * k2**2)*psi_2
n = (1+1/12.*h**2*k1**2)*psi_1
o = 1 + 1/12. *h**2 *k3**2
return (m-n)/o
def numerov(N,x0,xE,a):
x,dx = np.linspace(x0,xE,N+1,retstep=True)
def V(x,a):
if (np.abs(x)<a):
return 1
else:
return 0
k = np.zeros(N+1)
for i in range(len(k)):
k[i] = 2*m*(E-V(x[i],a))/hbar**2
psi= np.zeros(N+1)
psi[0]=0
psi[1]=0.1
for j in np.arange(2,N):
psi[j+1]= numerov_step(psi[j],psi[j+1],k[j-1],k[j],k[j+1],dx)
return psi
x0 =-10
xE = 10
N =1000
psi=numerov(N,x0,xE,3)
x = np.linspace(x0,xE,N+1)
plt.figure()
plt.plot(x,psi)
plt.show()
Since the plot doesn't look like a wavefunction at all something has to be wrong, but I'm having trobule to find out what it is.. Would be nice if someone could help a little.
Thanks Sito
Unfortunately I don't quite remember the quantum physics so I don't understand some details. Still I see some bugs in your code:
Why inside numerov_step you square k1, k2 and k3?
In your main cycle
for j in np.arange(2,N):
psi[j+1]= numerov_step(psi[j],psi[j+1],k[j-1],k[j],k[j+1],dx)
you messed up with indices. It looks like this line should be
for j in np.arange(2, N):
psi[j] = numerov_step(psi[j - 2], psi[j - 1], k[j - 2], k[j - 1], k[j], dx)
This is the part I don't really understand. Looking into animation at your first link it looks like this equation has good solutions only for certain combinations of V(x) and E and in other cases it quickly goes wild. It looks like both your V(x) and proportion of E to hbar and V(x) are quite different from the referenced articles and this might be one more reason why the solution goes wild.

1D Random Walk from Matlab to Python

I have a Matlab code that generates a 1D random walk.
%% probability to move up or down
prob = [0.05, 0.95];
start = 2; %% start with 2
positions(1) = start;
for i=2:1000
rr = rand(1);
down = rr<prob(1) & positions(i-1)>1;
up = rr>prob(2) & positions(i-1)<4;
positions(i) = positions(i-1)-down + up;
figure(1), clf
plot(positions)
This gives me the plot below 1D Random Walk with Matlab
I need to try to translate this in Python and I have came up with this (using numpy):
import random
import numpy as np
import matplotlib.pyplot as plt
prob = [0.05, 0.95] ##probability to move up or down
N = 100 ##length of walk
def randomWalk(N):
positions=np.zeros(N)
start = 2 ##Start at 2
positions[0] = start
for i in range(1,100):
rr = random.randint(0,1)
if rr<prob[0] and positions[i-1]>1:
start -= 1
elif rr>prob[1] and positions[i-1]<4:
start += 1
positions[i] = start
return positions
plt.plot(randomWalk(N))
plt.show()
It looks fairly close to what I want (see figure below):1D Random Walk with Python
But I wonder if they are really equivalent, because they do seem different: The Python code seems spikier than the Matlab one.
What is missing in my Python code to achieve the perfect stepwise increase/decrease (similar to the Matlab code)? Maybe it needs an "else" that tells it to stay the same unless the two conditions are met. How do I implement that?
You are doing a bunch of things differently.
For one, you are using rand in MATLAB, which returns a random float between 0 and 1. In python, you are using randint, which returns a random integer. You are doing randint(0, 1), which means "a random integer from 0 to 1, not including 0". So it will always be 1. You want random.random(), which returns a random float between 0 and 1.
Next, you are computing down and up in MATLAB, but in Python you are computing down or up in Python. For your particular case of probabilities these end up having the same result, but they are syntactically different. You can use an almost identical syntax to MATLAB for Python in this case.
Finally, you are calculating a lot more samples for MATLAB than Python (about a factor of 10 more).
Here is a direct port of your MATLAB code to Python. The result for me is pretty much the same as your MATLAB example (with different random numbers, of course):
import random
import matplotlib.pyplot as plt
prob = [0.05, 0.95] # Probability to move up or down
start = 2 #Start at 2
positions = [start]
for _ in range(1, 1000):
rr = random.random()
down = rr < prob[0] and positions[-1] > 1
up = rr > prob[1] and positions[-1] < 4
positions.append(positions[-1] - down + up)
plt.plot(positions)
plt.show()
If speed is an issue you can probably speed this up by using np.random.random(1000) to generate the random numbers up-front, and do the probability comparisons up-front as well in a vectorized manner.
So something like this:
import random
import numpy as np
import matplotlib.pyplot as plt
prob = [0.05, 0.95] # Probability to move up or down
start = 2 #Start at 2
positions = [start]
rr = np.random.random(1000)
downp = rr < prob[0]
upp = rr > prob[1]
for idownp, iupp in zip(downp, upp):
down = idownp and positions[-1] > 1
up = iupp and positions[-1] < 4
positions.append(positions[-1] - down + up)
plt.plot(positions)
plt.show()
Edit: To explain a bit more about the second example, basically what I am doing is pre-computing whether the probability is below the first threshold or above the second for every step ahead of time. This is much faster than computing a random sample and doing the comparison at each step of the loop. Then I am using zip to combine those two random sequences into one sequence where each element is the pair of corresponding elements from the two sequences. This is assuming python 3, if you are using python 2 you should use itertools.izip instead of zip.
So it is roughly equivalent to this:
import random
import numpy as np
import matplotlib.pyplot as plt
prob = [0.05, 0.95] # Probability to move up or down
start = 2 #Start at 2
positions = [start]
rr = np.random.random(1000)
downp = rr < prob[0]
upp = rr > prob[1]
for i in range(len(rr)):
idownp = downp[i]
iupp = upp[i]
down = idownp and positions[-1] > 1
up = iupp and positions[-1] < 4
positions.append(positions[-1] - down + up)
plt.plot(positions)
plt.show()
In python, it is generally preferred to iterate over values, rather than indexes. There is pretty much never a situation where you need to iterate over an index. If you find yourself doing something like for i in range(len(foo)):, or something equivalent to that, you are almost certainly doing something wrong. You should either iterate over foo directly, or if you need the index for something else you can use something like for i, ifoo in enumerate(foo):, which gets you both the elements of foo and their indexes.
Iterating over indexes is common in MATLAB because of various limitations in the MATLAB language. It is technically possible to do something similar to what I did in that Python example in MATLAB, but in MATLAB it requires a lot of boilerplate to be safe and will be extremely slow in most cases. In Python, however, it is the fastest and cleanest approach.

Working out an equation

I'm trying to solve a differential equation numerically, and am writing an equation that will give me an array of the solution to each time point.
import numpy as np
import matplotlib.pylab as plt
pi=np.pi
sin=np.sin
cos=np.cos
sqrt=np.sqrt
alpha=pi/4
g=9.80665
y0=0.0
theta0=0.0
sina = sin(alpha)**2
second_term = g*sin(alpha)*cos(alpha)
x0 = float(raw_input('What is the initial x in meters?'))
x_vel0 = float(raw_input('What is the initial velocity in the x direction in m/s?'))
y_vel0 = float(raw_input('what is the initial velocity in the y direction in m/s?'))
t_f = int(raw_input('What is the maximum time in seconds?'))
r0 = x0
vtan = sqrt(x_vel0**2+y_vel0**2)
dt = 1000
n = range(0,t_f)
r_n = r0*(n*dt)
r_nm1 = r0((n-1)*dt)
F_r = ((vtan**2)/r_n)*sina-second_term
r_np1 = 2*r_n - r_nm1 + dt**2 * F_r
data = [r0]
for time in n:
data.append(float(r_np1))
print data
I'm not sure how to make the equation solve for r_np1 at each time in the range n. I'm still new to Python and would like some help understanding how to do something like this.
First issue is:
n = range(0,t_f)
r_n = r0*(n*dt)
Here you define n as a list and try to multiply the list n with the integer dt. This will not work. Pure Python is NOT a vectorized language like NumPy or Matlab where you can do vector multiplication like this. You could make this line work with
n = np.arange(0,t_f)
r_n = r0*(n*dt),
but you don't have to. Instead, you should move everything inside the for loop to do the calculation at each timestep. At the present point, you do the calculation once, then add the same only result t_f times to the data list.
Of course, you have to leave your initial conditions (which is a key part of ODE solving) OUTSIDE of the loop, because they only affect the first step of the solution, not all of them.
So:
# Initial conditions
r0 = x0
data = [r0]
# Loop along timesteps
for n in range(t_f):
# calculations performed at each timestep
vtan = sqrt(x_vel0**2+y_vel0**2)
dt = 1000
r_n = r0*(n*dt)
r_nm1 = r0*((n-1)*dt)
F_r = ((vtan**2)/r_n)*sina-second_term
r_np1 = 2*r_n - r_nm1 + dt**2 * F_r
# append result to output list
data.append(float(r_np1))
# do something with output list
print data
plt.plot(data)
plt.show()
I did not add any piece of code, only rearranged your lines. Notice that the part:
n = range(0,t_f)
for time in n:
Can be simplified to:
for time in range(0,t_f):
However, you use n as a time variable in the calculation (previously - and wrongly - defined as a list instead of a single number). Thus you can write:
for n in range(0,t_f):
Note 1: I do not know if this code is right mathematically, as I don't even know the equation you're solving. The code runs now and provides a result - you have to check if the result is good.
Note 2: Pure Python is not the best tool for this purpose. You should try some highly optimized built-ins of SciPy for ODE solving, as you have already got hints in the comments.

Categories