Python 使用scipy.integrate对向量场(numpy数组)进行积分

Python 使用scipy.integrate对向量场(numpy数组)进行积分,python,scipy,interpolation,pde,numerical-integration,Python,Scipy,Interpolation,Pde,Numerical Integration,我感兴趣的是使用scipy.integrate库对给定初始点的向量场进行积分(即寻找流线)。由于向量场是在计算网格上定义的numpy.ndarray对象,因此必须对网格点之间的值进行插值。有任何集成商处理这个问题吗?也就是说,如果我尝试下面的方法 import numpy as np import scipy.integrate as sc vx = np.random.randn(10,10) vy = np.random.randn(10,10) def f(x,t): return

我感兴趣的是使用
scipy.integrate
库对给定初始点的向量场进行积分(即寻找流线)。由于向量场是在计算网格上定义的
numpy.ndarray
对象,因此必须对网格点之间的值进行插值。有任何集成商处理这个问题吗?也就是说,如果我尝试下面的方法

import numpy as np
import scipy.integrate as sc
vx = np.random.randn(10,10)
vy = np.random.randn(10,10)
def f(x,t):
    return [vx[x[0],x[1]], vy[x[0],x[1]]] # which obviously does not work if x[i] is a float
p0 = (0.5,0.5)
dt = 0.1
t0 = 0
t1 = 1
t = np.arange(t0,t1+dt,dt)
sc.odeint(f,p0,t)
编辑:

我需要返回周围网格点的向量场的插值:

def f(x,t):
    im1 = int(np.floor(x[0]))
    ip1 = int(np.ceil(x[1]))
    jm1 = int(np.floor(x[0]))
    jp1 = int(np.ceil(x[1]))
    if (im1 == ip1) and (jm1 == jp1):
        return [vx[x[0],x[1]], vy[x[0],x[1]]]
    else:
        points = (im1,jm1),(ip1,jm1),(im1,jp1),(ip1,jp1)
        values_x = vx[im1,jm1],vx[ip1,jm1],vx[im1,jp1],vx[ip1,jp1]
        values_y = vy[im1,jm1],vy[ip1,jm1],vy[im1,jp1],vy[ip1,jp1]
        return interpolated_values(points,values_x,values_y) # how ?
最后一个return语句只是一些伪代码。但这基本上就是我要找的

编辑:

这个功能似乎是一个方向。是否有可能将其纳入其自身的功能中?这句话的意思是:

    def f(x,t):
        return [scipy.interpolate.griddata(x,vx),scipy.interpolate.griddata(x,vy)]
我打算建议从1.5.0版开始,哪个支持关键字参数
start\u points
,但是它不实用,也非常不准确

你的代码示例让我有点困惑:如果你有
vx
vy
向量场坐标,那么你应该有两个网格:
x
y
。使用这些函数,您确实可以使用
scipy.interpolate.griddata
来获得平滑的向量场进行积分,但是当我尝试这样做时,这似乎占用了太多内存。下面是一个基于
scipy.interpolate.interp2d
的类似解决方案:

import numpy as np
import matplotlib.pyplot as plt
import scipy.interpolate as interp
import scipy.integrate as integrate

#dummy input from the streamplot demo
y, x = np.mgrid[-3:3:100j, -3:3:100j]
vx = -1 - x**2 + y
vy = 1 + x - y**2

#dfun = lambda x,y: [interp.griddata((x,y),vx,np.array([[x,y]])), interp.griddata((x,y),vy,np.array([[x,y]]))]
dfunx = interp.interp2d(x[:],y[:],vx[:])
dfuny = interp.interp2d(x[:],y[:],vy[:])
dfun = lambda xy,t: [dfunx(xy[0],xy[1])[0], dfuny(xy[0],xy[1])[0]]

p0 = (0.5,0.5)
dt = 0.01
t0 = 0
t1 = 1
t = np.arange(t0,t1+dt,dt)

streamline=integrate.odeint(dfun,p0,t)

#plot it
plt.figure()
plt.plot(streamline[:,0],streamline[:,1])
plt.axis('equal')
mymask = (streamline[:,0].min()*0.9<=x) & (x<=streamline[:,0].max()*1.1) & (streamline[:,1].min()*0.9<=y) & (y<=streamline[:,1].max()*1.1)
plt.quiver(x[mymask],y[mymask],vx[mymask],vy[mymask])
plt.show()
它与
odeint
兼容。它计算由
odeint
提供给它的每个
p
点的插值。此解决方案不会消耗过多内存,但是使用上述参数运行需要更长的时间。这可能是由于在
odeint
中对
dfun
进行了大量的评估,远远超过了作为输入提供给它的100个时间点

