| | | |

Matemática Numérica Paralela

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

3.5 Aplicação: Equação do calor

Em revisão

Nesta seção, vamos construir um código MPI para a resolução da equação do calor pelo método das diferenças finitas. Como um caso teste, vamos considerar a equação do calor

utuxx=0,0<x<1,t>0, (3.15)

com condições de contorno

u(0,t)=u(1,t)=0,t>0, (3.16)

e condições iniciais

u(x,0)=sen(πx),0x1, (3.17)

onde, u=u(t,x).

Seguindo o método de Rothe33endnote: 3Erich Hans Rothe, 1895 - 1988, matemático alemão. Fonte: Wikipédia.44endnote: 4Também chamado de método das linhas., vamos começar pela discretização no tempo. Para tanto, vamos usar a notação tm=mht, m=0,1,2,,M, onde ht é o passo no tempo e M o número total de iterações no tempo. Usando o método de Euler55endnote: 5Leonhard Paul Euler, 1707 - 1783, matemático suíço. Fonte: Wikipédia., a equação (3.15) é aproximada por

u(tm+1,x)u(tm,x)+htuxx(tm,x),0<x<1, (3.18)

onde u(t0,x)=u(0,x), dada pela condição inicial (3.17).

Agora, vamos denotar xi=ihx, i=0,1,2,,I, onde hx=1/I é o tamanho da malha (passo espacial). Então, usando a aproximação por diferenças finitas centrais de ordem 2, obtemos

u(tm+1,xi)u(tm,xi)+ht[u(tm,xi1)2u(tm,xi)+u(tm,xi+1)hx2], (3.19)

para i=1,2,,I1, observando que, das condições de contorno (3.16), temos

u(tm,x0)=u(tm,xI)=0. (3.20)

Em síntese, denotando uimu(tm,xi), temos que uma aproximação da solução do problema de calor acima pode ser calculada pela seguinte iteração

uim+1=uim+hthx2ui1m2hthx2uim+hthx2ui+1m, (3.21)

com ui0=sen(πxi) e u0m=uIm=0.

Para construirmos um código MPI, vamos fazer a distribuição do processamento pela malha espacial entre os np processos disponíveis. Mais precisamente, cada processo p=0,1,2,,np1, computará a solução uim nos ponto de malha i=ip,ip+1,,fp, onde

ip=pInp,p=0,1,,np1, (3.22)
fp=(p+1)Inp,p=0,1,,np2, (3.23)

com fnp1=I. Ainda, por simplicidade e economia de memória computacional, vamos remover o superíndice, denotando por u a solução na iteração corrente e por u0 a solução na iteração anterior.

Neste caso, a cada iteração m=0,1,2,,M, o processo p=0, irá computar

uj=uj0+hthx2uj102hthx2uj0+hthx2uj+10, (3.24)

para j=1,,f0, com as condições de contorno

u00=0 (3.25)
uf0+10=ui1+10. (3.26)

Ou seja, este passo requer a comunicação entre o processo 0 e o processo 1.

Os processos p=1,2,,np2 irão computar

uj=uj0+hthx2uj102hthx2uj0+hthx2uj+10, (3.27)

para j=ip+1,,fp, com as condições de contorno

uip0=ufp10 (3.28)
ufp+10=uip+1+10. (3.29)

Logo, este passo requer a comunicação entre os processos p1, p e p+1.

Ainda, o processo p=np1 deve computar

uj=uj0+hthx2uj102hthx2uj0+hthx2uj+10, (3.30)

para j=inp1+1,,fnp11, com as condições de contorno

uinp10=ufnp20 (3.31)
ufnp10=0. (3.32)

O que requer a comunicação entre os processos np2 e o np1.

O código abaixo calor.cc é uma implementação MPI deste algoritmo. Cada processo aloca e computa a solução em apenas um pedaço da malha, conforme descrito acima. As comunicações entre os processos ocorrem apenas nas linhas 60-80. Verifique!

