Você está na página 1de 36

13

Soluo numrica de equaes


diferenciais parciais

13.1 Adveco pura: a onda cinemtica

Considere a equao

u u
+c = 0, u(x, 0) = g(x). (13.1)
t x

A sua soluo pode ser obtida pelo mtodo das caractersticas, e

u(x, t) = g(x c t). (13.2)

Seja ento o problema

u u
+2 = 0, (13.3)
t x
u(x, 0) = 2x(1 x). (13.4)

A condio inicial, juntamente com u(x, 1), u(x, 2) e u(x, 3) esto mostrados na figura
13.1. Observe que a soluo da equao uma simples onda cinemtica.

3
u(x, t)

tempo

0.50 1

0.00 0
0 2 4 6 8 10
x

Figura 13.1: Condio inicial da equao 13.3.

233
Matemtica Aplicada 234

Vamos adotar a notao

uni u(x i , t n ), (13.5)


x i = ix, (13.6)
t n = nt, (13.7)

com

x = L/Nx , (13.8)
t = T /Nt (13.9)

onde L, T so os tamanhos de grade no espao e no tempo, respectivamente, e Nx , Nt


so os nmeros de divises no espao e no tempo.
Uma maneira simples de transformar as derivadas parciais em diferenas finitas
na equao (13.3) fazer

u un+1 uni

= i + O(t), (13.10)
t i,n t
u un uni1

= i+1 + O(x 2 ). (13.11)
x i,n x

Substituindo na equao (13.3), obtemos o esquema de diferenas finitas explcito:


n
uin+1 uni ui+1 uni1

= c ,
t 2x
ct n
uin+1 = uni (u uni1 ), (13.12)
2x i+1
(com c = 2 no nosso caso). Esse um esquema incondicionalmente instvel, e vai
fracassar. Vamos fazer uma primeira tentativa, j conformados com o fracasso an-
tecipado. Ela vai servir para desenferrujar nossas habilidades de programao de
mtodos de diferenas finitas.
O programa que implementa o esquema instvel o onda1d-ins.py, mostrado
na listagem 13.1. Por motivos que ficaro mais claros na sequncia, ns escolhemos
x = 0,01, e t = 0,0005.
O programa gera um arquivo de sada binrio, que por sua vez lindo pelo pr-
ximo programa na sequncia, surf1d-ins.py, mostrado na listagem 13.2. O nico
trabalho deste programa selecionar algumas linhas da sada de onda1d-ins.py;
no caso, ns o rodamos com o comando
surf1d-ins.py 3 250 ,
 

o que significa selecionar 3 sadas (alm da condio inicial), de 250 em 250 interva-
los de tempo t. Observe que para isto ns utilizamos uma lista (v), cujos elementos
so arrays.
O resultado dos primeiros 750 intervalos de tempo de simulao mostrado na
figura 13.2. Repare como a soluo se torna rapidamente instvel. Repare tambm
como a soluo numrica, em t = 750t = 0,375, ainda est bastante distante dos
tempos mostrados na soluo analtica da figura 13.1 (que vo at t = 4). Clara-
mente, o esquema explcito que ns programamos jamais nos levar a uma soluo
numrica satisfatria para tempos da ordem de t = 1!
235 13.1 Adveco pura: a onda cinemtica

Listagem 13.1: onda1d-ins.py Soluo de uma onda 1D com um mtodo explcito


instvel
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ----------------------------------------------------------
4 # onda1d-ins resolve uma equao de onda com um mtodo
5 # explcito
6 #
7 # uso: ./onda1d-ins.py
8 # ----------------------------------------------------------
9 from __future__ import print_function
10 from __future__ import division
11 fou = open('onda1d-ins.dat','wb')
12 dx = 0.01
13 dt = 0.0005
14 print('# dx = %9.4f' % dx)
15 print('# dy = %9.4f' % dt)
16 from numpy import zeros
17 nx = int(10.0/dx) # nmero de pontos em x
18 nt = int(1.0/dt) # nmero de pontos em t
19 print('# nx = %9d' % nx)
20 print('# nt = %9d' % nt)
21 u = zeros((2,nx+1),float) # apenas 2 posies no tempo
22 # so necessrias!
23 def CI(x): # define a condio inicial
24 if 0 <= x <= 1.0:
25 return 2.0*x*(1.0-x)
26 else:
27 return 0.0
28 for i in range(nx+1): # monta a condio inicial
29 xi = i*dx
30 u[0,i] = CI(xi)
31 u[0].tofile(fou) # imprime a condio inicial
32 old = False
33 new = True
34 c = 2.0 # celeridade da onda
35 couhalf = c*dt/(2.0*dx) # metade do nmero de Courant
36 for n in range(nt): # loop no tempo
37 for i in range(1,nx): # loop no espao
38 u[new,i] = u[old,i] - couhalf*(u[old,i+1] - u[old,i-1])
39 u[new,0] = 0.0
40 u[new,nx] = 0.0
41 u[new].tofile(fou) # imprime uma linha com os novos dados
42 (old,new) = (new,old) # troca os ndices
43 fou.close()
Matemtica Aplicada 236

Listagem 13.2: surf1d-ins.py Seleciona alguns intervalos de tempo da soluo


numrica para plotagem
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ----------------------------------------------------------
4 # surf1d-ins.py: imprime em <arq> <m>+1 sadas de
5 # onda1d-ins a cada <n> intervalos de tempo
6 #
7 # uso: ./surf1d-ins.py <m> <n>
8 # ----------------------------------------------------------
9 from __future__ import print_function
10 from __future__ import division
11 from sys import argv
12 dx = 0.01
13 dt = 0.0005
14 print('# dx = %9.4f' % dx)
15 print('# dy = %9.4f' % dt)
16 nx = int(10.0/dx) # nmero de pontos em x
17 print('# nx = %9d' % nx)
18 m = int(argv[1]) # m sadas
19 n = int(argv[2]) # a cada n intervalos de tempo
20 print('# m = %9d' % m)
21 print('# n = %9d' % n)
22 fin = open('onda1d-ins.dat',
23 'rb') # abre o arquivo com os dados
24 from numpy import fromfile
25 u = fromfile(fin,float,nx+1) # l a condio inicial
26 v = [u] # inicializa a lista da "transposta"
27 for it in range(m): # para <m> instantes:
28 for ir in range(n): # l <ir> vezes, s guarda a ltima
29 u = fromfile(fin,float,nx+1)
30 v.append(u) # guarda a ltima
31 founam = 'surf1d-ins.dat'
32 print(founam)
33 fou = open(founam,'wt') # abre o arquivo de sada
34 for i in range(nx+1):
35 fou.write('%10.6f' % (i*dx)) # escreve o "x"
36 fou.write('%10.6f' % v[0][i]) # escreve a cond inicial
37 for k in range(1,m+1):
38 fou.write('%10.6f' % v[k][i])# escreve o k-simo
39 fou.write('\n')
40 fou.close()
237 13.1 Adveco pura: a onda cinemtica

0.500

0.375

tempo de simulao
u(x, t)

0.250

0.50 0.125

0.00 0.000
0 2 4 6 8 10
x

Figura 13.2: Soluo numrica produzida por onda1d-ins.py, para t = 250t,


500t e 750t.

Por que o esquema utilizado em (13.12) fracassa? Uma forma de obter a resposta
fazer uma anlise de estabilidade de von Neumann.
A anlise de estabilidade de von Neumann consiste primeiramente em observar
que, em um computador real, (13.12) jamais ser calculada com preciso infinita. O
que o computador realmente calcula um valor truncado uni . Por enquanto, ns s
vamos fazer essa distino de notao, entre u e u, aqui, onde ela importa. O erro de
truncamento
ni uni uni . (13.13)
Note que (13.12) se aplica tanto para u quanto para u; subtraindo as equaes resul-
tantes para uin+1 e uin+1 , obtm-se a mesma equao para a evoluo de ni :

Co
in+1 = ni (ni+1 ni1 ), (13.14)
2
onde
ct
Co (13.15)
x
o nmero de Courant. Isso s foi possvel porque (13.12) uma equao linear em
u. Mesmo para equaes no-lineares, entretanto, sempre ser possvel fazer pelo
menos uma anlise local de estabilidade.
O prximo passo da anlise de estabilidade de von Neumman escrever uma srie
de Fourier para ni , na forma

t n = nt,
x i = ix,
N /2
X
ni = l eat n eikl x i , (13.16)
l=1
Matemtica Aplicada 238
p
onde e a base dos logaritmos naturais, i = 1, N = L/x o nmero de pontos
da discretizao em x, e L o tamanho do domnio em x.
Argumentando novamente com a linearidade, desta vez de (13.14), ela vale para
cada modo l de (13.16), donde
Co at ik (i+1)x
l ea(t n +t) eikl ix = l eat n eikl ix l eat n eikl (i1)x ;

l e n e l (13.17)
2

eliminando o fator comum l eat n +ikl ix ,

Co +ik x
eat = 1 e l eikl x

2
= 1 iCo sen kl x. (13.18)

O lado direito um nmero complexo, de maneira que o lado esquerdo tambm tem
que ser! Como concili-los? Fazendo a = + i, e substituindo:

