我试图将下面的matlab代码翻译成python代码。该代码计算氘分子的对位状态的数值,然后绘制结果。当我尝试将其转换为python时,似乎我陷入了嵌套的for循环中,它计算了一个总和。过去几天我一直在网上搜索但没有成功。
因为它是物理代码我会从代码中提到一些方面。所以首先我们计算分区函数(Z)。之后计算能量,它是ln(Z)对β的偏导数。由此我们可以计算比热(大约)作为能量与温度的导数。
所以matlab代码如下所示:
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');
相应的python代码:
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()
这应该是计算(近似)此问题的最简单方法,因为分析表达式更复杂。我是python的新手,所以任何建议或更正都会受到赞赏。
答案 0 :(得分:2)
我同意@rayryeng这个问题是偏离主题的。但是,由于我对matlab,python,和理论物理感兴趣,我花了很多时间来查看你的代码。
它有多个语法问题,也有多个语义问题。数组中的[]
总是应该访问数组,通常你会尝试使用()
。与matlab不同,数组的自然索引从0开始。
这是原始代码的语法和语义修正版本:
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()
花时间浏览我所做的评论,他们会在那里指导你。如果问题不明确,请要求澄清:)
但是,这段代码效率很低。特别是你的双循环需要很长时间才能运行(这可能解释了为什么你认为它挂了)。所以我也非常基于numpy
。
结果如下:
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()
再次,请查看所做的更改。我使用scipy.constants
模块将物理常量导入到高精度。我还使用了数组广播,它允许我将双循环转换为沿其中一个维度的矩阵之和(就像你应该在matlab中完成它一样;你原来的matlab代码也远没有效率)。
这是常见的结果:
你可以看到它似乎是正确的:在高温下你得到了Dulong - Petit行为,在T->0
我们根据热力学第三定律获得了零限制。热容量呈指数衰减,但这应该是有意义的,因为你的能隙有限。