6. Řešení diferenciálních rovnic s okrajovou podmínkou#
V předchozím cvičení jsme se zabývali metodami na řešení počáteční úlohy tvaru:
Tato úloha představuje soustavu obyčejných diferenciálních rovnic 1. řádu s počáteční podmínkou
V dnešním cvičení si představíme numerické metody řešící okrajové úlohy. Oproti počáteční úloze budeme znát část řešení ve více bodech. Pro
Omezíme se na řešení soustavy dvou ODR, která odpovídá diferenciální rovnici druhého řádu:
kde máme dvě okrajové podmínky pro funkci
Zde si představíme dvě metody na řešení okrajových úloh:
Metoda střelby
Metoda sítí (konečných diferencí)
Potřebné knihovny:
import numpy as np
import matplotlib.pyplot as plt
from scipy import linalg, integrate, optimize
Pomocí metod budeme řešit následující úlohu, která odpovídá rovnici tlumeného oscilátoru:
kde
Rovnice má obecné řešení:
Pro naší volbu koeficientů a okrajové podmínky máme:
na intervalu
# analyticke reseni rovnice tlumeneho oscilatoru
def y_sol(t):
return -(np.exp(-t) * np.sin(3 * np.sqrt(11) * (t - 2))) / np.sin(6*np.sqrt(11))
tt = np.linspace(0, 2, 100)
plt.plot([0, 2], [1, 0], 'r*')
plt.plot(tt, y_sol(tt));

6.1. Metoda střelby#
Tato metoda je inspirovaná úlohou střelby na cíl v homogenním gravitačním poli Země.
Řekněme, že máme kanón na pozici
Na začátku známe pouze polohu kanónu a cíle, což odpovídá:
kde
Uvažujme opět následující okrajovou úlohu s ODR druhého řádu:
Řešení pomocí metody střelby spočívá v následujících krocích:
náhodně zvolíme počáteční hodnotu derivace
vyřešíme počáteční úlohu:
tím získáme
řešíme nelineální rovnici
tím, že opakujeme kroky 1-3
Na krok 4 je možné aplikovat libovolnou z metod představených v předchozím cvičení. Jednoduchou volbou je bisekce, tedy máme na začátku dva odhady
Ukážeme si implementaci metody střelby na úloze tlumeného oscilátoru. Počáteční úloha z bodu 2 metody vypadá následovně:
# definice prave strany pocatecni ulohy
def f(t, Y):
y, z = Y # Y je vektor reseni [y, z]
return np.array([z, -2*z - 100*y])
# okrajove podminky
x0 = 0
x1 = 2
y0 = 1
y1 = 0
Řešení počáteční úlohy pro jednu konkrétní volbu
beta = -100
# druha pocatecni podminka
z0 = beta
res = integrate.solve_ivp(f, [x0, x1], [y0, z0], method='RK45', t_eval = tt)
plt.plot(res.t, res.y[0]) # y[0] odpovida y, y[1] odpovida z=y'
plt.plot([2], [res.y[0, -1]], 'go')
plt.plot([0, 2], [y0, y1], 'r*')
plt.xlabel('t')
plt.ylabel('y');

Implementace metody střelby
Pomocí metody střelby řešte úlohu tlumeného oscilátoru.
Defininujte funkci beta_root
) pomocí knihovní funkce optimize.root_scalar(..., method='newton')
. Budete potřebovat také darivaci funkce
def F(beta):
## DOPLŇTE - definice funkce na hledani korene ##
return None
# numericka derivace
def dF(beta, h = 0.00001):
## DOPLŇTE - numericka derivace funkce F(beta) ##
return None
## DOPLŇTE - hledani korene, Newton-Ralphson ##
res_root = optimize.root_scalar(F, fprime=dF, x0=0, method='newton', rtol=1e-6)
print(res_root)
beta_root = res_root.root
converged: True
flag: 'converged'
function_calls: 8
iterations: 4
root: array([-6.65510418])
Vizualizace řešení
Nalezením kořene
res_ivp = integrate.solve_ivp(f, [x0, x1], [y0, beta_root], method='RK45', t_eval = tt)
plt.plot(res_ivp.t, res_ivp.y[0], '.', label='numericky střelba') # y[0] odpovida y, y[1] odpovida z=y'
plt.plot(res_ivp.t, y_sol(res_ivp.t), '-', label='analyticky') # presne reseni
plt.plot([2], [res_ivp.y[0, -1]], 'go')
plt.plot([0, 2], [y0, y1], 'r*')
plt.xlabel('t')
plt.ylabel('y')
plt.legend();

