【问题标题】:Integrating a vector field (a numpy array) using scipy.integrate使用 scipy.integrate 积分向量场(一个 numpy 数组)
【发布时间】:2016-02-24 18:18:58
【问题描述】:

我有兴趣使用 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 语句只是一些伪代码。但这基本上是我正在寻找的。

编辑:

scipy.interpolate.griddata 函数似乎是要走的路。是否可以将其合并到它自己的功能中?这句话中的一些东西:

    def f(x,t):
        return [scipy.interpolate.griddata(x,vx),scipy.interpolate.griddata(x,vy)]

【问题讨论】:

  • 我在 scipy 文档中找到了以下内容 (docs.scipy.org/doc/scipy/reference/generated/…)。这正是我所需要的。我只需要弄清楚如何将它与 scipy 集成器一起使用,因为它仍然需要一个函数。而且我不知道积分器使用什么网格间距。
  • 我现在意识到这个问题更适合计算科学部分。可以把它移到那里吗?或者我应该在那里创建一个副本?
  • 您的问题绝对是 StackOverflow 的主题 - 事实上,您可能更有可能在这里得到有用的答案,因为 SO 拥有庞大且活跃的 scipy 用户社区。​​span>
  • 在实际应用中如何生成矢量场?这不是来自某种形式的二维微分方程吗?在这种情况下,您可以整合 that 以获得流线型...
  • 我正在通过二阶张量场的特征分解(可能是分析的,也可能不是分析的)生成我的“矢量场”。通过特征分解的向量场成为主要或次要特征向量

标签: python scipy interpolation pde numerical-integration


【解决方案1】:

我打算建议 matplotlib.pyplot.streamplot 从 1.5.0 版本开始支持关键字参数 start_points,但它不实用而且非常不准确。

您的代码示例让我有点困惑:如果您有 vxvy 向量场坐标,那么您应该有 两个 网格:xy。使用这些,您确实可以使用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()

请注意,我使集成网格更加密集以提高精度,但在这种情况下并没有太大变化。

结果:

更新

在 cmets 中做了一些笔记后,我重新审视了我原来的基于 griddata 的方法。这样做的原因是 interp2d 计算整个数据网格的插值,griddata 只计算给定点的插值,所以在几个点的情况下后者应该快得多。

我在之前的 griddata 尝试中修复了错误并提出了

xyarr = np.array(zip(x.flatten(),y.flatten()))
dfun = lambda p,t: [interp.griddata(xyarr,vx.flatten(),np.array([p]))[0], interp.griddata(xyarr,vy.flatten(),np.array([p]))[0]]

odeint 兼容。它计算odeint 给它的每个p 点的插值。此解决方案不会消耗过多的内存,但是使用上述参数运行需要更多更长的时间。这可能是由于在odeint 中对dfun 进行了很多评估,远远超过作为输入的 100 个时间点所得出的结果。

但是,尽管这两种方法都使用了默认的 linear 插值方法,但得到的流线比使用 interp2d 获得的流线平滑得多:

【讨论】:

  • 您可以在进行插值时使用关键字参数kind='cubic'来改进集成。
  • 我对你的代码有一个争论,那就是你在整个网格上执行插值。如果您想为整个矢量场生成流线,那是可以理解的,但是为单个流线生成流线会导致不必要的代价高昂的操作。简单地在每个积分点本地执行插值会更好吗?即在最近的网格点。
  • @imranal 你是对的:插值越简单,计算就越容易。但是,1.help(interp.interp2d) 表示kind : {'linear', 'cubic', 'quintic'},默认为linear,我使用了它。所以即使我想,我也无法使用更简单的方法。 interp.griddata 确实 支持最近邻插值,但我没有尝试。 2.如果你的网格太密集,我担心最近邻插值会导致丑陋的结果,给odeint一个不连续的向量场可能会很糟糕。目视检查可能会决定这一点。
  • @imranal 或者我误解了你的意思。如果您的意思是我们应该使用 local linear/cubic 插值:我相信我们已经这样做了。所有这些插值方法都涉及插值点的一个小邻域,因此对于cubic 插值,您会得到一个分段三次函数。但是插值的阶数越高,需要的邻域就越大。这实际上是我让python 使用默认的bilinear 插值器的原因。来自help(interp.interp2d)The minimum number of data points required [...] is (k+1)**2k=1,3,5 用于线性、三次、五次。
  • @imranal 我在griddata 的基础上添加了一个更新:最终速度要慢得多(由于多个函数评估),但结果要平滑得多。
【解决方案2】:

如果有人有该领域的表达,我使用了一个简明版本的 Andras 答案,没有掩码和向量:

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()

我希望它对有相同需求的人有用。

【讨论】:

  • 感谢@Svaberg 发现了这个缺陷。现在它应该可以正常工作了。
  • 我刚刚注意到这个答案!很好,我认为拥有一个使用面向对象的有状态集成器的版本非常有用。
猜你喜欢
  • 1970-01-01
  • 2016-10-06
  • 1970-01-01
  • 2020-11-27
  • 2022-01-01
  • 2015-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多