Интегриране на векторно поле (масив numpy) с помощта на scipy.integrate

Интересувах се от интегриране на векторно поле (т.е. намиране на рационализация) за дадена начална точка с помощта на библиотеката 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 ?

Последният оператор за връщане е просто някакъв псевдо код. Но основно това е, което търся.

Редактиране:

scipy.interpolate.griddata изглежда е правилният начин. Възможно ли е да го включите в самата функция? Нещо в редовете на това:

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

person imranal    schedule 22.11.2015    source източник
comment
Намерих следното в документацията на scipy (docs.scipy.org/doc/scipy/reference/generated/). Точно това ми трябва. Просто трябва да разбера как мога да използвам това с scipy интегратора, тъй като той все още изисква функция. И не знам какво разстояние между мрежите използва интеграторът.   -  person imranal    schedule 23.11.2015
comment
Сега осъзнавам, че този въпрос е по-подходящ за раздела за компютърни науки. Възможно ли е да го преместите там? Или да създам дубликат там?   -  person imranal    schedule 23.11.2015
comment
Вашият въпрос определено е по темата за StackOverflow - всъщност вероятно е много по-вероятно да получите полезен отговор тук, тъй като SO има голяма и активна общност от scipy потребители.   -  person ali_m    schedule 23.11.2015
comment
Как генерирате вашето векторно поле във вашето действително приложение? Това не идва ли от някаква форма на 2D диференциално уравнение? В такъв случай можете просто да интегрирате that, за да получите рационализация...   -  person thomas    schedule 26.11.2015
comment
Генерирам моите векторни полета чрез собствено разлагане на тензорни полета от втори ред (които могат или не могат да бъдат аналитични). Векторното поле чрез собственото разлагане става или главният, или вторичният собствен вектор   -  person imranal    schedule 26.11.2015
comment
Надявам се, че някой тук може да ми помогне с подобен въпрос [stackoverflow.com/questions/63220629/   -  person Morgs    schedule 05.08.2020


Отговори (2)


Щях да предложа matplotlib.pyplot.streamplot, който поддържа аргумента на ключовата дума start_points от версия 1.5.0, но не е практична и също много неточна.

Вашите примери за код са малко объркващи за мен: ако имате 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()

Имайте предвид, че направих интеграционната мрежа по-плътна за допълнителна прецизност, но в този случай не се промени много.

Резултат:

изход

Актуализация

След някои бележки в коментарите преразгледах моя оригинален подход, базиран на 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. Той изчислява интерполираните стойности за всяка p точка, дадена му от odeint. Това решение не консумира излишна памет, но отнема много много повече време, за да работи с горните параметри. Това вероятно се дължи на много оценки на dfun в odeint, много повече от това, което би било очевидно от 100-те времеви точки, дадени му като вход.

Въпреки това, получената рационализация е много по-плавна от тази, получена с interp2d, въпреки че и двата метода използват метода за интерполация по подразбиране linear:

подобрен резултат

person Andras Deak    schedule 28.11.2015
comment
Можете да подобрите интеграцията, като използвате аргумента на ключовата дума kind='cubic', когато извършвате интерполация. - person imranal; 28.11.2015
comment
Има едно твърдение, което имам с вашия код, и то е, че извършвате интерполация върху цялата мрежа. Ако искате да генерирате линии на рационализация за цялото векторно поле, това би било разбираемо, но правенето му за една линия на рационализация го прави ненужно скъпо действие. Би ли било много по-оптимално просто да извършите интерполация локално във всяка точка на интегриране? Т.е. в най-близките точки на мрежата. - person imranal; 28.11.2015
comment
@imranal прав си: колкото по-проста е интерполацията, толкова по-лесно е изчислението. Въпреки това, 1. help(interp.interp2d) казва kind : {'linear', 'cubic', 'quintic'} и по подразбиране е linear, което използвах. Така че дори и да исках, не бих могъл да използвам по-прост метод. interp.griddata поддържа обаче интерполация на най-близкия съсед, но не го пробвах. 2. Страхувам се, че интерплацията на най-близкия съсед може да доведе до грозни резултати, ако вашата мрежа е твърде гъста, може да е лошо да дадете непродължително векторно поле на odeint. Визуална проверка вероятно би могла да реши това. - person Andras Deak; 28.11.2015
comment
@imranal или може би не съм те разбрал правилно. Ако имате предвид, че трябва да използваме локална linear/cubic интерполация: вярвам, че вече сме. Всички тези методи на интерполация включват малка околност на интерполиращата точка, така че за cubic интерполант получавате частична кубична функция. Но колкото по-висок е редът на интерполанта, толкова по-голям квартал е необходим. Това всъщност е причината да позволя на python да използва биlinear интерполатора по подразбиране. От help(interp.interp2d): The minimum number of data points required [...] is (k+1)**2 с k=1,3,5 за линеен, кубичен, квинтичен. - person Andras Deak; 28.11.2015
comment
@imranal Добавих актуализация, базирана на griddata: в крайна сметка е много по-бавна (поради множество оценки на функции), но резултатът е много по-гладък. - person Andras Deak; 28.11.2015
comment
Относно вашата актуализация: Трябва да изравните numpy масивите vx и vy само веднъж. Пробях известно време върху разликата между методите за интерполация, които сте изброили. И се оказва, че за по-малко плътни мрежи interp2d (с кубичен метод) е по-бързо от griddata, докато за плътните мрежи е обратното. Но като цяло говорим за сравнително редки мрежи, на които тествах това (вариращи от 10x10 до 50x50), и те все още бяха доста взискателни. Това прави и двете техники за интерполация безполезни за по-големи мрежи, да речем 500x500. Особено ако интегрирането се извършва многократно. - person imranal; 29.11.2015
comment
Добавих вашия изменен код тук (като непосочен за всеки случай): pastebin.com/7uGCjfSn - person imranal; 29.11.2015
comment
@imranal Благодаря, прав си за сплескването, разбира се. И така, наистина ли се нуждаете от времевия интеграл на векторното поле или само от линията на потока до някакъв момент? Няма ли да е опция интеграцията да се приложи ръчно, като да се използва някакъв метод на Ойлер? Тогава ще имате близък контрол върху броя на включените изчисления, но разбира се, ако вашите фазови линии са объркани, тогава по-простото интегриране може да бъде по-малко точно. И благодаря за променения код. - person Andras Deak; 29.11.2015

В случай, че някой има израз на полето, използвах кратка версия на отговора на 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()

въведете описание на изображението тук

Надявам се да е полезно за някой със същите нужди.

person Marcos Lourenço    schedule 12.08.2018
comment
Благодаря на @Svaberg, че улови недостатъка. Сега трябва да работи правилно. - person Marcos Lourenço; 31.07.2020
comment
Току що забелязах този отговор! Добър улов, мисля, че е наистина полезно да има версия, която използва обектно-ориентиран интегратор със състояние. - person Andras Deak; 31.07.2020