我有一个耦合方程系统:静水平衡方程,质量连续性方程和理想气体的状态方程。这些是数学语法,
\frac{dP}{dr}=- \rho*g
,其中\rho
是密度,g
是重力加速度。
\frac{dM}{dr}=4*pi* r^2*\rho
和
p=\rho* k_B* T/(\mu *m_p)
,其中k_B
是boltzmann常数,\mu
是平均分子量,m_p
是质子质量。
我想使用Runge-Kutta数值技术解决这些耦合方程,我在这里展示了我为解决这个问题而设计的python代码:
from scipy.constants import m_p,G,k,pi
from pylab import *
#mu may be changed for different molecular composition:
mu=2
def g(r_atm, p_atm):
T=165
return 4*pi*r_atm**2*mu*m_p*p_atm/(k*T)
def f(r_atm,p_atm, m_atm):
T=165
return -mu*m_p*p_atm*G*m_atm/(k*T*r_atm**2)
def rk4_1(g,f, r0, p0,m0, r1, n):
r_atm = [0]*(n + 1)
p_atm = [0]*(n + 1)
m_atm=[0]*(n + 1)
h = (r1 - r0)/n
# h=-20
r_atm[0]=r0
p_atm[0]=p0
m_atm[0]=m0
for i in range(0,10000000):
if p_atm[i]<100000:
k0 = h*g(r_atm[i], p_atm[i])
l0 = h*f(r_atm[i], p_atm[i], m_atm[i])
k1 = h*g(r_atm[i] + 0.5*h, p_atm[i] + 0.5*k0)
l1 = h*f(r_atm[i] + 0.5*h, p_atm[i] + 0.5*l0, m_atm[i]+0.5*k0)
k2 = h*g(r_atm[i] + 0.5*h, p_atm[i] + 0.5*k1)
l2 = h*f(r_atm[i] + 0.5*h, p_atm[i] + 0.5*l1, m_atm[i]+0.5*k1)
k3 = h*g(r_atm[i] + h, p_atm[i] + k2)
l3 = h*f(r_atm[i] + h, p_atm[i] + l2, m_atm[i]+k2)
r_atm[i+1] = r0 + (i+1)*h
p_atm[i+1] = p_atm[i] + (l0 + 2*l1 + 2*l2 + l3)/6
m_atm[i+1] = m_atm[i] + (k0 + 2*k1 + 2*k2 + k3)/6
else:
break
return h, r_atm, p_atm, m_atm
h, r_atm, p_atm, m_atm = rk4_1(g,f, 6.991e7, 1e-6*1e5, 1.898e27, 2.0e7,10000000) #bar to pascals (*1e5)
对于压力的初始条件p_atm
,半径,r_atm
和质量,m_atm
,我使用h, r_atm, p_atm, m_atm = rk4_1(g,f, 6.991e7, 1e-6*1e5, 1.898e27, 2.0e7,10000000)
中显示的值。请注意,我正在从高层大气(初始条件已经给出)接近这个边界值问题并且在大气层中向下行进(注意h是负的)。我的目的是评估从10^-1
帕斯卡到100000
帕斯卡的这种数值积分。我从运行此代码得到的结果是,压力只是分三个步骤飙升到~1e+123
,所以显然有一些非常错误的流式传输,但这将有助于另一个眼睛或视角,因为这是我第一次表演Runga-Kutta方法。
答案 0 :(得分:1)
As Wolph says, dividing by n
might simply give you h=0
, depending on which version of Python you're using. If you're using 2.x, you should include from __future__ import division
in the beginning, or handle the division in some other way (e.g., divide by float(n)
). (Oh, and I guess perhaps you also intended to use n
in your loop, rather than hard-coding range(0,10000000)
? And there are a couple of indentation errors in the code as it stands, but I guess that's just from posting it here.)
This doesn't seem to be the main problem, though. You say you get a high pressure early; when I run it, it gets really low? Even with proper divisions, I get p_atm[3] = -2.27e+97
, and from that, I start getting infinities (inf
and -inf
) and nan
s.
It's hard, without knowing the specific problem better, to see if there's an error in your implementation, or if this is simply a matter of numerical instability. It looks right to me, but I may very well have missed something (sort of hard to read.) If this is your first time with Runge–Kutta, I'd strongly suggest using an existing implementation rather than trying to get it right yourself. Numerical computation and avoiding floating-point issues can be quite challenging. You're already using scipy
— why not use their implementation of the R–K method, or related numerical integration solutions? have a look at scipy.integrate, for example. If nothing else, if the scipy
integrators can't solve your problem, at least you know more about what your challenges are.
答案 1 :(得分:1)
这是一个使用小数btw的版本,它看起来效果稍好:
from decimal import Decimal as D
from scipy.constants import m_p,G,k,pi
m_p = D(m_p)
G = D(G)
k = D(k)
pi = D(pi)
# mu may be changed for different molecular composition:
mu = D(2)
def g(r_atm, p_atm):
T = D(165)
return D(4) * pi * r_atm ** D(2) * mu * m_p * p_atm/(k * T)
def f(r_atm,p_atm, m_atm):
T = D(165)
return -mu * m_p * p_atm * G * m_atm/(k * T * r_atm ** D(2))
def rk4_1(g,f, r0, p0,m0, r1, n):
r_atm = [D(0)] * (n + 1)
p_atm = [D(0)] * (n + 1)
m_atm = [D(0)] * (n + 1)
h = (r1 - r0) / n
# h = -20
r_atm[0] = r0
p_atm[0] = p0
m_atm[0] = m0
for i in range(0, 10000000):
if p_atm[i] < 100000:
k0 = h * g(r_atm[i], p_atm[i])
l0 = h * f(r_atm[i], p_atm[i], m_atm[i])
k1 = h * g(r_atm[i] + D('0.5') * h, p_atm[i] + D('0.5') * k0)
l1 = h * f(r_atm[i] + D('0.5') * h, p_atm[i] + D('0.5') * l0,
m_atm[i]+D('0.5') * k0)
k2 = h * g(r_atm[i] + D('0.5') * h, p_atm[i] + D('0.5') * k1)
l2 = h * f(r_atm[i] + D('0.5') * h, p_atm[i] + D('0.5') * l1,
m_atm[i]+D('0.5') * k1)
k3 = h * g(r_atm[i] + h, p_atm[i] + k2)
l3 = h * f(r_atm[i] + h, p_atm[i] + l2, m_atm[i]+k2)
r_atm[i + 1] = r0 + (i + 1) * h
p_atm[i + 1] = p_atm[i] + (l0 + D('2') * l1 + D('2') * l2 +
l3)/D('6')
m_atm[i + 1] = m_atm[i] + (k0 + D('2') * k1 + D('2') * k2 + k3)/D('6')
else:
break
return h, r_atm, p_atm, m_atm
h, r_atm, p_atm, m_atm = rk4_1(
g,
f,
D('6.991e7'),
D('1e-6') * D('1e5'),
D('1.898e27'),
D('2.0e7'),
10000000,
) # bar to pascals (*1e5)
print 'h', h