Código: calor.cc
1#include <stdio.h>
2#include <math.h>
3
4// API MPI
5#include <mpi.h>
6
7int main (int argc, char** argv) {
8 // Inicializa o MPI
9 MPI_Init(NULL, NULL);
10
11 // numero total de processos
12 int world_size;
13 MPI_Comm_size(MPI_COMM_WORLD, &world_size);
14
15 // ID (rank) do processo
16 int world_rank;
17 MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
18
19 // parametros
20 size_t M = 1000;
21 size_t I = 10;
22
23 // tamanho dos passos discretos
24 double ht = 1e-3;
25 double hx = 1.0/I;
26 double cfl = ht/(hx*hx);
27
28 // malha espacial local
29 size_t ip = world_rank * int (I/world_size);
30 size_t fp = (world_rank+1) * int (I/world_size);
31 if (world_rank == world_size-1)
32 fp = I;
33 size_t my_I = fp - ip;
34
35 double x[my_I+1];
36 for (size_t j=0; j<=my_I; j++)
37 x[j] = (ip+j) * hx;
38
39 // solucao local
40 double u0[my_I+1], u[my_I+1];
41
42 // condicao inicial
43 for (size_t j=0; j<=my_I; j++) {
44 u0[j] = sin (M_PI * x[j]);
45 }
46
47 // condicoes de contorno de Dirichlet
48 if (world_rank == 0)
49 u[0] = 0.0;
50 if (world_rank == world_size-1)
51 u[my_I] = 0.0;
52
53 // auxiliares
54 double u00, u0I;
55
56
57 // iteracoes no tempo
58 for (size_t m=0; m<M; m++) {
59
60 if (world_rank == 0) {
61 MPI_Send (&u0[my_I], 1, MPI_DOUBLE,
62 1, 0, MPI_COMM_WORLD);
63 MPI_Recv (&u0I, 1, MPI_DOUBLE,
64 1, 0, MPI_COMM_WORLD,
65 MPI_STATUS_IGNORE);
66 } else if (world_rank < world_size-1) {
67 MPI_Recv (&u00, 1, MPI_DOUBLE,
68 world_rank-1, 0, MPI_COMM_WORLD,
69 MPI_STATUS_IGNORE);
70 MPI_Send (&u0[my_I], 1, MPI_DOUBLE,
71 world_rank+1, 0, MPI_COMM_WORLD);
72
73 MPI_Recv (&u0I, 1, MPI_DOUBLE,
74 world_rank+1, 0, MPI_COMM_WORLD,
75 MPI_STATUS_IGNORE);
76 MPI_Send (&u0[1], 1, MPI_DOUBLE,
77 world_rank-1, 0, MPI_COMM_WORLD);
78 } else {
79 MPI_Recv (&u00, 1, MPI_DOUBLE,
80 world_size-2, 0, MPI_COMM_WORLD,
81 MPI_STATUS_IGNORE);
82 MPI_Send (&u0[1], 1, MPI_DOUBLE,
83 world_size-2, 0, MPI_COMM_WORLD);
84 }
85
86 // atualizacao
87 u[1] = u0[1]
88 + cfl * u00
89 - 2*cfl * u0[1]
90 + cfl * u0[2];
91 for (size_t j=2; j<my_I; j++)
92 u[j] = u0[j]
93 + cfl * u0[j-1]
94 - 2*cfl * u0[j]
95 + cfl * u0[j+1];
96 if (world_rank < world_size-1)
97 u[my_I] = u0[my_I]
98 + cfl * u0[my_I-1]
99 - 2*cfl * u0[my_I]
100 + cfl * u0I;
101
102 // prepara nova iteracao
103 for (size_t j=0; j<=my_I; j++)
104 u0[j] = u[j];
105
106 }
107
108
109 // Finaliza o MPI
110 MPI_Finalize ();
111
112 return 0;
113}

No código acima, as comunicações entre os processos foram implementadas de forma cíclica. A primeira a ocorrer é o envio de ufp0 do processo zero (linhas 61-62) e o recebimento de uip0 pelo processo 1 (linhas 67-69). Enquanto essa comunicação não for completada, todos os processos estarão aguardando, sincronizados nas linhas 67-69 e 79-81. Ocorrida a comunicação entre os processos 0 e 1, ocorrerá a comunicação entre o 1 e o 2, entre o 2 e o 3 e assim por diante, de forma cíclica (linhas 67-71 e 79-81).

