| | | |

Introdução a PINNs

Ajude a manter o site livre, gratuito e sem propagandas. Colabore!

3.2 Problemas transientes

Como um problema fundamental em equações diferenciais parciais transientes, vamos estudar como podemos criar uma PINN para resolver o problema de calor

ut=uxx+f,(t,x)(0,1]×(0,1), (3.28)
u(0,x)=g(x),x[0,1], (3.29)
u(t,1)=u(t,1)=0,t(t0,tf]. (3.30)

Como um exemplo, vamos assumir a fonte f(t,x)=(π21)etsen(πx) e a condição inicial g(x)=sen(πx). Este problema foi manufaturado com a solução analítica

u(t,x)=etsen(πx). (3.31)

3.2.1 PINN sem discretização temporal

A primeira abordagem que iremos adotar para resolver o problema de calor é criar uma PINN

u~=𝒩(t,x;𝜽), (3.32)

com uma rede do tipo MLP, em que a entrada da rede são as coordenadas (t,x) e a saída é a estimativa da solução do problema de calor (3.28)-(3.30).

Refer to caption
Figura 3.3: Comparação entre a solução PINN (iso-cores) e a solução analítica (iso-linhas). Exemplo de pontos de amostragem para treinamento da PINN (pontos vermelhos).

O Código 18 é uma implementação da PINNs para esse resolver o problema de calor (verifique!). A função de perda da rede é baseada no resíduo da equação diferencial, na condição inicial e nas condições de contorno. O treinamento da rede é feito minimizando a função de perda, que é composta por

ε:=1ns,ins=1ns,in|f(s)u~t(s)+u~xx(s)|2resíduo
+1ns,cis=1ns,ci|u~(s)g(s)|2c.i.+1ns,ccs=1ns,cc|u~s0|2c.c.. (3.33)

Aqui, o primeiro termo representa o resíduo da equação diferencial e é calculado em pontos de amostragem internos no domínio (0,1]×(0,1). O segundo, representa a condição inicial e é calculado em pontos de amostragem no domínio com t=0. O terceiro termo representa as condições de contorno e é calculado em pontos de amostragem no domínio com x=0 e x=1. A cada época de treinamento, conjuntos randômicos de pontos de amostragem internos, de pontos de contorno e de pontos da condição inicial são gerados com distribuição uniforme.

A Figura 3.3 mostra uma comparação entre a solução PINN (iso-cores) obtida pelo código e a solução analítica (iso-linhas). A cada época de treinamento, os pontos de amostragem são gerados de forma randômica por uma distribuição uniforme. A figura mostra um exemplo de pontos de amostragem (pontos vermelhos) para treinamento da PINN.

