Решение ОДУ
Contents
Решение ОДУ¶
Подмодуль scipy.integrate содержит в себе множество методов решения систем обыкновенных дифференциальных уравнений (ОДУ, ordinary differential equation, ODE).
Т.к. везде ниже будут строиться графики полученного и точного решений уравнения, определим функцию для построения графиков этих решений.
from matplotlib import pyplot as plt
def plot(ax, x, exact_sol, sol_x, sol_y, xlabel="t", s="r."):
ax.plot(x, exact_sol(x), "b")
ax.plot(sol_x, sol_y, s)
ax.set_xlabel(xlabel)
ax.set_ylabel("y")
ax.legend(["Точное решение", "Приближенное решение"])
Задача Коши¶
Функция scipy.integrate.solve_ivp (solve initial value problem) позволяет численно решать задачу Коши вида
Предполагается, что в общем случае \(y=y(t)\) является векторной и решается система обыкновенных дифференциальных уравнений.
Note
Чтобы численно решить уравнение \(n\)-го порядка, необходимо свести его к системе из \(n\) уравнений первого порядка.
В качестве самого простого примера, решим уравнение
import numpy as np
from scipy import integrate
from matplotlib import pyplot as plt
def f(t, y):
return y
def exact_solution(t):
return np.exp(t)
y_0 = [1]
t_0 = 0
t_final = 3
sol = integrate.solve_ivp(f, (t_0, t_final), y_0)
fig, ax = plt.subplots()
fig.set_size_inches((10, 9))
plot(ax, np.linspace(t_0, t_final, 100), exact_solution, sol.t, sol.y[0], s="r-")
На что следует обратить внимание.
Хоть правая часть уравнения и не зависит явным образом от \(t\), функция
f(t, x)все равно объявляется с первым параметромt;Функция \(y=y(t)\) в этом уравнение скалярная, но мы представляем её в виде векторной функции \(f:\mathbb{R}^1 \to \mathbb{R}^1\);
Помимо функции правой части искомого ОДУ, функция
scipy.integrate.solve_ivpпринимает отрезокt_span(\([t_0, t_{\text{final}}])\), на котором решается уравнение, и начальные условия в виде вектора \(y_0\);В итоговом решение компонента
sol.tсоответствует значениям по осиt, аsol.yзначениям полученного решения в точках изsol.t. Т.к. \(y\) считается векторной функцией, тоsol.е— матрица, каждая строка которой соответствует компоненте вектора \(y\), а каждый столбец значению \(t\) изsol.t;Для данного уравнения функция
scipy.integrate.solve_ivpвыдала решение, содержащее совсем небольшое количество значений \(t\). Можно попросить у этой функции получить решение на заданной сетке \(t\).
sol = integrate.solve_ivp(f, (t_0, t_final), y_0, t_eval=np.linspace(t_0, t_final, 50))
fig, ax = plt.subplots()
fig.set_size_inches((10, 9))
plot(ax, np.linspace(t_0, t_final, 100), exact_solution, sol.t, sol.y[0])
Выбор метода решения ОДУ¶
Решение таких систем ОДУ отнюдь не тривиально. Разработано множество методов их решения и ряд из них “зашит” в подмодуле scipy.integrate, среди которых:
Явные ме́тоды Ру́нге — Ку́тты 2-го (
RK23), 4-го (RK45) и 8-го (DOP853) порядков;Неявный метод Руне — Кутты 5-го порядка
Radau;Неявные методы BDF и
LSODA.
Основным критерием выбора является жесткость системы ОДУ: явные методы плохо проявляют себя на жестких системах. SciPy рекомендует использовать по умолчанию метод RK45 и если он плохо/долго сходится переключаться на метод Radau иди BDF.
Краевая задача¶
Функция scipy.integrate.solve_bvp (Solve boundary value problem) предназначена для решения системы ОДУ с краевыми условиями
В качестве аргументов функция solve_bvp принимает:
функцию
f, задающую правую часть уравнения,функцию
bc(сокращение отboundary condition), задающую невязку для граничных условий,массив
x, определяющий сетку значений независимой переменной \(x\),массив
y, задающий “догадку” об итоговом решении \(y = y(x)\).
В качестве примера решим уравнение
на отрезке \(x \in [0, \frac{\pi}{2}]\). Чтобы это сделать, необходимо свести это уравнение второго порядка к системе уравнений первого порядка. Для этого введем обозначения \(z_1 = y\) и \(z_2 = y'\). Тогда систему выше можно записать в виде
import numpy as np
from scipy import integrate
from matplotlib import pyplot as plt
def f(x, z):
return [
z[1],
-z[0]
]
def R(za, zb):
return np.array([za[0] - 1, zb[0]])
def exact_solution(x):
return np.cos(x)
a, b = 0, np.pi/2
N = 30
x = np.linspace(a, b, N)
z_guess = np.zeros((2, N), dtype=float)
sol = integrate.solve_bvp(f, R, x, z_guess)
fig, ax = plt.subplots()
fig.set_size_inches((10, 9))
plot(ax, x, exact_solution, sol.x, sol.y[0], xlabel="x")