As últimas comunicações também ocorrem de forma cíclica, começando pelo envio de uip+10 pelo processo np1 (linhas 81-83) e o recebimento de ufp+10 pelo processo np2 (linhas 73-75). Em sequência, ocorrem as comunicações entre o processo np2 e o processo np3, entre o np3 e o np4, até o término das comunicações entre o processo 1 (linhas 76-77) e o processo 0 (linhas 63-65).

3.5.1 Exercícios

Em revisão

E. 3.5.1.

O problema (3.15)-(3.17) tem solução analítica

u(t,x)=eπ2tsen(πx). (3.33)

Modifique o código calor.cc acima, de forma que a acada iteração m no tempo, seja impresso na tela o valor de um(0.5) computado e o valor da solução analítica u(tm,0.5). Faça refinamentos nas malhas temporal e espacial, até conseguir uma aproximação com três dígitos significativos corretos.

E. 3.5.2.

Modifique o código calor.cc acima, de forma que a cada iteração m no tempo, seja impresso na tela o valor do funcional

q(t)=01u(t,x)𝑑x. (3.34)

Valide os resultados por comparação com a solução analítica (veja o Exercício (3.5.1)).

E. 3.5.3.

Modifique o código calor.cc acima, de forma que as comunicações iniciem-se entre os processos np1 e np2. Ou seja, que a primeira comunicação seja o envio de uip+10 pelo processo np1 e o recebimento de ufp+10 pelo processo np2. E que, de forma cíclica, as demais comunicações ocorram. Poderia haver alguma vantagem em fazer esta modificação?

E. 3.5.4.

Modifique o código calor.cc acima, de forma que as comunicações sejam feitas de forma assíncrona, i.e. usando as rotinas MPI_Isend e MPI_Irecv. Há vantagens em se fazer esta modificação?

E. 3.5.5.

Faça um código MPI para computar uma aproximação da solução de

utuxx=0,0<x<1,t>0, (3.35)

com condições de contorno periódicas

u(0,t)=u(1,t),t>0, (3.36)

e condições iniciais

u(x,0)=sen(πx),0x1, (3.37)

onde, u=u(t,x).


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.

Matemática Numérica Paralela

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

3.5 Aplicação: Equação do calor

Em revisão

Nesta seção, vamos construir um código MPI para a resolução da equação do calor pelo método das diferenças finitas. Como um caso teste, vamos considerar a equação do calor

utuxx=0,0<x<1,t>0, (3.15)

com condições de contorno

u(0,t)=u(1,t)=0,t>0, (3.16)

e condições iniciais

u(x,0)=sen(πx),0x1, (3.17)

onde, u=u(t,x).

Seguindo o método de Rothe33endnote: 3Erich Hans Rothe, 1895 - 1988, matemático alemão. Fonte: Wikipédia.44endnote: 4Também chamado de método das linhas., vamos começar pela discretização no tempo. Para tanto, vamos usar a notação tm=mht, m=0,1,2,,M, onde ht é o passo no tempo e M o número total de iterações no tempo. Usando o método de Euler55endnote: 5Leonhard Paul Euler, 1707 - 1783, matemático suíço. Fonte: Wikipédia., a equação (3.15) é aproximada por

u(tm+1,x)u(tm,x)+htuxx(tm,x),0<x<1, (3.18)

onde u(t0,x)=u(0,x), dada pela condição inicial (3.17).

Agora, vamos denotar xi=ihx, i=0,1,2,,I, onde hx=1/I é o tamanho da malha (passo espacial). Então, usando a aproximação por diferenças finitas centrais de ordem 2, obtemos

u(tm+1,xi)u(tm,xi)+ht[u(tm,xi1)2u(tm,xi)+u(tm,xi+1)hx2], (3.19)

para i=1,2,,I1, observando que, das condições de contorno (3.16), temos

u(tm,x0)=u(tm,xI)=0. (3.20)

Em síntese, denotando uimu(tm,xi), temos que uma aproximação da solução do problema de calor acima pode ser calculada pela seguinte iteração

uim+1=uim+hthx2ui1m2hthx2uim+hthx2ui+1m, (3.21)

com ui0=sen(πxi) e u0m=uIm=0.

