Introdução à Equação de Onda
A equação de onda é uma equação diferencial parcial fundamental na física para modelar a propagação de ondas. Em sua forma unidimensional, a expressão matemática é dada por:
$$ \frac{\partial^2 u}{\partial t^2} = c^2 \frac{\partial^2 u}{\partial x^2} $$
Onde $u$ representa a amplitude da onda, $t$ é a variável temporal, $x$ é a coordenada espacial e $c$ denota a velocidade de propagação da onda.
Abordagem Explícita
O método de diferenças finitas explícito calcula o estado futuro do sistema utilizando exclusivamente valores conhecidos dos passos de tempo atuais e anteriores. Aplicando diferenças centrais tanto para o tempo quanto para o espaço, obtemos a seguinte discretização:
$$ \frac{u_i^{n+1} - 2u_i^n + u_i^{n-1}}{\Delta t^2} = c^2 \frac{u_{i+1}^n - 2u_i^n + u_{i-1}^n}{\Delta x^2} $$
Nesta formulação, $u_i^n$ é o valor da onda na posição $i$ e no instante $n$. O isolamento de $u_i^{n+1}$ permite o cálculo direto e iterativo da malha.
Abordagem Impllícita
Por outro lado, o esquema implícito avalia as derivadas espaciais no passo de tempo futuro ($n+1$). A equação discretizada assume a forma:
$$ \frac{u_i^{n+1} - 2u_i^n + u_i^{n-1}}{\Delta t^2} = c^2 \frac{u_{i+1}^{n+1} - 2u_i^{n+1} + u_{i-1}^{n+1}}{\Delta x^2} $$
Essa escolha exige a resolução de um sistema de equações lineares a cada iteração temporal, uma vez que os valores de $u^{n+1}$ estão acoplados espacialmente.
Implementação Computacional em Python
Abaixo estão apresentadas implementações otimizadas para ambos os esqeumas. O código explícito utiliza operações vetorizadas do NumPy para evitar laços aninhados, enquanto o código implícito emprega matrizes esparsas para resolver o sistema linear de forma eficiente.
Simulação com Método Explícito
import numpy as np
import matplotlib.pyplot as plt
# Configuração do domínio e parâmetros físicos
domain_length = 10.0
grid_points = 150
spatial_step = domain_length / (grid_points - 1)
total_time = 2.0
time_steps = 800
time_step = total_time / time_steps
wave_speed = 1.0
# Malhas espaciais e temporais
x_grid = np.linspace(0, domain_length, grid_points)
t_grid = np.linspace(0, total_time, time_steps)
# Matriz de solução e condição inicial
wave_field = np.zeros((grid_points, time_steps))
wave_field[:, 0] = np.sin(np.pi * x_grid / domain_length)
wave_field[:, 1] = wave_field[:, 0] # Condição de velocidade inicial nula
# Constante de Courant ao quadrado
courant_sq = (wave_speed * time_step / spatial_step) ** 2
# Iteração temporal vetorizada
for step in range(1, time_steps - 1):
wave_field[1:-1, step + 1] = (
2 * wave_field[1:-1, step] - wave_field[1:-1, step - 1] +
courant_sq * (wave_field[2:, step] - 2 * wave_field[1:-1, step] + wave_field[:-2, step])
)
# Visualização dos resultados
plt.figure(figsize=(8, 5))
plt.imshow(wave_field, extent=[0, total_time, 0, domain_length], origin='lower', aspect='auto', cmap='viridis')
plt.colorbar(label='Amplitude da Onda')
plt.xlabel('Tempo (s)')
plt.ylabel('Posição (m)')
plt.title('Propagação de Onda - Esquema Explícito')
plt.show()
Simulação com Método Implícito
import numpy as np
import matplotlib.pyplot as plt
from scipy.sparse import diags
from scipy.sparse.linalg import spsolve
# Parâmetros do sistema
L_x = 10.0
N_x = 150
dx = L_x / (N_x - 1)
T_max = 2.0
N_t = 800
dt = T_max / N_t
c_wave = 1.0
x_vals = np.linspace(0, L_x, N_x)
t_vals = np.linspace(0, T_max, N_t)
# Inicialização do campo
U = np.zeros((N_x, N_t))
U[:, 0] = np.sin(np.pi * x_vals / L_x)
U[:, 1] = U[:, 0]
# Parâmetro de malha
r = (c_wave * dt / dx) ** 2
# Construção da matriz tridiagonal esparsa (constante no tempo)
main_diag = (1 + 2 * r) * np.ones(N_x - 2)
off_diag = -r * np.ones(N_x - 3)
A_sparse = diags([off_diag, main_diag, off_diag], [-1, 0, 1], format='csr')
# Resolução do sistema linear a cada passo
for k in range(1, N_t - 1):
# Vetor do lado direito (RHS)
rhs = 2 * U[1:-1, k] - U[1:-1, k - 1]
# Ajuste para condições de contorno de Dirichlet (u=0 nas bordas)
# Como U[0, k+1] e U[-1, k+1] são 0, não precisamos adicionar termos extras ao rhs.
# Resolve o sistema para o passo k+1
U[1:-1, k + 1] = spsolve(A_sparse, rhs)
# Plotagem do campo de onda
plt.figure(figsize=(8, 5))
plt.imshow(U, extent=[0, T_max, 0, L_x], origin='lower', aspect='auto', cmap='plasma')
plt.colorbar(label='Amplitude da Onda')
plt.xlabel('Tempo (s)')
plt.ylabel('Posição (m)')
plt.title('Propagação de Onda - Esquema Implícito')
plt.show()
Análise Comparativa dos Esquemas
1. Tratamento da Derivada Temporal
- Esquema Explícito: A derivada temporal é avaliada com base em estados passados e presentes. A atualização do estado futuro é uma função direta e desacoplada: $u^{n+1} = \mathcal{F}(u^n, u^{n-1})$.
- Esquema Implícito: A formulação envolve o estado futuro na avaliação das derivadas espaciais, resultando em uma dependência implícita: $u^{n+1} = \mathcal{F}(u^n, u^{n-1}, u^{n+1})$, o que obriga a inversão de matrizes.
2. Estabilidade Numérica
- Explícito: Sofre de estabilidade condicional. O passo de tempo deve obedecer estritamente ao critério de Courant-Friedrichs-Lewy (CFL). Violações desse limite resultam em explosões numéricas.
- Implícito: Geralmente exibe estabilidade incondicional. Permite o uso de passos de tempo arbitrariamente grandes sem que a solução diverja, sendo ideal para problemas rígidos (stiff).
3. Custo Computacional
- Explícito: Baixo custo por iteração, pois envolve apenas multiplicações e adições de matrizes/vetores. É altamente paralelizável e eficiente para malhas massivas.
- Implícito: Alto custo por iteração devido à necessidade de fatorar e resolver sistemas lineares (ou não lineares). O uso de matrizes esparsas e pré-condicionadores é mandatório para viabilizar a simulação em larga escala.
4. Cenários de Aplicação
- Explícito: Preferido em dinâmica de fluidos computacional (CFD), acústica e simulações de ondas sísmicas onde a precisão temporal exige passos de tempo pequenos.
- Implícito: Amplamente adotado em difusão térmica, mecânica dos sólidos e simulações de circuitos, onde os fenômenos de interesse ocorrem em escalas de tempo muito maiores que as constantes de estabilidade explícita.
5. Dissipação e Dispersão Numérica
- Explícito: Pode introduzir erros de dispersão significativos, causando a separação artificial de frequências e distorcendo a frente de onda ao longo do tempo.
- Implícito: Tende a ser mais dissipativo, amortecendo as componentes de alta frequência da solução, o que pode suavizar artificialmente gradientes íngremes, mas preserva a estabilidade global.