e(i)t = 1 iCo sen kl x;


et cos(t) i sen(t) = 1 iCo sen kl x;
 

et cos(t) = 1, (13.19)
e t
sen(t) = Co sen(kl x). (13.20)

As duas ltimas equaes formam um sistema no-linear nas incgnitas . O sis-


tema pode ser resolvido:

tg(t) = Co sen(kl x) t = arctg Co sen(kl x) .




Note que 6= 0, donde et > 1 via (13.19), e o esquema de diferenas finitas


incondicionalmente instvel.

O mtodo de Lax Uma alternativa que produz um esquema estvel o mtodo de


Lax:
1 n
uin+1 = (ui+1 + uni1 ) Co(uni+1 uni1 ) .

(13.21)
2
Agora que ns j sabemos que esquemas numricos podem ser instveis, deve-
mos fazer uma anlise de estabilidade antes de tentar implementar (13.21) nume-
ricamente. Vamos a isto: utilizando novamente (13.16) e substituindo em (13.21),
temos
1 at ik (i+1)x
l ea(t n +t) eikl ix = + l eat n eikl (i1)x

l e n e l
2
Co l eat n eikl (i+1)x l eat n eikl (i1)x ;

1 +ik x
e at = e l + eikl x Co e+ikl x eikl x ;

2
e at = cos(kl x) iCo sen(kl x) (13.22)

Ns podemos, claro, fazer a = i, mas h um caminho mais rpido: o truque


perceber que se o fator de amplificao eat for um nmero complexo com mdulo
maior que 1, o esquema ser instvel. Desejamos, portanto, que |eat 1|, o que s
possvel se
Co 1, (13.23)
239 13.1 Adveco pura: a onda cinemtica

que o critrio de estabilidade de Courant-Friedrichs-Lewy.


A mgica de (13.21) que ela introduz um pouco de difuso numrica; de fato,
podemos reescrev-la na forma

uin+1 uni uni+1 uni1 uni+1 2uni + uni1


= c +
t 2x 2t
uni+1 uni1
n
x 2 ui+1 2uni + uni1

= c + . (13.24)
2x 2t x 2

No custa repetir: (13.24) idntica a (13.21). Porm, comparando-a com (13.12)


(nosso esquema instvel inicialmente empregado), ns vemos
 n que ela tambm equi-
valente a esta ltima, com o termo adicional x /2t (ui+1 2ui + uni1 )/x 2 . O
2 n

que este termo adicional significa? A resposta uma derivada numrica de ordem 2.
De fato, considere as expanses em srie de Taylor

du 1 d 2u

ui+1 = ui + x + + O(x 2 ),
d x i 2 d x 2 i
du 1 d 2u

ui1 = ui x + + O(x 2 ),
d x i 2 d x 2 i

e some:

d 2u

ui+1 + ui1 = 2ui + x 2 + O(x 2 ),
2
dx i
2
d u u 2ui + ui1

= i+1 + O(x 2 ). (13.25)
2
dx i x 2

Portanto, a equao (13.24) ou seja: o esquema de Lax (13.21) pode ser inter-
pretada tambm como uma soluo aproximada da equao de adveco-difuso

u u 2u
+c =D ,
t c x2

com
x 2

D= .
2t

Note que D tem dimenses de difusividade: J DK = L2 T1 . No entanto: no estamos


ento resolvendo a equao errada? De certa forma, sim: estamos introduzindo um
pouco de difuso na equao para amortecer as oscilaes que aparecero em decor-
rncia da amplificao dos erros de truncamento.
O quanto isto nos prejudica? No muito, desde que o efeito da difuso seja muito
menor que o da adveco que estamos tentando simular. Como a velocidade de advec-
o (fsica; real) que estamos simulando c, precisamos comparar isto com (por
exemplo) a magnitude das velocidades introduzidas pela difuso numrica; devemos
Matemtica Aplicada 240

portanto verificar se
2
D xu2
 1,
c ux
D xu 2
u  1,
c x
D
 c,
x
x 2
 c,
2tx
ct 1
= Co 
x 2
Em outras palavras, ns descobrimos que o critrio para que o esquema seja acurado
do ponto de vista fsico conflitante com o critrio de estabilidade: enquanto que
estabilidade demandava Co < 1, o critrio de que a soluo seja tambm fisicamente
acurada demanda que Co  1/2. Na prtica, isto significa que, para c = 2, ou o
esquema estvel com muita difuso numrica, ou ele instvel. Isto praticamente
elimina a possibilidade de qualquer uso srio de (13.21).
Mesmo assim, vamos program-lo! O programa onda1d-lax.py est mostrado
na listagem 13.3. Ele usa os mesmos valores t = 0,0005 e x = 0,01, ou seja,
Co = 0,10.
O programa gera um arquivo de sada binrio, que por sua vez lido pelo pr-
ximo programa na sequncia, surf1d-lax.py, mostrado na listagem 13.4. O nico
trabalho deste programa selecionar algumas linhas da sada de onda1d-lax.py;
no caso, ns o rodamos com o comando
surf1d-lax.py 3 500 ,
 

o que significa selecionar 3 sadas (alm da condio inicial), de 500 em 500 interva-
los de tempo t. Com isto, ns conseguimos chegar at o instante 0,75 da simulao.
O resultado dos primeiros 1500 intervalos de tempo de simulao mostrado na
figura 13.3. Observe que agora no h oscilaes esprias: o esquema estvel no
tempo. No entanto, a soluo est agora amortecida pela difuso numrica!

Upwind Um esquema que conhecido na literatura como indicado por representar


melhor o termo advectivo em (13.1) o esquema de diferenas regressivas; neste
esquema, chamado de esquema upwind literalmente, corrente acima na lite-
ratura de lngua inglesa, a discretizao utilizada
uin+1 uni uni uni1
= c ,
t x
un+1 n n n

i
= u i
Co u i
u i1
. (13.26)
Claramente, estamos utilizando um esquema de O(x) para a derivada espacial. Ele
um esquema menos acurado que os usados anteriormente, mas se ele ao mesmo
tempo for condicionalmente estvel e no introduzir difuso numrica, o resultado
pode ser melhor para tratar a adveco.
Antes de colocarmos as mos na massa, sabemos que devemos analisar analiti-
camente a estabilidade do esquema. Vamos a isto:
241 13.1 Adveco pura: a onda cinemtica

Listagem 13.3: onda1d-lax.py Soluo de uma onda 1D com um mtodo explcito


laxtvel
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ----------------------------------------------------------
4 # onda1d-lax resolve uma equao de onda com um mtodo
5 # explcito
6 #
7 # uso: ./onda1d-ins.py
8 # ----------------------------------------------------------
9 from __future__ import print_function
10 from __future__ import division
11 fou = open('onda1d-lax.dat','wb')
12 dx = 0.01
13 dt = 0.0005
14 print('# dx = %9.4f' % dx)
15 print('# dy = %9.4f' % dt)
16 from numpy import zeros
17 nx = int(10.0/dx) # nmero de pontos em x
18 nt = int(1.0/dt) # nmero de pontos em t
19 print('# nx = %9d' % nx)
20 print('# nt = %9d' % nt)
21 u = zeros((2,nx+1),float) # apenas 2 posies no tempo
22 # so necessrias!
23 def CI(x): # define a condio inicial
24 if 0 <= x <= 1.0:
25 return 2.0*x*(1.0-x)
26 else:
27 return 0.0
28 for i in range(nx+1): # monta a condio inicial
29 xi = i*dx
30 u[0,i] = CI(xi)
31 u[0].tofile(fou) # imprime a condio inicial
32 old = False
33 new = True
34 c = 2.0 # celeridade da onda
35 cou = c*dt/(dx) # nmero de Courant
36 print("Co = %10.6f" % cou)
37 for n in range(nt): # loop no tempo
38 for i in range(1,nx): # loop no espao
39 u[new,i] = 0.5*( (u[old,i+1] + u[old,i-1]) -
40 cou*(u[old,i+1] - u[old,i-1]) )
41 u[new,0] = 0.0
42 u[new,nx] = 0.0
43 u[new].tofile(fou) # imprime uma linha com os novos dados
44 (old,new) = (new,old) # troca os ndices
45 fou.close()
Matemtica Aplicada 242

Listagem 13.4: surf1d-lax.py Seleciona alguns intervalos de tempo da soluo


