Método direto sequencial
Introdução
Vamos mostrar aqui a implementação mais convencional possível, que terá um desempenho
ruim, com todos os laços em Python e o acesso individual a cada elemento dos vetores.
Não há realmente muito o que fazer em relação a isto com esta implementação.
Vamos também comparar os tempos de execução desta implementação com uma implementação
de um solver para matrizes em banda, disponível na bibliteca SciPy.
Implementação Clássica
A implementação é praticamente uma tradução direta do algoritmo para a linguagem Python.
Em primeiro lugar precisamos reorganizar o arquivo de suporte, eliminando as coisas
relacionadas à iteração de Gauss-Seidel. Nada de muito excitante aqui.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 | """
tridisup01.py
This just organizes a few support functions for the Direct Poisson Solver.
"""
import logging
import numpy as np
from math import pi
__format = "%(asctime)s: %(message)s"
logging.basicConfig(format=__format, level=logging.INFO, datefmt="%H:%M:%S")
def info(*args, **kwargs):
"""Directly logs info calls, 99% of what we're doing."""
logging.info(*args, **kwargs)
def zero_rhs(x, L):
"""Sets up a null vector for the RHS of the equation"""
return np.zeros_like(x)
def sin_rhs(x, L, A):
"""Sets up a rhs that produces a single period of a senoidal function with
amplitude A as the solution."""
f = 2*pi/L
return -f*f*A*np.sin(f*x)
def parab_rhs(x, L, A):
"""
Untested rhs for parabolic solution.
"""
return np.full_like(x, 2*A/(L*L))
def cub_rhs(x, L, A):
"""Sets up rhs to have a cubic solution with zeros on 0, L/2, L/3
and value A on L.
Don't forget to set up the correct boundary conditions."""
return -9*(A*L - 3*A*x)/(L*L*L)
|
A classe que define o problema agora também inclui a montagem da matriz de coeficientes
e sua triangularização com o algoritmo de Thomas.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 | """
"problem01.py"
Computational domain definitions.
"""
import numpy as np
from tridisup01 import zero_rhs
#%%
class TriDiag:
"""
A tridiagonal matrix with diagonals stored as vectors o size N.
Columns will be named 'a', 'b', 'c', as is traditional.
"""
def __init__(self, size):
self._a = np.full(size, 1.0)
self._b = np.full(size, -2.0)
self._c = np.full(size, 1.0)
self._a[0] = 0.0
self._c[-1] = 0.0
def solve(self, u, f):
"""
Solves the system of equations with the given rhs.
Returns the solution.
If "keep" is true, do not change the coeficient matrix and rhs.
"""
a = self._a
b = self._b
c = self._c
for i in np.arange(1, b.size):
fac = a[i]/b[i-1]
b[i] -= c[i-1]*fac
f[i] -= f[i-1]*fac
u[-1] = f[-1]/b[-1]
for i in np.arange(b.size-2, -1, -1):
u[i] = (f[i] - c[i]*u[i+1])/b[i]
#%%
class Problem:
def __init__(self, L, nnode, U0=None, UL=None, calc_rhs=zero_rhs, **kwargs):
"""
L: float -- Length of the domain
nnode: int -- Number of nodes
U0, UL: float -- Dirichlet boundary conditions
calc_rhs function(x, L, *args) -- computes the rhs of the equation
"""
self.x = np.linspace(0, L, nnode)
self.dx = self.x[1] - self.x[0]
self.dx2 = self.dx*self.dx
self.u = np.zeros_like(self.x)
self.L = L
self.nn = nnode
if U0 is not None: self.u[0] = U0
if UL is not None: self.u[-1] = UL
self.f = calc_rhs(self.x, self.L, **kwargs)
def solve(self):
trid = TriDiag(self.x.size-2)
self.f *= self.dx2
self.f[1] -= self.u[0]
self.f[-2] -= self.u[-1]
trid.solve(self.u[1:-1], self.f[1:-1])
|
Como a solução do problema foi basicamente trazida para dentro da classe que define
o problema, o programa de simulação fica muito simples.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | """
Direct solve with tridiagonal matrix algorithm
"""
import matplotlib.pyplot as plt
from problem02 import Problem
import tridisup01 as td
td.info("Setting up problems.")
A = 4.0
L = 3.0
nn = 31
probs_rbv = { 'linear': Problem(L, nn, UL=A, calc_rhs=td.zero_rhs),
'sinusoidal': Problem(L, nn, calc_rhs=td.sin_rhs, A=A),
'parabolic': Problem(L, nn, calc_rhs=td.parab_rhs, A=A),
'cubic': Problem(L, nn, UL=A, calc_rhs=td.cub_rhs, A=A)}
for name, p in probs_rbv.items():
p.solve()
td.info("Solving {} problem.".format(name))
fig = 0
for name, p in probs_rbv.items():
plt.figure(fig)
plt.title("Solution for problem {}".format(name))
plt.plot(p.x, p.u, 'b')
fig += 1
|
Método direto do SciPy
Não há