Para construirmos um código MPI, vamos fazer a distribuição do processamento pela malha espacial entre os np processos disponíveis. Mais precisamente, cada processo p=0,1,2,,np1, computará a solução uim nos ponto de malha i=ip,ip+1,,fp, onde

ip=pInp,p=0,1,,np1, (3.22)
fp=(p+1)Inp,p=0,1,,np2, (3.23)

com fnp1=I. Ainda, por simplicidade e economia de memória computacional, vamos remover o superíndice, denotando por u a solução na iteração corrente e por u0 a solução na iteração anterior.

Neste caso, a cada iteração m=0,1,2,,M, o processo p=0, irá computar

uj=uj0+hthx2uj102hthx2uj0+hthx2uj+10, (3.24)

para j=1,,f0, com as condições de contorno

u00=0 (3.25)
uf0+10=ui1+10. (3.26)

Ou seja, este passo requer a comunicação entre o processo 0 e o processo 1.

Os processos p=1,2,,np2 irão computar

uj=uj0+hthx2uj102hthx2uj0+hthx2uj+10, (3.27)

para j=ip+1,,fp, com as condições de contorno

uip0=ufp10 (3.28)
ufp+10=uip+1+10. (3.29)

Logo, este passo requer a comunicação entre os processos p1, p e p+1.

Ainda, o processo p=np1 deve computar

uj=uj0+hthx2uj102hthx2uj0+hthx2uj+10, (3.30)

para j=inp1+1,,fnp11, com as condições de contorno

uinp10=ufnp20 (3.31)
ufnp10=0. (3.32)

O que requer a comunicação entre os processos np2 e o np1.

O código abaixo calor.cc é uma implementação MPI deste algoritmo. Cada processo aloca e computa a solução em apenas um pedaço da malha, conforme descrito acima. As comunicações entre os processos ocorrem apenas nas linhas 60-80. Verifique!

Código: calor.cc
1#include <stdio.h>
2#include <math.h>
3
4// API MPI
5#include <mpi.h>
6
7int main (int argc, char** argv) {
8 // Inicializa o MPI
9 MPI_Init(NULL, NULL);
10
11 // numero total de processos
12 int world_size;
13 MPI_Comm_size(MPI_COMM_WORLD, &world_size);
14
15 // ID (rank) do processo
16 int world_rank;
17 MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
18
19 // parametros
20 size_t M = 1000;
21 size_t I = 10;
22
23 // tamanho dos passos discretos
24 double ht = 1e-3;
25 double hx = 1.0/I;
26 double cfl = ht/(hx*hx);
27
28 // malha espacial local
29 size_t ip = world_rank * int (I/world_size);
30 size_t fp = (world_rank+1) * int (I/world_size);
31 if (world_rank == world_size-1)
32 fp = I;
33 size_t my_I = fp - ip;
34
35 double x[my_I+1];
36 for (size_t j=0; j<=my_I; j++)
37 x[j] = (ip+j) * hx;
38
39 // solucao local
40 double u0[my_I+1], u[my_I+1];
41
42 // condicao inicial
43 for (size_t j=0; j<=my_I; j++) {
44 u0[j] = sin (M_PI * x[j]);
45 }
46
47 // condicoes de contorno de Dirichlet
48 if (world_rank == 0)
49 u[0] = 0.0;
50 if (world_rank == world_size-1)
51 u[my_I] = 0.0;
52
53 // auxiliares
54 double u00, u0I;
55
56
57 // iteracoes no tempo
58 for (size_t m=0; m<M; m++) {
59
60 if (world_rank == 0) {
61 MPI_Send (&u0[my_I], 1, MPI_DOUBLE,
62 1, 0, MPI_COMM_WORLD);
63 MPI_Recv (&u0I, 1, MPI_DOUBLE,
64 1, 0, MPI_COMM_WORLD,
65 MPI_STATUS_IGNORE);
66 } else if (world_rank < world_size-1) {
67 MPI_Recv (&u00, 1, MPI_DOUBLE,
68 world_rank-1, 0, MPI_COMM_WORLD,
69 MPI_STATUS_IGNORE);
70 MPI_Send (&u0[my_I], 1, MPI_DOUBLE,
71 world_rank+1, 0, MPI_COMM_WORLD);
72
73 MPI_Recv (&u0I, 1, MPI_DOUBLE,
74 world_rank+1, 0, MPI_COMM_WORLD,
75 MPI_STATUS_IGNORE);
76 MPI_Send (&u0[1], 1, MPI_DOUBLE,
77 world_rank-1, 0, MPI_COMM_WORLD);
78 } else {
79 MPI_Recv (&u00, 1, MPI_DOUBLE,
80 world_size-2, 0, MPI_COMM_WORLD,
81 MPI_STATUS_IGNORE);
82 MPI_Send (&u0[1], 1, MPI_DOUBLE,
83 world_size-2, 0, MPI_COMM_WORLD);
84 }
85
86 // atualizacao
87 u[1] = u0[1]
88 + cfl * u00
89 - 2*cfl * u0[1]
90 + cfl * u0[2];
91 for (size_t j=2; j<my_I; j++)
92 u[j] = u0[j]
93 + cfl * u0[j-1]
94 - 2*cfl * u0[j]
95 + cfl * u0[j+1];
96 if (world_rank < world_size-1)
97 u[my_I] = u0[my_I]
98 + cfl * u0[my_I-1]
99 - 2*cfl * u0[my_I]
100 + cfl * u0I;
101
102 // prepara nova iteracao
103 for (size_t j=0; j<=my_I; j++)
104 u0[j] = u[j];
105
106 }
107
108
109 // Finaliza o MPI
110 MPI_Finalize ();
111
112 return 0;
113}