然而,产生的流线比使用
interp2d
获得的流线要平滑得多,即使两种方法都使用默认的
线性插值方法:


如果有人有一个字段的表达式,我使用了一个没有掩码和向量的简明版本的安德拉斯答案:

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import ode

vx = lambda x,y: -1 - x**2 + y
vy = lambda x,y: 1 + x - y**2

y0, x0 = 0.5, 0.6

def f(x, y):
    return vy(x,y)/vx(x,y)

r = ode(f).set_integrator('vode', method='adams')
r.set_initial_value(y0, x0)

xf =  1.0
dx = -0.001

x, y = [x0,], [y0,]
while r.successful() and r.t <= xf:
    r.integrate(r.t + dx)
    x.append(r.t + dx)
    y.append(r.y[0])

#plot it
plt.figure()
plt.plot(x, y)
plt.axis('equal')
plt.show()
将numpy导入为np
将matplotlib.pyplot作为plt导入
从scipy.integrate导入ode
vx=λx,y:-1-x**2+y
vy=λx,y:1+x-y**2
y0,x0=0.5,0.6
定义f(x,y):
返回vy(x,y)/vx(x,y)
r=ode(f)。设置积分器('vode',方法='adams')
r、 设置初始值(y0,x0)
xf=1.0
dx=-0.001
x、 y=[x0,],[y0,]

而r.successful()和r.t我在scipy文档中发现了以下内容()。这正是我需要的。我只需要弄清楚如何在scipy integrator中使用它,因为它仍然需要一个函数。我不知道积分器使用什么网格间距。我现在意识到这个问题更适合计算科学部分。可以把它移到那里吗?或者我应该在那里创建一个副本?你的问题肯定是关于StackOverflow的主题-事实上,你很可能在这里得到一个有用的答案,因为有一个庞大而活跃的scipy用户社区。你如何在实际应用中生成向量场?这不是来自某种形式的2D微分方程吗?在这种情况下,你可以积分得到一条流线……我通过二阶张量场的特征分解生成我的“向量场”(可能是,也可能不是解析的)。通过特征分解的向量场成为主要或次要特征向量。在执行插值时,您可以使用关键字参数
kind='cubic'
来改进积分。我对您的代码有一个争议,那就是您在整个网格上执行插值。如果您想为整个向量场生成流线,这是可以理解的,但是为单个流线生成流线会使其成为不必要的昂贵操作。简单地在每个积分点局部执行插值是否更为理想?例如在最近的网格点。@imranal你说得对:插值越简单,计算就越容易。然而,1<代码>帮助(interp.interp2d)
种类:{'linear'、'cubic'、'quintic'}
,默认值是
linear
,我使用了它。所以,即使我想,我也不能使用更简单的方法
interp.griddata
确实支持最近邻插值,但我没有尝试。2.我担心最近邻相互作用可能会导致不好的结果。如果网格过于密集,则为
odeint
提供非连续向量场可能不好。目视检查可能会决定这一点。@imranal或者我误解了你。如果你的意思是我们应该使用局部
线性插值
/
立方插值:我相信我们已经做到了。所有这些插值方法都涉及插值点的一个小邻域,因此对于
cubic
插值,您可以得到一个分段三次函数。但插值的阶数越高,所需的邻域越大。这就是我让python使用默认bi
线性插值器的原因。从
帮助(interp.interp2d)
所需的最小数据点数[…]为(k+1)**2
,对于线性、三次、五次型,
k=1,3,5
。@imranal I添加了一个基于
griddata
的更新:最终速度要慢得多(由于多个函数求值),但是结果要平滑得多。谢谢@Svaberg发现了这个缺陷。现在它应该可以正常工作了。我刚刚注意到这个答案!很好,我认为拥有一个使用面向对象、有状态积分器的版本是非常有用的。
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import ode

vx = lambda x,y: -1 - x**2 + y
vy = lambda x,y: 1 + x - y**2

y0, x0 = 0.5, 0.6

def f(x, y):
    return vy(x,y)/vx(x,y)

r = ode(f).set_integrator('vode', method='adams')
r.set_initial_value(y0, x0)

xf =  1.0
dx = -0.001

x, y = [x0,], [y0,]
while r.successful() and r.t <= xf:
    r.integrate(r.t + dx)
    x.append(r.t + dx)
    y.append(r.y[0])

#plot it
plt.figure()
plt.plot(x, y)
plt.axis('equal')
plt.show()