Python para Equações Diferenciais
Neste capítulo, o Python será usado para aproximar soluções de problemas de valor inicial. Os métodos de Euler, Runge-Kutta e Adams-Bashforth repetem atualizações a partir de um ponto inicial. Como funções, laços e listas já foram apresentados na introdução ao Python, aqui o foco será organizar a malha de cálculo, armazenar a trajetória da solução, encapsular passos numéricos e comparar resultados com solve_ivp.
Definindo a EDO
Uma equação diferencial ordinária de primeira ordem pode ser escrita como:
\[y' = f(x,y)\]Em Python, representamos \(f(x,y)\) como uma função que recebe o ponto atual e devolve a inclinação da solução naquele ponto:
def f(x, y):
return 2*x - y + 1
print(f(0.2, 1))
Valores iniciais e malha
Um problema de valor inicial informa o ponto inicial, o valor inicial da solução e o passo de cálculo:
import numpy as np
x0 = 0
y0 = 1
h = 0.2
xf = 1
n_passos = int(round((xf - x0) / h))
malha_x = np.linspace(x0, xf, n_passos + 1)
| Variável | Significado |
|---|---|
x0 |
Valor inicial de \(x\) |
y0 |
Valor inicial de \(y\) |
h |
Passo usado entre dois pontos consecutivos |
xf |
Valor final de \(x\) |
n_passos |
Quantidade de atualizações necessárias para ir de \(x_0\) até \(x_f\) |
Usar uma malha com quantidade definida de passos evita problemas de comparação entre números decimais que podem ocorrer quando controlamos o laço apenas com while x < xf.
Método de Euler em forma de função
A fórmula do método de Euler é:
\[y_{i+1}=y_i+h f(x_i,y_i)\]Em Python, podemos escrever um passo do método como uma função independente. Isso facilita trocar Euler por Runge-Kutta mantendo a mesma estrutura de repetição:
def euler(x, y, h):
return y + h*f(x, y)
print(euler(0.2, 1, 0.2))
Repetindo os passos com histórico
Para avançar de \(x_0\) até \(x_f\), guardamos a trajetória calculada. Em vez de depender de uma comparação contínua com \(x_f\), percorremos a quantidade de passos definida pela malha:
valores_x = [x0]
valores_y = [y0]
x = x0
y = y0
for passo in range(n_passos):
y = euler(x, y, h)
x = x + h
valores_x.append(x)
valores_y.append(y)
Tabela de resultados
Para conferir o método, podemos reunir os valores calculados em pares \((x_i,y_i)\). Essa tabela será útil para comparar Euler, Runge-Kutta e Adams-Bashforth:
tabela = list(zip(valores_x, valores_y))
for xi, yi in tabela:
print(xi, yi)
Preparando métodos de ordem maior
Métodos como Runge-Kutta calculam inclinações intermediárias antes de atualizar \(y\). A estrutura abaixo mostra como guardar esses coeficientes em variáveis separadas:
def rk2(x, y, h):
k1 = f(x, y)
k2 = f(x + h, y + h*k1)
return y + h/2 * (k1 + k2)
Com essa organização, a função que executa os passos pode receber o método como argumento:
def resolver_pvi(metodo, x0, y0, h, n_passos):
valores_x = [x0]
valores_y = [y0]
x = x0
y = y0
for passo in range(n_passos):
y = metodo(x, y, h)
x = x + h
valores_x.append(x)
valores_y.append(y)
return valores_x, valores_y
Usando SciPy
Em algumas situações, podemos comparar os métodos estudados com uma função pronta da biblioteca scipy. A função solve_ivp recebe o intervalo, o valor inicial e os pontos nos quais desejamos avaliar a solução:
from scipy.integrate import solve_ivp
solucao = solve_ivp(f, [x0, xf], [y0], t_eval=malha_x)
print(solucao.t)
print(solucao.y[0])
Atividade
Para \(f(x,y)=x+y\), defina x0 = 0, y0 = 1, h = 0.1 e xf = 0.5. Calcule n_passos, gere a malha e use uma função resolver_pvi para guardar todos os pares \((x_i,y_i)\) obtidos pelo método de Euler.