No código acima, as comunicações entre os processos foram implementadas de forma cíclica. A primeira a ocorrer é o envio de ufp0 do processo zero (linhas 61-62) e o recebimento de uip0 pelo processo 1 (linhas 67-69). Enquanto essa comunicação não for completada, todos os processos estarão aguardando, sincronizados nas linhas 67-69 e 79-81. Ocorrida a comunicação entre os processos 0 e 1, ocorrerá a comunicação entre o 1 e o 2, entre o 2 e o 3 e assim por diante, de forma cíclica (linhas 67-71 e 79-81).

As últimas comunicações também ocorrem de forma cíclica, começando pelo envio de uip+10 pelo processo np1 (linhas 81-83) e o recebimento de ufp+10 pelo processo np2 (linhas 73-75). Em sequência, ocorrem as comunicações entre o processo np2 e o processo np3, entre o np3 e o np4, até o término das comunicações entre o processo 1 (linhas 76-77) e o processo 0 (linhas 63-65).

3.5.1 Exercícios

Em revisão

E. 3.5.1.

O problema (3.15)-(3.17) tem solução analítica

u(t,x)=eπ2tsen(πx). (3.33)

Modifique o código calor.cc acima, de forma que a acada iteração m no tempo, seja impresso na tela o valor de um(0.5) computado e o valor da solução analítica u(tm,0.5). Faça refinamentos nas malhas temporal e espacial, até conseguir uma aproximação com três dígitos significativos corretos.

E. 3.5.2.

Modifique o código calor.cc acima, de forma que a cada iteração m no tempo, seja impresso na tela o valor do funcional

q(t)=01u(t,x)𝑑x. (3.34)

Valide os resultados por comparação com a solução analítica (veja o Exercício (3.5.1)).

E. 3.5.3.

Modifique o código calor.cc acima, de forma que as comunicações iniciem-se entre os processos np1 e np2. Ou seja, que a primeira comunicação seja o envio de uip+10 pelo processo np1 e o recebimento de ufp+10 pelo processo np2. E que, de forma cíclica, as demais comunicações ocorram. Poderia haver alguma vantagem em fazer esta modificação?

E. 3.5.4.

Modifique o código calor.cc acima, de forma que as comunicações sejam feitas de forma assíncrona, i.e. usando as rotinas MPI_Isend e MPI_Irecv. Há vantagens em se fazer esta modificação?

E. 3.5.5.

Faça um código MPI para computar uma aproximação da solução de

utuxx=0,0<x<1,t>0, (3.35)

com condições de contorno periódicas

u(0,t)=u(1,t),t>0, (3.36)

e condições iniciais

u(x,0)=sen(πx),0x1, (3.37)

onde, u=u(t,x).


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
| | | |