Código 18: pinn_calor.py
1import torch
2
3# dispositivo GPU/CPU ?
4device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
5print(f'device: {device}')
6
7# modelo
8
9n_h = 4 # num de camadas escondidas
10n_n = 50 # num de neurônios por camada escondida
11act_fun = torch.nn.Tanh() # função de ativação
12
13model = torch.nn.Sequential()
14# camada de entrada
15model.add_module('layer_1', torch.nn.Linear(2,n_n))
16model.add_module('fun_1', act_fun)
17# camadas escondidas
18for i in range(n_h-1):
19 model.add_module(f'layer_{i+2}', torch.nn.Linear(n_n,n_n))
20 model.add_module(f'fun_{i+2}', act_fun)
21# camada de saída
22model.add_module(f'layer_{n_h+1}', torch.nn.Linear(n_n,1))
23model.to(device) # envia modelo para o dispositivo
24
25# treinamento
26
27## fonte
28def f(t, x):
29 return (torch.pi**2 - 1) * torch.exp(-t) * torch.sin(torch.pi*x)
30
31## optimizador
32optim = torch.optim.Adam(model.parameters(), lr=0.001)
33
34## num de amostras por época
35ns_t = 20 # num de amostras no tempo
36ns_x = 40 # num de amostras no espaço
37
38## num max épocas
39nepochs = 50_000
40
41## tolerância
42tol = 1e-4
43persist = 5
44
45# contador de persistência
46n_conv = 0
47
48# validação
49t_v = torch.linspace(0, 1, 25, device=device)
50x_v = torch.linspace(0, 1, 50, device=device)
51T_v, X_v = torch.meshgrid(t_v, x_v, indexing='ij')
52S_v = torch.hstack((T_v.reshape(-1,1), X_v.reshape(-1,1)))
53S_v.requires_grad = True
54
55# laço de treinamento
56for epoch in range(nepochs):
57
58 model.train()
59
60 # amostras de pontos internos
61 t = torch.rand(ns_t, 1, device=device)
62 x = torch.rand(ns_x, 1, device=device)
63 T, X = torch.meshgrid(t.flatten(), x.flatten(), indexing='ij')
64 S = torch.hstack((T.reshape(-1,1), X.reshape(-1,1)))
65 S.requires_grad = True
66
67 # propagação nos pontos internos
68 U = model(S)
69
70 # derivadas de primeira ordem
71 DU = torch.autograd.grad(U, S,
72 grad_outputs=torch.ones_like(U),
73 create_graph=True)[0]
74 DUDT = DU[:,0:1] # du/dt
75 DUDX = DU[:,1:2] # du/dx
76
77 # derivadas de segunda ordem
78 # d2u/dx^2
79 D2UDX = torch.autograd.grad(DUDX, S,
80 grad_outputs=torch.ones_like(DUDX),
81 create_graph=True)[0][:,1:2]
82
83 # rhs
84 L = DUDT - D2UDX
85
86 # fonte
87 F = f(S[:,0:1], S[:,1:2])
88
89 # função de perda: resíduo
90 loss_res = torch.mean((F - L)**2)
91
92 # condição inicial
93 Sci = torch.hstack((torch.zeros_like(x), x))
94 Uci = model(Sci)
95 Uexp_ci = torch.sin(torch.pi*x)
96 loss_ci = torch.mean((Uci - Uexp_ci)**2)
97
98 # condições de contorno
99
100 # contorno x = 0
101 Scc1 = torch.hstack((t, torch.zeros_like(t)))
102 Ucc1 = model(Scc1)
103 loss_cc1 = torch.mean((Ucc1 - 0.0)**2)
104
105 # contorno x = 1
106 Scc2 = torch.hstack((t, torch.ones_like(t)))
107 Ucc2 = model(Scc2)
108 loss_cc2 = torch.mean((Ucc2 - 0.0)**2)
109
110 # função de perda total
111 loss = loss_res + loss_ci + loss_cc1 + loss_cc2
112
113 if (epoch % 100 == 0):
114 print(f'{epoch}: loss_res: {loss.item():.2e}, ci: {loss_ci.item():.2e}, cc1: {loss_cc1.item():.2e}, cc2: {loss_cc2.item():.2e}')
115 torch.save(model, f'model.pt')
116 torch.save(track, f'track.pt')
117
118 if (loss.item() < tol):
119 n_conv += 1
120 print(f'\t{epoch}: loss: {loss.item():.2e}, {n_conv}/{persist}')
121 if (n_conv >= persist):
122 print(f'\t{epoch}: critério de parada atingido.')
123 break
124 else:
125 n_conv = 0
126
127 # backward
128 optim.zero_grad()
129 loss.backward()
130 optim.step()

3.2.2 PINN com discretização temporal

Uma abordagem alternativa para resolver o problema de calor é criar uma PINN que incorpora a discretização temporal. Neste caso, a rede neural tem apenas a variável espacial como entrada, e as saídas da rede são as estimativas da solução em diferentes instantes de tempo. A função de perda da rede é baseada no resíduo da equação diferencial, na condição inicial e nas condições de contorno, mas agora a equação diferencial é avaliada usando uma discretização temporal.

Como um exemplo, vamos considerar a discretização temporal pelo método de Euler implícito para o problema de calor (3.28)-(3.30). A equação diferencial é discretizada no tempo como

un+1htuxx(n+1)=u(n)+htf(n), (3.34)

onde u(n) é a solução no instante de tempo tn=nht, f(n)=f(tn,x), n=0,1,,nt1, nt é o número de passos no tempo de tamanho ht=tf/nt.

O Código 19 é uma implementação da PINN com esta discretização temporal. A função de perda da rede é

ε:=1ns,in(nt1)s=1ns,inn=1nt1|u~(n),(s)+htf(n),(s)u~(n+1),(s)+htu~xx(n),(s)|2resíduo
+1ns,cis=1ns,ci|u~(0),(s)g(s)|2c.i.+1ns,cc(nt1)s=1ns,ccn=1nt1|u~(n),(s)0|2c.c.. (3.35)