# srovnani reseni na pravem okraji
res_ivp.y[0][-1], y_sol(x1)
(-2.7755575615628914e-17, -0.0)
6.2. Metody sítí (konečných diferencí)#
Metoda konečných diferencí, také nazývaná metodou sítí nebo relaxační metodou, je založená na nahrazení derivací v diferenciální rovnici pomocí konečných diferencí. Tím získáme algebraický výraz, který již dokážeme řešit na počítači. Uvažujeme následující ODR 2. řádu s okrajovou podmínkou:
Provedeme nahrazení jednotlivých derivací pomocí následujících aproximací pomocí konečných diferencí, které počítáme na rovnoměrné síti na intervalu
Okrajovou úlohu pak můžeme přepsat jako soustavu lineárních algebraických rovnic:
kde
V maticové formě má systém lineárních rovnic tvar:
Tato metoda se někdy nazývá relaxační metodou, jelikož typicky řešíme velkou soustavu linearních rovnic pomocí iteračního algoritmu. Tedy máme nějaký počáteční odhad, který iteračně zlepšujeme (viz předchozí kapitola). V případě diferenciální rovnice 2. řádu vidíme, že soustava je třidiagonální, stačilo by tedy využít Thomasova algoritmu!
Pomocí této metody budeme opět řešit úlohu tlumeného oscilátoru:
Postup metody konečných diferencí lze shrnout do následujících kroků:
Zvolíme tvar konečných diferencí, kterými nahradíme jednotlivé derivace.
Získáme soustavu lineárních rovnic - sestavíme matici soustavy a pravou stranu.
Řešíme soustavu pomocí zvoleného algoritmu.
Implementace metodu konečných diferencí
Pomocí metody konečných diferencí řešte úlohu tlumeného oscilátoru.
a = 0
b = 2
# okrajove podminky
alpha1 = 1
alpha2 = 0
## 1. volíme diference podle predchoziho popisu pro obecnou ODR 2. radu
N = 101 # velikost site
h = (b - a) / (N-1)
tt = np.linspace(a, b, N)
## 2. sestaveni matice a prave strany
# koeficienty p_i, q_i, r_i a s_i nezavisi na t !
p_i = 1
q_i = -2 - 2*h + 100*h**2
r_i = 1 + 2*h
s_i = 0
# matice soustavy
A = np.zeros([N, N])
A[0,0] = A[-1,-1] = 1
idx = np.arange(0, N-1)
A[idx[:-1]+1,idx[:-1]] = p_i # spodni vedlejsi diagonala
A[idx[1:],idx[1:]] = q_i # diagonala
A[idx[1:],idx[1:]+1] = r_i # diagonala
# prava strana
b = np.zeros(N)
b[0] = alpha1
b[1:-1] = s_i
b[-1] = alpha2
## 3. vyreseni soustavy
y = linalg.solve(A, b)
A, b
(array([[ 1. , 0. , 0. , ..., 0. , 0. , 0. ],
[ 1. , -2. , 1.04, ..., 0. , 0. , 0. ],
[ 0. , 1. , -2. , ..., 0. , 0. , 0. ],
...,
[ 0. , 0. , 0. , ..., -2. , 1.04, 0. ],
[ 0. , 0. , 0. , ..., 1. , -2. , 1.04],
[ 0. , 0. , 0. , ..., 0. , 0. , 1. ]]),
array([1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]))
Vizualizace řešení
plt.plot(tt, y, '.', label='numericky MKD') # y[0] odpovida y, y[1] odpovida z=y'
plt.plot(tt, y_sol(tt), '-', label='analyticky') # presne reseni
plt.plot([0, 2], [y0, y1], 'r*')
plt.xlabel('t')
plt.ylabel('y')
plt.legend();

Knihovna SciPy poskytuje funkci scipy.integrate.solve_bvp()
pro řešení okrajových úloh:
y_init = np.zeros([2,N]) # pocatecni odhad
res_bvp = integrate.solve_bvp(f, lambda ya, yb: np.array([ya[0]-alpha1, yb[0] - alpha2]), tt, y_init)
#print(res_bvp)
plt.plot(res_bvp.x, res_bvp.y[0], '.', label='numericky střelba') # y[0] odpovida y, y[1] odpovida z=y'
plt.plot(res_bvp.x, y_sol(res_bvp.x), '-', label='analyticky') # presne reseni
plt.plot([2], [res_bvp.y[0, -1]], 'go')
plt.plot([0, 2], [y0, y1], 'r*')
plt.xlabel('t')
plt.ylabel('y');