numrica para plotagem
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ----------------------------------------------------------
4 # surf1d-lax.py: imprime em <arq> <m>+1 sadas de
5 # onda1d-lax a cada <n> intervalos de tempo
6 #
7 # uso: ./surf1d-lax.py <m> <n>
8 # ----------------------------------------------------------
9 from __future__ import print_function
10 from __future__ import division
11 from sys import argv
12 dx = 0.01
13 dt = 0.0005
14 print('# dx = %9.4f' % dx)
15 print('# dy = %9.4f' % dt)
16 nx = int(10.0/dx) # nmero de pontos em x
17 print('# nx = %9d' % nx)
18 m = int(argv[1]) # m sadas
19 n = int(argv[2]) # a cada n intervalos de tempo
20 print('# m = %9d' % m)
21 print('# n = %9d' % n)
22 fin = open('onda1d-lax.dat',
23 'rb') # abre o arquivo com os dados
24 from numpy import fromfile
25 u = fromfile(fin,float,nx+1) # l a condio inicial
26 v = [u] # inicializa a lista da "transposta"
27 for it in range(m): # para <m> instantes:
28 for ir in range(n): # l <ir> vezes, s guarda a ltima
29 u = fromfile(fin,float,nx+1)
30 v.append(u) # guarda a ltima
31 founam = 'surf1d-lax.dat'
32 print(founam)
33 fou = open(founam,'wt') # abre o arquivo de sada
34 for i in range(nx+1):
35 fou.write('%10.6f' % (i*dx)) # escreve o "x"
36 fou.write('%10.6f' % v[0][i]) # escreve a cond inicial
37 for k in range(1,m+1):
38 fou.write('%10.6f' % v[k][i])# escreve o k-simo
39 fou.write('\n')
40 fou.close()
243 13.1 Adveco pura: a onda cinemtica

1.000

0.750

tempo de simulao
u(x, t)

0.500

0.50 0.250

0.00 0.000
0 2 4 6 8 10
x

Figura 13.3: Soluo numrica produzida por onda1d-lax.py, para t = 500t,


1000t e 1500t.

l ea(t n +t) eikl ix = l eat n eikl ix Co l eat n eikl ix l eat n eikl (i1)x

eat eikl ix = eikl ix Co eikl ix eikl (i1)x


eat = 1 Co 1 eikl x

eat = 1 Co + Co cos(kl x) iCo sen(kl x). (13.27)


Desejamos que o mdulo do fator de amplificao eat seja menor que 1. O m-
dulo (ao quadrado)
2 2
|eat |2 = 1 Co + Co cos(kl x) + Co sen(kl x) .
Para aliviar a notao, faamos
Ck cos(kl x),
Sk sen(kl x).
Ento,
|eat |2 = (CoSk )2 + (CoCk Co + 1)2
= Co2 Sk2 + (Co2 Ck2 + Co2 + 1) + 2(Co2 Ck + CoCk Co)
= Co2 (Sk2 + Ck2 + 1 2Ck ) + 2Co(Ck 1) + 1
= 2Co2 (1 Ck ) + 2Co(Ck 1) + 1.
A condio para que o esquema de diferenas finitas seja estvel , ento,
2Co2 (1 Ck ) + 2Co(Ck 1) + 1 1,
2Co Co(1 Ck ) + (Ck 1) 0,
 

1 cos(kl x) [Co 1] 0,


Co 1
Matemtica Aplicada 244

1.000

0.750

tempo de simulao
u(x, t)

0.500

0.50 0.250

0.00 0.000
0 2 4 6 8 10
x

Figura 13.4: Soluo numrica produzida pelo esquema upwind, para t = 500t,
1000t e 1500t.

Reencontramos, portanto, a condio (13.23), mas em um outro esquema de diferenas


finitas. A lio no deve ser mal interpretada: longe de supor que (13.23) vale sem-
pre, a anlise de estabilidade que deve refeita para cada novo esquema de diferenas
finitas!
O esquema upwind, portanto, condicionalmente estvel, e tudo indica que po-
demos agora implement-lo computacionalmente, e ver no que ele vai dar. Ns uti-
lizamos os mesmos valores de t e de x de antes. As mudanas necessrias nos
cdigos computacionais so bvias, e so deixadas a cargo do(a) leitor(a).
A figura 13.4 mostra o resultado do esquema upwind. Note que ele muito melhor
(para esta equao diferencial) que o esquema de Lax. No entanto, a figura sugere
que algum amortecimento tambm est ocorrendo, embora em grau muito menor.

Exerccios Propostos

13.1 Escreva o programa onda1d-upw e surfa1d-upw, que implementam o esquema upwind.


Reproduza a figura 13.4.

13.2 Calcule a difusividade numrica introduzida pelo esquema upwind.

13.2 Difuso pura

Considere agora a equao da difuso,


u 2u
=D , (13.28)
t x2
com condies iniciais e de contorno
u(x, 0) = f (x) (13.29)
u(0, t) = u(L, t) = 0. (13.30)
245 13.2 Difuso pura

Esta soluo ser vista no captulo 17:



X
n
2 2 2
t nx
u(x, t) = An e L2 sen , (13.31)
n=1
L
L
2 nx
Z
An = f (x) sen dx. (13.32)
L 0
L

Em particular, se

D = 2,
L = 1,
f (x) = 2x(1 x),
Z1
8
An = 2 2x(1 x) sen(nx) dx = 3 3
[1 (1)n ] .
0
n

Todos os An s pares se anulam. Fique ento apenas com os mpares:


16
A2n+1 = ,
3 (2n + 1)3