Ao rodarmos o código, é notável que a convergência do treinamento é mais rápida do que na abordagem sem discretização temporal pelo código anterior (Código 18). Ainda, observamos que esta abordagem nos permite usar uma rede neural com menos camadas escondidas e menos neurônios por camada escondida, o que reduz o custo computacional. Verifique!

Código 19: pinn_calor_discretizado.py
1import torch
2
3# dispositivo GPU/CPU ?
4device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
5print(f'device: {device}')
6
7# discretização temporal
8t0 = 0.0 # tempo inicial
9tf = 1.0 # tempo final
10nt = 10 # num de passos no tempo
11ts = torch.linspace(t0, tf, nt+1, device=device) # instantes de tempo
12ht = (tf - t0)/nt # tamanho do passo no tempo
13
14# modelo
15n_h = 2 # num de camadas escondidas
16n_n = 25 # num de neurônios por camada escondida
17act_fun = torch.nn.Tanh() # função de ativação
18
19model = torch.nn.Sequential()
20# camada de entrada
21model.add_module('layer_1', torch.nn.Linear(1,n_n))
22model.add_module('fun_1', act_fun)
23# camadas escondidas
24for i in range(n_h-1):
25 model.add_module(f'layer_{i+2}', torch.nn.Linear(n_n,n_n))
26 model.add_module(f'fun_{i+2}', act_fun)
27# camada de saída
28model.add_module(f'layer_{n_h+1}', torch.nn.Linear(n_n,nt+1))
29model.to(device) # envia modelo para o dispositivo
30
31# treinamento
32
33## fonte
34def f(t, x):
35 return (torch.pi**2 - 1) * torch.exp(-t) * torch.sin(torch.pi*x)
36
37## optimizador
38optim = torch.optim.Adam(model.parameters(), lr=0.001)
39
40## num de amostras por época
41ns_x = 40 # num de amostras no espaço
42
43## num max épocas
44nepochs = 50_000
45
46## tolerância
47tol = 1e-4
48persist = 5
49
50# contador de persistência
51n_conv = 0
52
53# laço de treinamento
54for epoch in range(nepochs):
55
56 model.train()
57
58 # amostras de pontos internos
59 Xs = torch.rand(ns_x, 1, device=device)
60 Xs.requires_grad = True
61
62 # propagação nos pontos internos
63 U = model(Xs)
64
65 loss_res = 0.0
66 for n in range(0, nt):
67
68 # derivadas de primeira ordem
69 DUDX = torch.autograd.grad(U[:,n+1:n+2], Xs,
70 grad_outputs=torch.ones_like(U[:,n+1:n+2]),
71 create_graph=True)[0]
72
73 # derivadas de segunda ordem
74 D2UDX = torch.autograd.grad(DUDX, Xs,
75 grad_outputs=torch.ones_like(DUDX),
76 create_graph=True)[0]
77
78 # resíduo da equação discretizada (Euler implícito)
79 R = U[:,n:n+1] + ht * f(ts[n+1], Xs) - U[:,n+1:n+2] + ht * D2UDX
80
81 loss_res += torch.sum(R**2)
82
83 # função de perda: resíduo
84 loss_res = loss_res / (ns_x * nt)
85
86 # condição inicial
87 Uexp_ci = torch.sin(torch.pi*Xs)
88 loss_ci = torch.mean((U[:,0:1] - Uexp_ci)**2)
89
90 # condições de contorno
91
92 # contorno x = 0
93 Ucc1 = model(torch.zeros((1,1), device=device))
94 loss_cc1 = torch.mean((Ucc1 - 0.0)**2)
95
96 # contorno x = 1
97 Ucc2 = model(torch.ones((1,1), device=device))
98 loss_cc2 = torch.mean((Ucc2 - 0.0)**2)
99
100 # função de perda total
101 loss = loss_res + loss_ci + loss_cc1 + loss_cc2
102
103
104 if (epoch % 100 == 0):
105 print(f'{epoch}: loss_res: {loss.item():.2e}, ci: {loss_ci.item():.2e}, cc1: {loss_cc1.item():.2e}, cc2: {loss_cc2.item():.2e}')
106 torch.save(model, f'model.pt')
107
108 if (loss.item() < tol):
109 n_conv += 1
110 print(f'\t{epoch}: loss: {loss.item():.2e}, {n_conv}/{persist}')
111 if (n_conv >= persist):
112 print(f'\t{epoch}: critério de parada atingido.')
113 break
114 else:
115 n_conv = 0
116
117 # backward
118 optim.zero_grad()
119 loss.backward()
120 optim.step()

3.2.3 Exercícios

E. 3.2.1.

Considere o problema de calor com condições de contorno de Robin555Victor Gustave Robin, 1855 - 1897, matemático francês. Fonte: Wikipedia: Victor Gustave Robin. não homogêneas

ut=uxx+f(t,x),(t,x)(0,1)×(0,1), (3.36)
u(0,x)=2x+1,x[0,1], (3.37)
u+un=g,x{0,1},t(0,1], (3.38)

onde u/n é a derivada normal exterior ao domínio (0,1) em x=0 e x=1, com fonte

f(t,x)=(2x+1)et, (3.39)

e condições de contorno com

g(t,0)=et,g(t,1)=5et,t(0,1]. (3.40)

Crie uma PINN u~=𝒩(t,x;𝜽) para resolver este problema. Compare seus resultados com a solução analítica u(t,x)=(2x+1)et.

E. 3.2.2.

Refaça o E.3.2.1 para criar PINNs da forma

𝒖~=𝒩(x;𝜽), (3.41)

tendo como entrada apenas a coordenada espacial x e como saída as estimativas da solução em diferentes instantes de tempo. Faça uma PINN para cada uma das seguintes discretizações temporais:

  1. a)

    Euler implícito.

  2. b)

    Euler explícito.

  3. c)

    Crank-Nicolson.

  4. d)

    Runge-Kutta de quarta ordem.

Quais dos esquemas de discretização temporal você considera mais eficiente para criar a PINN para este problema? Justifique sua resposta.

E. 3.2.3.

Considere o problema de onda

uttuxx=0,(t,x)(0,2)×(0,1), (3.42)
u(0,x)=0,ut(0,x)=πsen(πx),x[0,1], (3.43)
u(t,0)=u(t,1)=0,t(0,2]. (3.44)

Este problema tem solução analítica u(t,x)=sen(πx)sin(πt).

  1. a)

    Crie uma PINN u~=𝒩(t,x;𝜽) para resolver este problema. Compare seus resultados com a solução analítica.

  2. b)

    Considerando uma discretização temporal por diferenças finitas centrais de ordem 2, crie uma PINN u~=𝒩(x;𝜽) tendo como entrada apenas a coordenada espacial x e como saída as estimativas da solução em diferentes instantes de tempo. Compare seus resultados com a solução analítica.

  3. c)

    Qual as duas abordagens apresentadas para resolver o problema de onda você considera mais eficiente? Justifique sua resposta.

E. 3.2.4.

Crie uma PINN para resolver o seguinte problema de Burgers

ut+uuxνuxx=0,(t,x)(0,1]×(0,1), (3.45)
u(0,x)=2νπsen(πx)2+cos(πx),x[0,1], (3.46)
u(t,0)=u(t,1)=0,t(0,1], (3.47)

com viscosidade ν=0.01/π. Este problema tem a seguinte solução analítica [19]

u(t,x)=2νπeπ2νtsen(πx)2+eπ2νtcos(πx). (3.48)
E. 3.2.5.

Crie uma PINN para resolver o seguinte problema de Burgers

ut+uuxuxx=0,(t,x)(0,1]×(1,1), (3.49)
u(0,x)=2sign(x),x[1,1], (3.50)
u(t,1)=u^(t,1),u(t,1)=u^(t,1),t(0,1], (3.51)

com a conhecida solução analítica [2]

u^(t,x)=2G(t,x)G(t,x)G(t,x)+G(t,x), (3.52)
G(t,x)=12etxerfc(2tx2t). (3.53)

Envie seu comentário

Aproveito para agradecer a todas/os que de forma assídua ou esporádica contribuem enviando correções, sugestões e críticas!

Opcional. Preencha seu nome para que eu possa lhe contatar.
Opcional. Preencha seu e-mail para que eu possa lhe contatar.
As informações preenchidas são enviadas por e-mail para o desenvolvedor do site e tratadas de forma privada. Consulte a política de uso de dados para mais informações.

Licença Creative Commons
Este texto é disponibilizado nos termos da Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional. Ícones e elementos gráficos podem estar sujeitos a condições adicionais.

Pedro H A Konzen
| | | |