X 16 2
2 )t
u(x, t) = 3 3
e(2(2n+1) sen ((2n + 1)x) (13.33)
n=0
(2n + 1)

O programa difusao1d-ana.py, mostrado na listagem 13.5, implementa a solu-


o analtica para t = 0,0005 e x = 0,001.
Da mesma maneira que os programas surf1d*.py, o programa divisao1d-ana.py,
mostrado na listagem 13.6, seleciona alguns instantes de tempo da soluo analtica
para visualizao:
divisao1d-ana.py 3 100 .
 

A figura 13.5 mostra o resultado da soluo numrica para t = 0, t = 0,05,


t = 0,10 e t = 0,15. Este praticamente o fim do processo difusivo, com a soluo
analtica tendendo rapidamente para zero.

Esquema explcito Talvez o esquema explcito mais bvio para discretizar (13.28)
seja
uin+1 uni uni+1 2uni + uni1
=D . (13.34)
t x 2
A derivada parcial em relao ao tempo de O(t), enquanto que a derivada segunda
parcial em relao ao espao , como vimos em (13.25), de O(x 2 ). Mas no nos
preocupemos muito, ainda, com a acurcia do esquema numrico. Nossa primeira
preocupao, como voc j sabe, outra: o esquema (13.34) estvel?
Explicitamos uin+1 em (13.34):

uin+1 = uni + Fo uni+1 2uni + uni1 ,



(13.35)

onde
Dt
Fo = (13.36)
x 2
Matemtica Aplicada 246

Listagem 13.5: difusao1d-ana.py Soluo analtica da equao da difuso


1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ----------------------------------------------------------
4 # difusao1d-ana: soluo analtica de
5 #
6 # du/dt = D du^2/dx^2
7 #
8 # u(x,0) = 2x(1-x)
9 # u(0,t) = 0
10 # u(1,t) = 0
11 #
12 # uso: ./difusao1d-ana.py
13 # ----------------------------------------------------------
14 from __future__ import print_function
15 from __future__ import division
16 fou = open('difusao1d-ana.dat','wb')
17 dx = 0.001
18 dt = 0.0005
19 print('# dx = %9.4f' % dx)
20 print('# dy = %9.4f' % dt)
21 nx = int(1.0/dx) # nmero de pontos em x
22 nt = int(1.0/dt) # nmero de pontos em t
23 print('# nx = %9d' % nx)
24 print('# nt = %9d' % nt)
25 from math import pi, sin, exp
26 epsilon = 1.0e-6 # preciso da soluo analtica
27 dpiq = 2*pi*pi # 2pi^2
28 dzpic = 16/(pi*pi*pi) # 16/pi^3
29 def ana(x,t):
30 s = 0.0
31 ds = epsilon
32 n = 0
33 while abs(ds) >= epsilon:
34 dnm1 = 2*n + 1 # (2n+1)
35 dnm1q = dnm1*dnm1 # (2n+1)^2
36 dnm1c = dnm1q*dnm1 # (2n+1)^3
37 ds = exp(-dnm1q*dpiq*t)
38 ds *= sin(dnm1*pi*x)
39 ds /= dnm1c
40 s += ds
41 n += 1
42 return s*dzpic
43 from numpy import zeros
44 u = zeros(nx+1,float) # um array para conter a soluo
45 for n in range(nt+1): # loop no tempo
46 t = n*dt
47 print(t)
48 for i in range(nx+1): # loop no espao
49 xi = i*dx
50 u[i] = ana(xi,t)
51 u.tofile(fou) # imprime uma linha com os novos dados
52 fou.close()
247 13.2 Difuso pura

Listagem 13.6: divisao1d-ana.py Seleciona alguns instantes de tempo da soluo


analtica para visualizao
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ----------------------------------------------------------
4 # divisao1d-ana.py: imprime em <arq> <m>+1 sadas de
5 # difusao1d-ana a cada <n> intervalos de tempo
6 #
7 # uso: ./divisao1d-ana.py <m> <n>
8 # ----------------------------------------------------------
9 from __future__ import print_function
10 from __future__ import division
11 from sys import argv
12 dx = 0.001
13 dt = 0.0005
14 print('# dx = %9.4f' % dx)
15 print('# dt = %9.4f' % dt)
16 nx = int(1.0/dx) # nmero de pontos em x
17 print('# nx = %9d' % nx)
18 m = int(argv[1]) # m sadas
19 n = int(argv[2]) # a cada n intervalos de tempo
20 print('# m = %9d' % m)
21 print('# n = %9d' % n)
22 fin = open('difusao1d-ana.dat',
23 'rb') # abre o arquivo com os dados
24 from numpy import fromfile
25 u = fromfile(fin,float,nx+1) # l a condio inicial
26 v = [u] # inicializa a lista da "transposta"
27 for it in range(m): # para <m> instantes:
28 for ir in range(n): # l <ir> vezes, s guarda a ltima
29 u = fromfile(fin,float,nx+1)
30 v.append(u) # guarda a ltima
31 founam = 'divisao1d-ana.dat'
32 print(founam)
33 fou = open(founam,'wt') # abre o arquivo de sada
34 for i in range(nx+1):
35 fou.write('%10.6f' % (i*dx)) # escreve o "x"
36 fou.write('%10.6f' % v[0][i]) # escreve a cond inicial
37 for k in range(1,m+1):
38 fou.write('%10.6f' % v[k][i])# escreve o k-simo
39 fou.write('\n')
40 fou.close()
Matemtica Aplicada 248

0.5

t = 0,00
0.4

0.3
u(x, t)

0.2
t = 0,05

0.1
t = 0,10
t = 0,15
0.0
0 0.2 0.4 0.6 0.8 1
x

Figura 13.5: Soluo analtica da equao de difuso para t = 0, t = 0,05, t = 0,10 e


t = 0,15.

o nmero de Fourier de grade (El-Kadi e Ling, 1993). A anlise de estabiliade de von


Neumann agora produz

l ea(t n +t) eikl ix = l eat n eikl ix +


Fo l eat n eikl (i+1)x 2l eat n eikl ix + l eat n eikl (i1)x ,

eat = 1 + Fo e+ikl x 2 + eikl x


= 1 + 2Fo cos(kl x) 1
 

kl x
 
2
= 1 4Fo sen (13.37)
2

A anlise de estabilidade requer que |e at | < 1:

kl x kl x
   
at 2 2 2 4
|e | = 1 8Fo sen + 16Fo sen <1
2 2
ou
kl x kl x
   
2 2 4
8Fo sen + 16Fo sen < 0,
2 2
kl x kl x
   
2 2
8Fo sen 1 + 2Fo sen < 0,
2 2
1
Fo < . (13.38)
2
Podemos agora calcular o nmero de Fourier que utilizamos para plotar a soluo
analtica (verifique nas listagens 13.5 e 13.6):
2 0,0005
Fo = = 1000.
(0,001)2
249 13.2 Difuso pura

Listagem 13.7: difusao1d-exp.py Soluo numrica da equao da difuso: m-


todo explcito.
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ----------------------------------------------------------
4 # difusao1d-exp resolve uma equao de difuso com um mtodo
5 # explcito
6 #
7 # uso: ./difusao1d-exp.py
8 # ----------------------------------------------------------
9 from __future__ import print_function
10 from __future__ import division
11 fou = open('difusao1d-exp.dat','wb')
12 dx = 0.01
13 dt = 0.00001
14 print('# dx = %9.4f' % dx)
15 print('# dy = %9.4f' % dt)
16 from numpy import zeros
17 nx = int(round(1.0/dx,0)) # nmero de pontos em x
18 nt = int(round(1.0/dt,0)) # nmero de pontos em t
19 print('# nx = %9d' % nx)
20 print('# nt = %9d' % nt)
21 u = zeros((2,nx+1),float) # apenas 2 posies no tempo
22 # so necessrias!
23 def CI(x): # define a condio inicial
24 if 0 <= x <= 1.0:
25 return 2.0*x*(1.0-x)
26 else:
27 return 0.0
28 for i in range(nx+1): # monta a condio inicial
29 xi = i*dx
30 u[0,i] = CI(xi)
31 u[0].tofile(fou) # imprime a condio inicial
32 old = False
33 new = True
34 D = 2.0 # celeridade da onda
35 Fon = D*dt/((dx)**2) # nmero de Fourier
36 print("Fo = %10.6f" % Fon)
37 for n in range(nt): # loop no tempo
38 print(n)
39 for i in range(1,nx): # loop no espao
40 u[new,i] = u[old,i] + Fon*(u[old,i+1] - 2*u[old,i] + u[old,i-1])
41 u[new,0] = 0.0 # condio de contorno, x = 0
42 u[new,nx] = 0.0 # condio de contorno, x = 1
43 u[new].tofile(fou) # imprime uma linha com os novos dados
44 (old,new) = (new,old) # troca os ndices
45 fou.close()

Utilizar os valores x = 0,0005 e x = 0,001 levaria a um esquema instvel. Preci-


samos diminuir t e/ou aumentar x. Com t = 0,00001 e x = 0,01,
2 0,00001
Fo = = 0,2 < 0,5 (OK).
(0,01)2

Repare que Fo < 1/2 um critrio de estabilidade muito mais exigente do que
Co < 1/2 (para D = 2). Ns esperamos que nosso esquema explcito agora rode muito
lentamente. Mas vamos implement-lo. O programa que implementa o esquema o
difusao1d-exp.py, mostrado na listagem 13.7.
O programa divisao1d-exp.py, mostrado na listagem 13.8, seleciona alguns
instantes de tempo da soluo analtica para visualizao:
divisao1d-exp 3 5000 .
 
Matemtica Aplicada 250

Listagem 13.8: divisao1d-exp.py Seleciona alguns instantes de tempo da soluo


analtica para visualizao
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ----------------------------------------------------------
4 # divisao1d-exp.py: imprime em <arq> <m>+1 sadas de
5 # difusao1d-exp a cada <n> intervalos de tempo
6 #
7 # uso: ./divisao1d-exp.py <m> <n>
8 # ----------------------------------------------------------
9 from __future__ import print_function
10 from __future__ import division
11 from sys import argv
12 dx = 0.01
13 dt = 0.00001
14 print('# dx = %9.4f' % dx)
15 print('# dt = %9.4f' % dt)
16 nx = int(round(1.0/dx,0)) # nmero de pontos em x
17 nt = int(round(1.0/dt,0)) # nmero de pontos em t
18 print('# nx = %9d' % nx)
19 m = int(argv[1]) # m sadas
20 n = int(argv[2]) # a cada n intervalos de tempo
21 print('# m = %9d' % m)
22 print('# n = %9d' % n)
23 fin = open('difusao1d-exp.dat',
24 'rb') # abre o arquivo com os dados
25 from numpy import fromfile
26 u = fromfile(fin,float,nx+1) # l a condio inicial
27 v = [u] # inicializa a lista da "transposta"
28 for it in range(m): # para <m> instantes:
29 for ir in range(n): # l <ir> vezes, s guarda a ltima
30 u = fromfile(fin,float,nx+1)
31 v.append(u) # guarda a ltima
32 founam = 'divisao1d-exp.dat'
33 fou = open(founam,'wt') # abre o arquivo de sada
34 for i in range(nx+1):
35 fou.write('%10.6f' % (i*dx)) # escreve o "x"
36 fou.write('%10.6f' % v[0][i]) # escreve a cond inicial
37 for k in range(1,m+1):
38 fou.write('%10.6f' % v[k][i])# escreve o k-simo
39 fou.write('\n')
40 fou.close()
251 13.2 Difuso pura

0.5

t = 0,00
0.4

0.3
u(x, t)

0.2
t = 0,05

0.1
t = 0,10
t = 0,15
0.0
0 0.2 0.4 0.6 0.8 1
x

Figura 13.6: Soluo numrica com o mtodo explcito (13.35) (crculos) versus a
soluo analtica (linha cheia) da equao de difuso para t = 0, t = 0,05, t = 0,10 e
t = 0,15. Apenas 1 a cada 5 pontos da grade numrica so mostrados, para facilitar
a comparao com a soluo analtica.

O resultado da soluo numrica com o mtodo explcito est mostrado na figura


13.6: ele impressionantemente bom, embora seja computacionalmente muito caro.
A escolha judiciosa de t e x para obeder ao critrio (13.38) foi fundamental para
a obteno de um bom resultado de primeira, sem a necessidade dolorosa de fi-
car tentando diversas combinaes at que o esquema se estabilize e produza bons
resultados.

Esquemas implcitos Embora o esquema explcito que ns utilizamos acima seja


acurado, ele lento se voc programou e rodou difusao1d-exp.py, deve ter no-
tado alguma demora para o programa rodar. Embora nossos computadores estejam
ficando a cada dia mais rpidos, isso no desculpa para utilizar mal nossos recursos
computacionais ( claro que, ao utilizarmos uma linguagem interpretada Python
para programar, ns j estamos utilizando muito mal nossos recursos; no entanto,
nosso argumento didtico: com uma linguagem mais simples, podemos aprender
mais rpido e errar menos. Alm disso, todos os ganhos relativos que obtivermos se
mantero em qualquer outra linguagem)
Vamos portanto fazer uma mudana fundamental nos nossos esquemas de dife-
renas finitas: vamos calcular a derivada espacial no instante n + 1:

un+1
i
uni un+1
i+1
2un+1
i
+ un+1
i1
=D ,
t x 2
un+1
i
uni = Fo(un+1
i+1
2un+1
i
+ un+1
i1
),
n+1
Foui1 + (1 + 2Fo)un+1
i
Foun+1
i+1
= uni . (13.39)

Reveja a discretizao (13.5)(13.9): para i = 1, . . . , Nx 1, (13.39) acopla 3


valores das incgnitas un+1 no instante n + 1. Quando i = 0, e quando i = Nx , no
Matemtica Aplicada 252

podemos utilizar (13.39), porque no existem os ndices i = 1, e i = Nx +1. Quando


i = 1 e i = Nx 1, (13.39) precisa ser modificada, para a introduo das condies de
contorno: como u0n = 0 e unNx = 0 para qualquer n, teremos

(1 + 2Fo)u1n+1 Fou2n+1 = u1n , (13.40)


FouNn+1
x 2
+ (1 + 2Fo)un+1
N x 1
= unNx 1 . (13.41)

Em resumo, nossas incgnitas so u1n+1 , u2n+1 , . . . un+1


N x 1
(Nx 1 incgnitas), e seu clculo
envolve a soluo do sistema de equaes
n+1 n
1 + 2Fo Fo 0 ... 0 0 u u

1n+1 1n
Fo 1 + 2Fo Fo 0 ... 0 u 2
u
.2

.. .. .


. .

.. = .. (13.42)
n+1 un

0 ... 0 Fo 1 + 2Fo Fo u

Nx 2 Nx 2
0 0 ... 0 Fo 1 + 2Fo un+1
N x 1
unNx 1

A anlise de estabilidade de von Neumann procede agora da maneira usual:

in+1 = ni + Fo(n+1
i+1
2n+1
i
+ n+1
i1
)
l e a(t n +t) eikl ix = l e at n eikl ix
+ Fo l e a(t n +t) eikl (i+1)x 2l e a (t n + t)eikl ix

+l e a(t n +t) eikl (i1)x ,


e at = 1 + e at Fo eikl x 2 + eikl x ,

e at = 1 + e at 2Fo cos(kl x) 1 ,


kl x
 
at at 2
e = 1 e 4Fo sin ,
2
kl x
  
at 2
e 1 + 4Fo sin = 1,
2
1
|e at | = k x 1 sempre. (13.43)
1 + 4Fo sin2 l 2

Portanto, o esquema implcito (13.39) incondicionalmente estvel, e temos con-


fiana de que o programa correspondente no se instabilizar.
Existem vrias coisas atraentes para um programador em (13.42). Em primeiro
lugar, a matriz do sistema uma matriz banda tridiagonal; sistemas lineares com este
tipo de matriz so particularmente simples de resolver, e esto disponveis na lite-
ratura (por exemplo: Press et al., 1992, seo 2.4, subrotina tridag). Em segundo
lugar, a matriz do sistema constante: ela s precisa ser montada uma vez no pro-
grama, o que torna a soluo numrica potencialmente muito rpida.
Ns vamos comear, ento, construindo um pequeno mdulo, convenientemente
denominado alglin.py, que exporta a funo tridag, que resolve um sistema tridi-
agonal, mostrado na listagem 13.9.
Em seguida, o programa difusao1d-imp.py resolve o problema com o mtodo
implcito. Ele est mostrado na listagem 13.10. A principal novidade est nas linhas
4246, e depois novamente na linha 56. Em Python e Numpy, possvel especi-
ficar sub-listas, e sub-arrays, com um dispositivo denominado slicing, que torna a
253 13.2 Difuso pura

Listagem 13.9: alglin.py Exporta uma rotina que resolve um sistema tridiagonal,
baseado em Press et al. (1992)
1 # -*- coding: iso-8859-1 -*-
2 # ------------------------------------------------------------------------------
3 # alglin.py implementa uma soluo de um sistema linear com matriz tridiagonal
4 # ------------------------------------------------------------------------------
5 from numpy import zeros
6 def tridag(A,y): # A,y tm que ser arrays!
7 m = A.shape[0] # garante que A representa uma
8 n = A.shape[1] # matriz tridiagonal
9 assert(m == 3) # garante que todos os tamanhos esto OK
10 o = y.shape[0]
11 assert (n == o)
12 x = zeros(n,float) # vetor de trabalho: vai retornar a soluo
13 gam = zeros(n,float) # vetor de trabalho: vai ficar por aqui
14 if A[1,0] == 0.0 :
15 exit("Erro 1 em tridag")
16 bet = A[1,0]
17 x[0] = y[0]/bet
18 for j in range(1,n):
19 gam[j] = A[2,j-1]/bet
20 bet = A[1,j] - A[0,j]*gam[j]
21 if (bet == 0.0):
22 exit("Erro 2 em tridag")
23 x[j] = (y[j] - A[0,j]*x[j-1])/bet
24 for j in range(n-2,-1,-1):
25 x[j] -= gam[j+1]*x[j+1]
26 return x

programao mais compacta e clara. Por exemplo, na linha 43, todos os elementos
A[0,1]. . . A[0,nx-1] recebem o valor -Fon.
Existe um programa divisao1d-imp.py, mas ele no precisa ser mostrado aqui,
porque as modificaes, por exemplo a partir de divisao1d-exp.py, so demasia-
damente triviais para justificarem o gasto adicional de papel. Para t = 0,001, e
x = 0,01, o resultado do mtodo implcito est mostrado na figura 13.7
Nada mal, para uma economia de 100 vezes (em relao ao mtodo explcito) em
passos de tempo! (Note entretanto que a soluo, em cada passo de tempo, um
pouco mais custosa, por envolver a soluo de um sistema de equaes acopladas,
ainda que tridiagonal.)

Crank Nicholson A derivada espacial em (13.28) aproximada, no esquema impl-


cito (13.39), por um esquema de O(x 2 ). A derivada temporal, por sua vez, apenas
de O(t). Mas possvel consertar isso! A idia substituir (13.39) por
n n n n+1 n+1 n+1
un+1 u n
D u 2u u u 2u u

i i i+1 i
+ i1 i+1 i
+ i1
= + ,
t 2 x 2 x 2
Fo n
uin+1 = uni + ui+1 2uni + uni1 + un+1 2un+1 + un+1

i+1 i i1
. (13.44)
2
Com esta mudana simples, a derivada espacial agora uma mdia das derivadas em
n e n + 1, ou seja: ela est centrada em n + 1/2. Com isto, a derivada temporal do
lado esquerdo torna-se, na prtica, um esquema de ordem 2 centrado em n + 1/2!
Como sempre, nosso trabalho agora verificar a estabilidade do esquema num-
rico. Para isto, fazemos
Fo n+1 Fo n
in+1 i+1 2in+1 + n+1 n n n

i1
= i
+ i+1
2 i
+ i1
,
2 2
Matemtica Aplicada 254

Listagem 13.10: difusao1d-imp.py Soluo numrica da equao da difuso:


mtodo implcito.
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ----------------------------------------------------------
4 # difusao1d-imp resolve uma equao de difuso com um mtodo
5 # implcito
6 #
7 # uso: ./difusao1d-imp.py
8 # ----------------------------------------------------------
9 from __future__ import print_function
10 from __future__ import division
11 fou = open('difusao1d-imp.dat','wb')
12 dx = 0.01 # define a discretizao em x
13 dt = 0.001 # define a discretizao em t
14 print('# dx = %9.4f' % dx)
15 print('# dy = %9.4f' % dt)
16 nx = int(round(1.0/dx,0)) # nmero de pontos em x
17 nt = int(round(1.0/dt,0)) # nmero de pontos em t
18 print('# nx = %9d' % nx)
19 print('# nt = %9d' % nt)
20 from numpy import zeros
21 u = zeros((2,nx+1),float) # apenas 2 posies no tempo
22 # so necessrias!
23 def CI(x): # define a condio inicial
24 if 0 <= x <= 1.0:
25 return 2.0*x*(1.0-x)
26 else:
27 return 0.0
28 for i in range(nx+1): # monta a condio inicial
29 xi = i*dx
30 u[0,i] = CI(xi)
31 u[0].tofile(fou) # imprime a condio inicial
32 old = False
33 new = True
34 D = 2.0 # difusividade
35 Fon = D*dt/((dx)**2) # nmero de Fourier
36 print("Fo = %10.6f" % Fon)
37 A = zeros((3,nx-1),float) # cria a matriz do sistema
38 # ------------------------------------------------------------------------------
39 # cuidado, "linha" e "coluna" abaixo no significam as reais linhas e colunas
40 # do sistema de equaes, mas sim a forma de armazenar uma matriz tridiagonal
41 # ------------------------------------------------------------------------------
42 A[0,0] = 0.0 # zera A[0,0]
43 A[0,1:nx-1] = -Fon # preenche o fim da 1a linha
44 A[1,0:nx-1] = 1.0 + 2*Fon # preenche a segunda linha
45 A[2,0:nx-2] = -Fon # preenche o incio da 2a linha
46 A[2,nx-2] = 0.0 # zera A[2,nx-2]
47 # ------------------------------------------------------------------------------
48 # importa uma traduo de tridag de Numerical Recipes para Python
49 # ------------------------------------------------------------------------------
50 from alglin import tridag
51 for n in range(nt): # loop no tempo
52 print(n)
53 # ------------------------------------------------------------------------------
54 # ateno: calcula apenas os pontos internos de u!
55 # ------------------------------------------------------------------------------
56 u[new,1:nx] = tridag(A,u[old,1:nx])
57 u[new,0] = 0.0 # condio de contorno, x = 0
58 u[new,nx] = 0.0 # condio de contorno, x = 1
59 u[new].tofile(fou) # imprime uma linha com os novos dados
60 (old,new) = (new,old) # troca os ndices
61 fou.close() # fecha o arquivo de sada, e fim.
255 13.2 Difuso pura

0.5

t = 0,00
0.4

0.3
u(x, t)

0.2
t = 0,05

0.1
t = 0,10
t = 0,15
0.0
0 0.2 0.4 0.6 0.8 1
x

Figura 13.7: Soluo numrica com o mtodo implcito (13.39) (crculos) versus a
soluo analtica (linha cheia) da equao de difuso para t = 0, t = 0,05, t = 0,10 e
t = 0,15. Apenas 1 a cada 5 pontos da grade numrica so mostrados, para facilitar
a comparao com a soluo analtica.

e substitumos um modo de Fourier:


Fo ik (i+1)x
 
a(t n +t) ikl ix ikl ix ikl (i1)x

l e e e l
2e +e =
2
Fo ik (i+1)x
 
at n ikl ix ikl ix ikl (i1)x

l e e + e l
2e +e
2

Fo Fo
   
at ikl x ikl x ikl x ikl x

e 1 e 2+e = 1+ e 2+e
2 2
e at 1 Fo cos(kl x) 1 = 1 + Fo cos(kl x) 1
   

kl x kl x
     
at 2 2
e 1 + 2Fo sin = 1 2Fo sin
2 2
2 kl x

1 2Fo sin
e at = 2 .
2 kl x
1 + 2Fo sin 2

fcil notar que |e at | < 1, e o esquema numrico de Crank-Nicholson incondi-


cionalmente estvel. O esquema numrico de Crank-Nicholson similar a (13.39):
Fo n+1 Fo Fo Fo
ui1 + (1 + Fo)uin+1 un+1 i+1
= uni1 + (1 Fo)uni + uni+1 (13.45)
2 2 2 2
Para as condies de contorno de (13.30), as linhas correspondentes a i = 1 e i =
Nx 1 so
Fo Fo
(1 + Fo)u1n+1 u2n+1 = (1 2Fo)u1n + u2n , (13.46)
2 2
Fou n+1 Fo n
uNx 2 + (1 + Fo)un+1N x 1
= uNx 2 + (1 Fo)unNx 1 (13.47)
2 2
Matemtica Aplicada 256

0.5

t = 0,00
0.4

0.3
u(x, t)

0.2
t = 0,05

0.1
t = 0,10
t = 0,15
0.0
0 0.2 0.4 0.6 0.8 1
x

Figura 13.8: Soluo numrica com o mtodo de Crank-Nicholson ( (13.45)) (cr-


culos) versus a soluo analtica (linha cheia) da equao de difuso para t = 0,
t = 0,05, t = 0,10 e t = 0,15. Apenas 1 a cada 5 pontos da grade numrica so
mostrados, para facilitar a comparao com a soluo analtica.

As mudanas no cdigo de difusao-imp.py so relativamente fceis de se identificar.


O cdigo do programa que implementa o esquema numrico de Crank-Nicholson,
difusao1d-ckn.py, mostrado na listagem 13.11.

A grande novidade computacional de difusao1d-ckn.py a linha 56: com os


arrays proporcionados por Numpy, possvel escrever (13.45) vetorialmente: note
que no h necessidade de fazer um loop em x para calcular cada elemento b[i]
individualmente. O mesmo tipo de facilidade est disponvel em FORTRAN90, FOR-
TRAN95, etc.. Com isso, a implementao computacional dos clculos gerada por
Numpy (ou pelo compilador FORTRAN) tambm potencialmente mais eficiente.
O mtodo de Crank-Nicholson possui acurcia O(t)2 , portanto ele deve ser ca-
paz de dar passos ainda mais largos no tempo que o mtodo implcito (13.39); no
programa difusao1d-ckn.py, ns especificamos um passo de tempo 5 vezes maior
do que em difusao1d-imp.py.
O resultado uma soluo cerca de 5 vezes mais rpida (embora, novamente, haja
mais contas agora para calcular o vetor de carga b), e mostrado na figura 13.8.

Exemplo 13.1 Dada a equao da difuso unidimensional

u 2u
=D , 0 x L,
t x2
257 13.2 Difuso pura

Listagem 13.11: difusao1d-ckn.py Soluo numrica da equao da difuso:


esquema de Crank-Nicholson.
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ----------------------------------------------------------
4 # difusao1d-ckn resolve uma equao de difuso com o mtodo
5 # de Crank-Nicholson
6 #
7 # uso: ./difusao1d-ckn.py
8 # ----------------------------------------------------------
9 from __future__ import print_function
10 from __future__ import division
11 fou = open('difusao1d-ckn.dat','wb')
12 dx = 0.01 # define a discretizao em x
13 dt = 0.005 # define a discretizao em t
14 print('# dx = %9.4f' % dx)
15 print('# dt = %9.4f' % dt)
16 nx = int(round(1.0/dx,0)) # nmero de pontos em x
17 nt = int(round(1.0/dt,0)) # nmero de pontos em t
18 print('# nx = %9d' % nx)
19 print('# nt = %9d' % nt)
20 from numpy import zeros
21 u = zeros((2,nx+1),float) # apenas 2 posies no tempo
22 # so necessrias!
23 def CI(x): # define a condio inicial
24 if 0 <= x <= 1.0:
25 return 2.0*x*(1.0-x)
26 else:
27 return 0.0
28 for i in range(nx+1): # monta a condio inicial
29 xi = i*dx
30 u[0,i] = CI(xi)
31 u[0].tofile(fou) # imprime a condio inicial
32 old = False
33 new = True
34 D = 2.0 # difusividade
35 Fon = D*dt/((dx)**2) # nmero de Fourier
36 print("Fo = %10.6f" % Fon)
37 A = zeros((3,nx-1),float) # cria a matriz do sistema
38 # ------------------------------------------------------------------------------
39 # cuidado, "linha" e "coluna" abaixo no significam as reais linhas e colunas
40 # do sistema de equaes, mas sim a forma de armazenar uma matriz tridiagonal
41 # ------------------------------------------------------------------------------
42 A[0,0] = 0.0 # zera A[0,0]
43 A[0,1:nx-1] = -Fon/2.0 # preenche o fim da 1a linha
44 A[1,0:nx-1] = 1.0 + Fon # preenche a segunda linha
45 A[2,0:nx-2] = -Fon/2.0 # preenche o incio da 2a linha
46 A[2,nx-2] = 0.0 # zera A[2,nx-2]
47 # ------------------------------------------------------------------------------
48 # importa uma traduo de tridag de Numerical Recipes para Python
49 # ------------------------------------------------------------------------------
50 from alglin import tridag
51 for n in range(nt): # loop no tempo
52 print(n)
53 # ------------------------------------------------------------------------------
54 # recalcula o vetor de carga vetorialmente
55 # ------------------------------------------------------------------------------
56 b = (Fon/2)*u[old,0:nx-1] + (1 - Fon)*u[old,1:nx] + (Fon/2)*u[old,2:nx+1]
57 # ------------------------------------------------------------------------------
58 # ateno: calcula apenas os pontos internos de u!
59 # ------------------------------------------------------------------------------
60 u[new,1:nx] = tridag(A,b)
61 u[new,0] = 0.0 # condio de contorno, x = 0
62 u[new,nx] = 0.0 # condio de contorno, x = 1
63 u[new].tofile(fou) # imprime uma linha com os novos dados
64 (old,new) = (new,old) # troca os ndices
65 fou.close() # fecha o arquivo de sada, e fim.
Matemtica Aplicada 258

e o esquema de discretizao

x = L/Nx ,
x i = ix, i = 0, . . . , Nx ,
t n = tt,
uni = u(x i , t n ),
1 n+1 n n+1 n
uni+1 2uni + uni1
(ui+1 ui+1 ) + (ui1 ui1 ) = D ,
2t x 2
obtenha o critrio de estabilidade por meio de uma anlise de estabilidade de von Neumann.
SOLUO
Suponha que a equao diferencial se aplique ao erro:

ni =
X
l e at n eikl ix
l

Ento
1
(l e a(t n +t) eikl (i+1)x l e at n eikl (i+1)x ) + (l e a(t n +t) eikl (i1)x l e at n eikl (i1)x =

2t
l e at n eikl (i+1)x 2l e at n eikl ix + l e at n eikl (i1)x
D ;
x 2
1
(l e at eikl (i+1)x l eikl (i+1)x ) + (l e at eikl (i1)x l eikl (i1)x =

2t
l eikl (i+1)x 2l eikl ix + l eikl (i1)x
D ;
x 2
1 at ik (i+1)x
eikl (i+1)x ) + ( e at eikl (i1)x eikl (i1)x =

(e e l
2t
eikl (i+1)x 2 eikl ix + eikl (i1)x
D ;
x 2
( e at eikl (i+1)x eikl (i+1)x ) + ( e at eikl (i1)x eikl (i1)x =

2Dt ik (i+1)x
2 eikl ix + eikl (i1)x .

2
e l
x
Segue-se que

e at eikl (i+1)x + eikl (i1)x = eikl (i+1)x + eikl (i1)x + 2Fo eikl (i+1)x 2 eikl ix + eikl (i1)x

e at eikl x + eikl x = eikl x + eikl x + 2Fo eikl x 2 + eikl x


2 cos(kl x) 2
e at = 1 + 2Fo
2 cos(kl x)
cos(kl x) 1
= 1 + 2Fo
cos(kl x)
sen2 (kl x/2)
= 1 4Fo .
cos(kl x)

A funo
sen2 (x/2)
f (x) =
cos(x)
possui singularidades em /2 + k, e muda de sinal em torno destas singularidades: no
possvel garantir que |e at | < 1 uniformemente, e o esquema incondicionalmente instvel.
259 13.2 Difuso pura

Exerccios Propostos

13.3 Considere a equao diferencial parcial

u 2u
=D , 0 x L,
t x2

com condies iniciais e de contorno

u
u(x, 0) = 0, u(0, t) = c, (L, t) = 0,
x

onde c uma constante. Dado o esquema de discretizao implcito clssico,

uin+1 uni un+1 n+1


i+1 2ui + un+1
i1
=D
t x 2

para Nx = 8, obtenha o sistema de equaes lineares

[A][u]n+1 = [ b]

onde os Ai, j s dependem do nmero de grade de Fourier, e os bi s dependem dos uni s. Em


outras palavras, escreva explicitamente a matriz quadrada [A] e a matriz-coluna [ b] para
Nx = 8.

13.4 Dada a equao diferencial no-linear de Boussinesq,

h h
 
= h ,
t x x
h(x, 0) = H,
h(0, t) = H0 ,
h

= 0,
x x=1

obtenha uma discretizao linearizada da mesma em diferenas finitas do tipo

h(x i , t n ) = h(ix, nt) = hni

da seguinte forma:

discretize a derivada parcial em relao ao tempo com um esquema progressivo no


tempo entre n e n + 1;

aproxime h dentro do colchete por hni (este o truque que lineariza o esquema de
diferenas finitas) e mantenha-o assim;

utilize esquemas de diferenas finitas implcitos centrados no espao para as derivadas


parciais em relao a x, exceto no termo hni do item anterior.

NO MEXA COM AS CONDIES DE CONTORNO.


Matemtica Aplicada 260

13.3 Difuso em 2 Dimenses: ADI, e equaes elticas

Considere a equao da difuso em 2 dimenses,


2
u u 2u

=D + . (13.48)
t x2 y2

Como sempre, ns queremos ser muito concretos, e trabalhar com um problema


que possua soluo analtica. Considere ento a condio inicial

(x 2 + y 2 )

u(x, y, 0) = u0 exp ; (13.49)
L2
a soluo analtica

u0 (x 2 + y 2 )

u(x, y, t) = exp . (13.50)
1 + 4t D/L 2 L 2 + 4Dt

Na verdade esta soluo se espalha por todo o plano x y, mas ns podemos trabalhar
com um problema finito em x e y, por exemplo, fazendo L x L, L y L, e
impondo condies de contorno que se ajustem exatamente soluo analtica:

u0 (L 2 + y 2 )

u(L, y, t) = exp 2 , (13.51)
1 + 4t D/L 2 L + 4Dt
u0 (L 2 + y 2 )

u(L, y, t) = exp 2 , (13.52)
1 + 4t D/L 2 L + 4Dt
u0 (x 2 + L 2 )

u(x, L, t) = exp 2 , (13.53)
1 + 4t D/L 2 L + 4Dt
u0 (x 2 + L 2 )

u(x, L, t) = exp 2 . (13.54)
1 + 4t D/L 2 L + 4Dt

Agora, ns vamos fazer D = 2 (como antes) e L = 1, e resolver o problema numeri-


camente. Nossa escolha recair sobre um mtodo simples, e de O(t)2 , denominado
ADI (alternating-direction implicit). Este mtodo nos proporcionar um exemplo de
uma tcnica denominada operator splitting ou time splitting, que ns vamos traduzir
como separao de operadores Esta tcnica consiste em marchar implicitamente em
uma dimenso espacial de cada vez, mantendo a outra dimenso explcita. Por-
tanto, ns vamos utilizar dois esquemas diferentes de diferenas finitas (na prtica),
para resolver o problema! Ei-los

ui,n+1 uni, j n+1


ui+1, 2un+1 + un+1 uni, j+1 2uni, j + uni, j1
!
j j i, j i1, j
=D + (13.55)
t x 2 y2
ui,n+2 ui,n+1 n+1
ui+1, 2un+1 + un+1 un+2 2un+2 + un+2
!
j j j i, j i1, j i, j+1 i, j i, j1
=D + (13.56)
t x 2 y2

Examine cuidadosamente (13.55) e (13.56): na primeira, note que o esquema im-


plcito em x; na segunda, a situao se reverte, e o esquema implcito em y. claro
261 13.3 Difuso em 2 Dimenses: ADI, e equaes elticas

que ns vamos precisar de duas anlises de estabilidade de von Neumann, uma para
cada equao.
2011-09-24T17:07:04 Por enquanto, vou supor que os dois esquemas so incon-
dicionalmente estveis, e mandar ver.
Alm disto, por simplicidade vamos fazer x = y = , de maneira que s
haver um nmero de Fourier de grade no problema,

Dt
Fo = , (13.57)
2
e ento teremos, para x:
 
un+1
i, j
u n
i, j
= Fo u n+1
i1, j
2u n+1
i, j
+ u n+1
i+1, j
+ u n
i, j1
2u n
i, j
+ u n
i, j+1
,
   
n+1 n+1 n+1 n+1 n n n n
ui, j Fo ui1, j 2ui, j + ui+1, j = ui, j + Fo ui, j1 2ui, j + ui, j+1 ,
n+1
Foui1, j
+ (1 + 2Fo)ui,n+1
j
Foun+1
i+1, j
= Founi, j1 + (1 2Fo)uni, j + Founi, j+1 (13.58)

Na dimenso y,
 
un+2
i, j
ui,n+1
j
n+1 n+1 n+1 n+2 n+2
= Fo ui1, j 2ui, j + ui+1, j + ui, j1 2ui, j + ui, j+1 , n+2

   
ui,n+2
j
Fo u n+2
i, j1
2u n+2
i, j
+ u n+2
i, j+1
= u n+1
i, j
+ Fo u n+1
i1, j
2u n+1
i, j
+ u n+1
i+1, j
,
Foui,n+2
j1
+ (1 + 2Fo)ui,n+2
j
Foun+2
i, j+1
= Foun+1
i1, j
+ (1 2Fo)un+1
i, j
+ Foun+1
i+1, j
(13.59)

Se ns utilizarmos (novamente por simplicidade) o mesmo nmero de pontos


N + 1 em x e em y, teremos o seguinte mapeamento para a nossa grade:

2L
N= ; (13.60)

x i = L + i, i = 0, . . . , N , (13.61)
y j = L + j, j = 0, . . . , N , (13.62)

e portanto L x i L e L y j L. Lembrando que os valores de u0, j , uN , j , ui,0


e ui,N esto especificados, h (N 1)2 incgnitas para serem calculadas. A beleza de
(13.58) e (13.59) que em vez de resolver a cada passo (digamos) 2t um sistema
de (N 1)2 incgnitas, ns agora podemos resolver a cada passo t N 1 sistemas
de (N 1) incgnitas, alternadamente para u1,...,N 1; j e ui;1,...,N1 .
claro que o cu o limite: poderamos, por exemplo, em vez de usar um es-
quema totalmente implcito, usar Crank-Nicholson em cada avano t; isto nos daria
imediatamente um esquema com acurcia de ordem t 2 . No entanto, assim como
est o mtodo ADI j suficientemente sofisticado para nosso primeiro encontro com
este tipo de problema. Devemos, portanto, program-lo. Vamos, inicialmente, pro-
gramar a soluo analtica, na listagem 13.12.
A soluo analtica do problema para os instantes de tempo t = 0, t = 0,1, t = 0,2
e t = 0,3 est mostrada na figura 13.9
Matemtica Aplicada 262

Listagem 13.12: difusao2d-ana.py Soluo analtica da equao da difuso bidi-


mensional.
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ----------------------------------------------------------
4 # difusao2d-ana: soluo analtica de
5 #
6 # du/dt = D (du^2/dx^2 + du^2/dy^2)
7 #
8 # u(x,0) = T0 exp(-(x^2 + y^2)/L^2)
9 #
10 # com T0 = 1, D = 2, L = 1, em um domnio (-L,-L) at (L,L)
11 #
12 # uso: ./difusao2d-ana.py
13 # ----------------------------------------------------------
14 from __future__ import print_function
15 from __future__ import division
16 fou = open('difusao2d-ana.dat','wb')
17 dd = 0.01
18 dt = 0.001
19 print('# dd = %9.4f' % dd)
20 print('# dt = %9.4f' % dt)
21 nn = int(2.0/dd) # nmero de pontos em x e em y
22 nt = int(1.0/dt) # nmero de pontos em t
23 print('# nn = %9d' % nn)
24 print('# nt = %9d' % nt)
25 from math import exp
26 def ana(x,y,t):
27 return (1.0/(1.0 + 8*t))*exp(-(x**2 + y**2))
28 from numpy import zeros
29 u = zeros((nn+1,nn+1),float) # um array para conter a soluo
30 for n in range(nt+1): # loop no tempo
31 t = n*dt
32 print(t)
33 for i in range(nn+1): # loop no espao
34 xi = -1 + i*dd
35 for j in range(nn+1):
36 yj = -1 + j*dd
37 u[i,j] = ana(xi,yj,t)
38 u.tofile(fou) # imprime uma matriz com os dados
39 fou.close()
263 13.3 Difuso em 2 Dimenses: ADI, e equaes elticas

Figura 13.9: Soluo analtica da equao da difuso bidimensional, para t = 0,


t = 0, t = 0,1, t = 0,2 e t = 0,3
Matemtica Aplicada 264

Em seguida, o esquema numrico ADI est implementado na listagem 13.13. O


resultado mostrado na figura 13.10.

Listagem 13.13: difusao2d-adi.py Soluo numrica da equao da difuso bi-


dimensional, esquema ADI.
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 # ------------------------------------------------------------------------------
4 # difusao2d-adi resolve uma equao de difuso com um mtodo implcito
5 #
6 # uso: ./difusao2d-adi.py
7 # ------------------------------------------------------------------------------
8 from __future__ import print_function
9 from __future__ import division
10 fou = open('difusao2d-adi.dat','wb')
11 dd = 0.02 # define a discretizao em x,y
12 dt = 0.001 # define a discretizao em t
13 print('# dd = %9.4f' % dd)
14 print('# dt = %9.4f' % dt)
15 nn = int(round(2.0/dd,0)) # nmero de pontos em x,y
16 nt = int(round(1.0/dt,0)) # nmero de pontos em t
17 print('# nn = %9d' % nn)
18 print('# nt = %9d' % nt)
19 from numpy import zeros
20 u = zeros((2,nn+1,nn+1),float) # apenas 2 posies no tempo
21 # so necessrias!
22 from math import exp
23 def CCy(y):
24 return (1.0/aux)*exp( - (1.0 + y*y)/aux)
25 def CCx(x):
26 return (1.0/aux)*exp( - (1.0 + x*x)/aux)
27 def CI(x, y):
28 return exp(-(x*x + y*y))
29 for i in range(nn+1): # monta a condio inicial
30 xi = -1.0 + i*dd # inteira, at as fronteiras
31 for j in range(nn+1): # inclusive
32 yj = -1.0 + j*dd
33 u[0,i,j] = CI(xi,yj)
34 u[0].tofile(fou) # imprime a condio inicial
35 D = 2.0 # difusividade
36 Fon = D*dt/((dd)**2) # nmero de Fourier
37 print("Fo = %10.6f" % Fon)
38 A = zeros((3,nn-1),float) # cria a matriz do sistema
39 # ------------------------------------------------------------------------------
40 # monta a matriz do sistema
41 # ------------------------------------------------------------------------------
42 A[0,0] = 0.0 # zera A[0,0]
43 A[0,1:nn-1] = -Fon # preenche o fim da 1a linha
44 A[1,0:nn-1] = 1.0 + 2*Fon # preenche a segunda linha
45 A[2,0:nn-2] = -Fon # preenche o incio da 2a linha
46 A[2,nn-2] = 0.0 # zera A[2,nn-2]
47 # ------------------------------------------------------------------------------
48 # importa uma traduo de tridag de Numerical Recipes para Python
49 # ------------------------------------------------------------------------------
50 from alglin import tridag
51 old = False # o velho truque!
52 new = True
53 n = 0
54 b = zeros(nn-1,float)
55 while (n < nt+1): # loop no tempo
56 n += 1
57 print(n)
58 # ------------------------------------------------------------------------------
59 # varre na direo x
60 # ------------------------------------------------------------------------------
61 t = n*dt
62 aux = 1.0 + 8.0*t
63 for j in range(nn+1): # CC ao longo de x
64 yj = -1.0 + j*dd
65 u[new,0,j] = CCy(yj)
66 u[new,nn,j] = CCy(yj)
265 13.3 Difuso em 2 Dimenses: ADI, e equaes elticas

67 for j in range(1,nn): # nn-1 varreduras em x (logo, loop em y)


68 yj = -1.0 + j*dd
69 b[1:nn-2] = Fon*u[old,2:nn-1,j-1] \
70 + (1.0 - 2*Fon)*u[old,2:nn-1,j] \
71 + Fon*u[old,2:nn-1,j+1]
72 b[0] = Fon*u[old,1,j-1] + (1.0 - 2*Fon)*u[old,1,j] \
73 + Fon*u[old,1,j+1] \
74 + Fon*u[new,0,j]
75 b[nn-2] = Fon*u[old,nn-1,j-1] + (1.0 - 2*Fon)*u[old,nn-1,j] \
76 + Fon*u[old,nn-1,j+1] \
77 + Fon*u[new,nn,j]
78 u[new,1:nn,j] = tridag(A,b)
79 u[new].tofile(fou)
80 # ------------------------------------------------------------------------------
81 # varre na direo y
82 # ------------------------------------------------------------------------------
83 (new,old) = (old,new)
84 n += 1
85 print(n)
86 t = n*dt
87 aux = 1.0 + 8.0*t
88 for i in range(nn+1): # CC ao longo de y
89 xi = -1.0 + i*dd
90 u[new,i,0] = CCx(xi)
91 u[new,i,nn] = CCx(xi)
92 for i in range(1,nn): # nn-1 varreduras em y (logo, loop em x)
93 xi = -1.0 + i*dd
94 b[1:nn-2] = Fon*u[old,i-1,2:nn-1] \
95 + (1.0 - 2*Fon)*u[old,i,2:nn-1] \
96 + Fon*u[old,i+1,2:nn-1]
97 b[0] = Fon*u[old,i-1,1] + (1.0 - 2*Fon)*u[old,i,1] \
98 + Fon*u[old,i+1,1] \
99 + Fon*u[new,i,0]
100 b[nn-2] = Fon*u[old,i-1,nn-1] + (1.0 - 2*Fon)*u[old,i,nn-1] \
101 + Fon*u[old,i+1,nn-1] \
102 + Fon*u[new,i,nn]
103 u[new,i,1:nn] = tridag(A,b)
104 u[new].tofile(fou)
105 (new,old) = (old,new)
106 fou.close() # fecha o arquivo de sada, e fim.

Exemplo 13.2 Utilizando a anlise de estabilidade de von Newmann, mostre que o esquema
numrico correspondente primeira das equaes acima incondicionalmente estvel. Su-
ponha x = y = s.
SOLUO
Inicialmente, rearranjamos o esquema de discretizao, multiplicando por t e dividindo
por x 2 :
h i
ui,n+1
j u n
i, j = Fo u n+1
i+1, j 2u n+1
i, j + u n+1
i1, j + u n
i, j+1 2u n
i, j + u n
i, j1 ,

onde
Dt
Fo = .
s2

Faa agora

t n = nt,
x i = is,
y j = js,
ni =
X
l,m eat n eikl x i eikm y j ,
l,m
Matemtica Aplicada 266

Figura 13.10: Soluo numrica da equao da difuso bidimensional com o esquema


ADI, para t = 0, t = 0, t = 0,1, t = 0,2 e t = 0,3
267 13.3 Difuso em 2 Dimenses: ADI, e equaes elticas

e substitua o modo (l, m) no esquema de discretizao:

l,m ea(t n +t) eikl is eikm js l,m eat n eikl is eikm js =


Fo l,m ea(t n +t) eikl (i+1)s eikm js 2l,m ea(t n +t) eikl is eikm js + l,m ea(t n +t) eikl (i1)s eikm js

l,m eat n eikl is eikm ( j+1)s 2l,m eat n eikl is eikm js + l,m eat n eikl is eikm ( j1)s .

Ns imediatamente reconhecemos o fator comum

l,m eat n eikl is eikm js ,

e simplificamos:

eat 1 = Fo eat e+ikl s 2eat + eat eikl s + e+ikm s 2 + eikm s ,


eat 1 Fo(e+ikl s 2 + eikl s ) = 1 + Fo e+ikm s 2 + eikm s


eat 1 2Fo(cos(kl s) 1) = 1 + 2Fo(cos(km s) 1)


 

1 + 2Fo(cos(km s) 1)

at .
|e | =
1 2Fo(cos(kl s) 1)
1 4Fo sen2 km s
 
2
|eat | =

 1
1 + 4Fo sen2 kl s

2
Matemtica Aplicada 268

Você também pode gostar