UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado...

101
i UNIVERSIDADE ESTADUAL DE CAMPINAS FACULDADE DE ENGENHARIA MECÂNICA Relatório Final Trabalho de Conclusão de Curso Partição de Domínios para Processamento em Cluster de GPUs Autor: Lucas Monteiro Volpe Orientador: Prof. Dr. Luiz Otávio Saraiva Ferreira Campinas, novembro de 2012

Transcript of UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado...

Page 1: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

i

UNIVERSIDADE ESTADUAL DE CAMPINAS

FACULDADE DE ENGENHARIA MECÂNICA

Relatório Final

Trabalho de Conclusão de Curso

Partição de Domínios para

Processamento em Cluster de GPUs

Autor: Lucas Monteiro Volpe

Orientador: Prof. Dr. Luiz Otávio Saraiva Ferreira

Campinas, novembro de 2012

Page 2: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

ii

UNIVERSIDADE ESTADUAL DE CAMPINAS

FACULDADE DE ENGENHARIA MECÂNICA

Relatório Final

Trabalho de Conclusão de Curso

Partição de Domínios para

Processamento em Cluster de GPUs

Autor: Lucas Monteiro Volpe

Orientador: Prof. Dr. Luiz Otávio Saraiva Ferreira

Curso: Engenharia de Controle e Automação

Trabalho de Conclusão de Curso apresentado à Comissão de Graduação da

Faculdade de Engenharia Mecânica, como requisito para a obtenção do título de

Engenheiro de Automação e Controle.

Campinas, 2012

SP – Brasil

Page 3: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

iii

Agradecimentos

Agradeço a todos os professores da Unicamp que ministraram as disciplinas

durante o curso por tudo que pude aprender durante o curso.

Agradeço principalmente ao Prof. Luiz Otávio por ter me orientado durante o

trabalho e pela oportunidade de continuar me desenvolvendo nessa área no ano que

vem através do mestrado.

Agradeço minha família pelo apoio dado durante todo o curso de graduação.

Agredeço a todos os meus amigos pelos bons momentos compartilhados e por

me incentivarem durante a graduação.

Page 4: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

iv

Índice

Agradecimentos ................................................................................... iii

Índice ................................................................................................... iv

Resumo ................................................................................................. 1

Lista de Figuras ..................................................................................... 2

Lista de Tabelas .................................................................................... 4

Nomenclatura ........................................................................................ 5

Abreviações e Siglas ............................................................................. 6

Capítulo 1 Introdução ............................................................................................. 7

Capítulo 2 Computer Unified Device Architeture (CUDA) ....................................... 8

2.1 API CUDA ........................................................................................... 10

2.2 Hierarquia de memória ........................................................................ 12

2.3 Utilização de POSIX Threads com CUDA ........................................... 14

2.4 Programação CUDA em Múltiplas GPUs............................................. 14

2.5 Programação CUDA em clusters ......................................................... 14

Capítulo 3 O Método Lattice Boltzmann ............................................................... 16

3.1 A equação de Boltzmann .................................................................... 16

3.2 O Modelo Lattice Boltzmann................................................................ 17

3.3 O Método Lattice Boltzmann ............................................................... 19

3.4 Condições de Fronteira ....................................................................... 20

3.5 Viscosidade ......................................................................................... 21

Capítulo 4 Programa latibol .................................................................................. 23

4.1 Funcionamento do programa latibol .................................................... 29

4.2 Inicialização ......................................................................................... 30

4.3 Arquivos de saída ................................................................................ 32

4.4 Propagação ......................................................................................... 32

4.5 Colisão ................................................................................................ 34

4.6 Finalização .......................................................................................... 35

Capítulo 5 Método de Ensaio ............................................................................... 36

Capítulo 6 Resultados .......................................................................................... 38

Capítulo 7 Conclusão ........................................................................................... 51

Referência Bibliográfica ....................................................................... 52

Apêndice ............................................................................................. 53

Page 5: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

v

latibol.h: ............................................................................................... 53

latibol.cu: ............................................................................................. 55

initialize.h: ........................................................................................... 62

initialize.cu: .......................................................................................... 62

streaming.h: ........................................................................................ 73

streaming.cu: ....................................................................................... 73

get_border.h: ....................................................................................... 77

get_border.cu: ..................................................................................... 77

copy_border.h: .................................................................................... 80

copy_border.cu: ................................................................................... 80

apply_border.h: ................................................................................... 83

apply_border.cu: .................................................................................. 83

collision.h: ........................................................................................... 86

collision.cu: .......................................................................................... 86

save.h:................................................................................................. 92

save.cu: ............................................................................................... 92

cudaFree_data.h: ................................................................................ 94

cudaFree_data.cu: .............................................................................. 94

Page 6: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

1

Resumo

VOLPE, Lucas Monteiro, Partição de Domínios para Processamento em Cluster de

GPUs, Faculdade de Engenharia Mecânica, Universidade Estadual de Campinas,

Trabalho de Conclusão de Curso (2012), 101 pp.

Este trabalho de graduação tem como objetivo distribuir o processamento em

um cluster de GPUs de uma simulação de fluídos através do método lattice

Boltzmann, para casos em duas e três dimensões. O software foi implementado em

linguagem CUDA C, para o processamento em GPUs, e em MPI, para realização da

troca de informações entre os nós do cluster. Foram realizados testes em um cluster

de quatro nós com quatro GPUs em cada para diferentes tamanhos de domínio e

diferentes configurações, considerando diferentes quantidades de GPUs ou o uso de

uma CPU. Os resultados demonstram um grande aumento de desempenho com o

uso de GPUs em relação à programação clássica. Observa-se também um aumento

de eficiência do processamento em múltiplas GPUs conforme se aumenta o

tamanho do domínio, justificando o uso de mais de uma GPU a partir de certos

tamanhos de domínio.

Palavras Chave: Computação paralela, cluster, GPU, CUDA, MPI.

Page 7: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

2

Lista de Figuras

Figura 2.1. Capacidade de processamento de GPUs Nvidia e Processadores Intel .. 8

Figura 2.2. Organização de transístores em GPUs e CPUs ....................................... 8

Figura 2.3. GPU Fermi. ............................................................................................ 10

Figura 2.4. Grid 2x3 com blocos 4x3. ....................................................................... 11

Figura 2.5. Exemplo de execução de um programa CUDA ...................................... 11

Figura 3.1. Modelo lattice Boltzmann D2Q9 ............................................................. 18

Figura 3.2. Modelo lattice Boltzmann D3Q19 ........................................................... 18

Figura 3.3. Topologia de domínios para uma direção (esq.) e duas direções (dir.) ... 20

Figura 3.4. Movimento das densidades f a para a condição de fronteira bounce back

................................................................................................................................. 21

Figura 4.1. Imagem ‘input.bmp’ de tamanho 128x64 simulando condição no-slip .... 25

Figura 4.2. Exibição do terminal após execução do exemplo ................................... 26

Figura 4.3. Arquivos de saídas ‘r0000.pvti’ (acima) e ‘r0001.pvti’ (abaixo) ............... 26

Figura 4.4. Perfil de velocidades simulado (azul) e aproximação quadrática (preto) 27

Figura 4.5. Arquivo ‘input.bmp’ utilizado no exemplo 3D .......................................... 28

Figura 4.6. Exibição no terminal após execução do exemplo 3D .............................. 28

Figura 4.7. Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x .......... 28

Figura 4.8. Perfil de velocidades simulado (azul) e aproximação quadrática (preto)

para o exemplo 3D ................................................................................................... 29

Figura 4.9. Distribuição de um domínio entre 6 devices ........................................... 30

Figura 4.10. Distribuição das fronteiras durante a propagação em dois nós com dois

devices cada ............................................................................................................ 34

Figura 6.1. Velocidade de execução dos testes em relação à área do domínio para

diferentes sistemas (D2Q9) ...................................................................................... 39

Figura 6.2. Velocidade de execução dos testes em relação à área do domínio para

diferentes sistemas (D3Q19) .................................................................................... 39

Figura 6.3. Ganho de velocidade de execução em relação a CPU (D2Q9) .............. 41

Figura 6.4. Ganho de velocidade de execução em relação a CPU (D3Q19) ............ 41

Figura 6.5. Ganho de velocidade em relação ao mínimo necessário de GPUs

(D2Q9) ..................................................................................................................... 42

Figura 6.6. Ganho de velocidade em relação ao mínimo necessário de GPUs

(D3Q19) ................................................................................................................... 42

Page 8: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

3

Figura 6.7.Eficiência do processamento em relação ao mínimo necessário de GPUs

(D2Q9) ..................................................................................................................... 43

Figura 6.8. Eficiência do processamento em relação ao mínimo necessário de GPUs

(D3Q19) ................................................................................................................... 43

Figura 6.9. Ganho de velocidade em relação ao mínimo necessário de nós (D2Q9)44

Figura 6.10. Ganho de velocidade em relação ao mínimo necessário de nós (D3Q19)

................................................................................................................................. 45

Figura 6.11. Eficiência do processamento em relação ao mínimo necessário de nós

(D2Q9) ..................................................................................................................... 45

Figura 6.12. Eficiência do processamento em relação ao mínimo necessário de nós

(D3Q19) ................................................................................................................... 46

Figura 6.13. Velocidade de execução dos testes em relação à área do domínio

comparando 1 nó com 4 devices e 2 nós com 2 devices cada (D2Q9) .................... 47

Figura 6.14. Velocidade de execução dos testes em relação à área do domínio

comparando 1 nó com 4 devices e 2 nós com 2 devices cada (D3Q19) .................. 47

Figura 6.15. Eficiência do processamento em relação a 1 nó com 4 devices (D2Q9)

................................................................................................................................. 48

Figura 6.16 Eficiência do processamento em relação a 1 nó com 4 devices (D3Q19)

................................................................................................................................. 48

Figura 6.17. Tempo para salvar arquivo de saída em relação a área do domínio

(D2Q9) ..................................................................................................................... 49

Figura 6.18 Tempo para salvar arquivo de saída em relação a área do domínio

(D3Q19) ................................................................................................................... 49

Page 9: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

4

Lista de Tabelas

Tabela 4.1. Estrutura de dados DataStruct ......................................................... 31

Tabela 4.2. Estrutura de dados SaveStruct .......................................................... 31

Tabela 5.1. Tamanhos dos domínios utilizados nos testes 2D ................................. 36

Tabela 5.2. Tamanhos dos domínios utilizados nos testes 3D ................................. 37

Tabela 6.1. Velocidade de execução dos testes (D2Q9) .......................................... 38

Tabela 6.2. Velocidade de execução dos testes (D3Q19) ........................................ 38

Page 10: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

5

Nomenclatura

Letras Latinas

f Função de distribuição de probabilidades -

x Vetor de posição -

p Momento linear [mu.lu/ ts ]

n Número de partículas -

t Tempo [t s ]

e Velocidade microscópica [lu/ ts ]

q Quantidade de direções -

u Velocidade macroscópica [lu/ ts ]

w Peso da função de distribuição de equilíbrio -

c Velocidade de propagação [lu/ ts ]

Letras Gregas

Γ( + )

Quantidade de partículas que não vieram da condição

esperada

-

Γ( - )

Quantidade partículas que não foram para a condição

esperada

-

ρ Densidade [mu/lu3 ]

ν Viscosidade [lu2/ ts ]

τ Fator de relaxamento -

Subscritos

a Direção -

Page 11: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

6

Abreviações e Siglas

CUDA Computer Unified Device Architeture

MPI Message Passing Interface

GPU Unidade de Processamento Gráfico

CPU Unidade Central de Processamento

UVA Endereço Virtual Unificado

LBM Método lattice Boltzmann

BGK Bhatagner, Gross e Krook

NFS Network File System

Page 12: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

7

Capítulo 1

Introdução

Há cinco anos, foi apresentada pela Nvidia, empresa líder no segmento de

placas de vídeo de alto desempenho, a tecnologia CUDA, possibilitando a utilização

da placa de vídeo para execução de programas. Essa tecnologia surgiu em uma

época em que as placas de vídeo já apresentavam um poder de computação maior

comparado com o dos processadores. Porém esse poder de processamento só

pode ser atingido em aplicações altamente paralelas, nas quais um problema é

subdividido de forma que cada parte seja executada ao mesmo tempo, sendo

geralmente utilizado para acelerar simulações computacionais.

Um dos tipos de simulação que pode ser realizada de forma paralela é a

simulação de fluídos utilizando o método lattice Boltzmann, que realiza a

discretização da equação de Boltzmann e, através de funções probabilísticas, simula

o comportamento de fluídos em interações com sólidos ou outros fluídos.

O presente trabalho tem como objetivo implementar e distribuir o

processamento de uma simulação de fluídos utilizando o método lattice Boltzmann

para que seja executada em um cluster com múltiplas placas de vídeo. Como o foco

é a distribuição do processamento, é tratado um caso simples para a simulação,

sendo a interação de um fluído em uma determinada velocidade inicial com sólidos

utilizando domínios de duas ou três dimensões. O programa é implementado

utilizando a linguagem CUDA C com auxílio de MPI para realização da troca de

informações entre os nós do cluster.

Por final são comparadas as velocidades de processamento do programa

executado tanto em processadores, como em placas de vídeo, para diferentes

configurações, com a finalidade de mostrar todo o potencial da distribuição do

processamento em um cluster.

Page 13: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

8

Capítulo 2

Computer Unified Device Architeture (CUDA)

Nos últimos anos tem-se observado um grande aumento na capacidade

computacional das unidades de processamento gráficas (GPUs). Através da figura 1

é possível observar esse fato tanto para o número de operações por segundo como

para largura de banda das GPUs Nvidia em relação aos processadores da Intel.

Figura 2.1. Capacidade de processamento de

GPUs Nvidia e Processadores Intel. [1]

Esse poder de processamento maior das GPUs se deve ao fato de que elas

tratam os dados de forma altamente paralela, já que para as aplicações gráficas

normalmente realizam conjuntos de mesmas operações ao mesmo tempo. Na figura

2 é possível observar a diferença entre a forma como uma CPU é organizada e uma

GPU, percebendo-se que em uma GPU existem mais transistores para o

processamento de dados e menos para o controle de fluxo e memória cache. [1]

Figura 2.2. Organização de transístores em GPUs e CPUs. [1]

Page 14: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

9

Sendo assim, em 2007 a Nvidia lançou a Arquitetura CUDA (Computer Unified

Device Architeture), possibilitando a programação das GPUs para processamento

paralelo de dados, e não somente para aceleração gráfica. Para sua programação é

utilizada a linguagem CUDA C, que é basicamente uma extensão da linguagem C e

possibilita a programação paralela.

Desde seu lançamento, a arquitetura CUDA já passou por algumas

atualizações, a medida que foram surgindo novas arquiteturas para as GPUs, com

novas funcionalidades. Por isso é bom sempre estar atento nas capacidades da

GPU que será utilizada para execução do programa. Essas capacidades são

definidas pela versão de Compute Capability da GPU. A arquitetura G80 da Nvidia

lançou a versão 1.0 contendo 8 núcleos CUDA por multiprocessador, a qual foi

sofrendo algumas melhorias até chegar na versão 1.4.

A versão 2.0 foi lançada em 2010 com a arquitetura Fermi, aumentando para

32 núcleos CUDA por multiprocessador e adicionando o cache L1 e L2, tendo uma

atualização para versão 2.1 com 48 núcleos por multiprocessador. Com a versão 2.0

também foram introduzidas funções de acesso direto a memória entre GPUs, não

necessitando mais a criação de buffers na memória RAM para transferência de

dados entre GPUs. No início de 2012, foi introduzida a arquitetura Kepler na versão

3.0, agora com 192 núcleos CUDA por multiprocessador, e com grandes novidades,

como acesso direto na memória entre GPUs que estão em diferentes nós de um

cluster e o paralelismo dinâmico, que permite que funções executadas na GPU

sejam chamadas dentro de outras funções que já estão sendo executado na GPU,

possibilitando chamadas recursivas e minimizando o uso da CPU durante a

execução do programa. [3]

Para este trabalho são utilizadas GPUs Geforce GTX 580, as quais possuem

GPUs Fermi com capacidade de computação versão 2.0. Cada GPU Fermi da

Geforce GTX 580 possui 512 núcleos CUDA, organizados em 16

multiprocessadores de 32 núcleos. Cada núcleo possui uma unidade lógica

aritmética para números inteiros e uma para ponto flutuante. A estrutura da GPU

pode ser vista na figura abaixo. Na figura é possível ver os 16 multiprocessadores

compartilhando a mesma memória L2, sendo que cada multiprocessador é uma

Page 15: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

10

fileira vertical contendo uma parte laranja (scheduler e dispatch), uma parte verde

(unidades de execução) e uma azul claro (registradores e cache L1). [4]

Figura 2.3. GPU Fermi. [4]

2.1 API CUDA

A execução de um programa em CUDA funciona da seguinte forma, a CPU

(host) chama funções que são executadas na GPU (device), as quais são

denominadas de kernel. Um thread é um pedaço de um kernel ao qual será

executado em paralelo com outros threads do mesmo kernel. Os threads são

organizados em blocos, que juntos formam um grid. Sendo assim, um grid é um

conjunto de blocos de threads que executam o mesmo kernel, leem e escrevem na

memória global e são sincronizados conforme solicitado pelo kernel. A figura 4

mostra um grid de dimensão 2x3 com blocos de tamanho 3x4. [4]

A quantidade de threads por bloco é limitada de acordo com a GPU, sendo que

nas mais modernas, pode haver até 1024 threads por bloco. Cada bloco é

executado por um multiprocessador, então é ideal que a quantidade de blocos de

um kernel seja no mínimo a quantidade de multiprocessadores da GPU. Durante a

execução de um bloco, seus threads são agrupados em estruturas denominadas

warps, sendo que cada warp possui 32 threads e são executados de forma paralela

dentro do multiprocessador.

Page 16: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

11

Na figura 5 é apresentado um modelo de um aplicativo CUDA, onde a

sequência de códigos em série é executada pelo host e os códigos em paralelo pelo

device.

Figura 2.4. Grid 2x3 com blocos 4x3. [1]

Figura 2.5. Exemplo de execução de um programa CUDA. [1]

Page 17: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

12

2.2 Hierarquia de memória

Um dos aspectos mais importantes da programação em CUDA é conseguir

minimizar os custos das transferências de dados dentro do device e utilizar ao

mínimo a transferência de dados entre o host e o device, já que a largura de banda é

de 8 GB/s no barramento PCIe X16, que é bem inferior aos 192,4 GB/s de uma

GeForce GTX 580 [3]. Para isso é necessário entender como funcionam os diversos

tipos de memórias dentro de uma GPU e quais as diferenças de acesso e

velocidade de cada uma.

Memória Global: Memória com maior capacidade da GPU e também a com

maior latência de acesso, podendo ser acessada para leitura ou escrita por qualquer

thread ou pelo host. Na arquitetura Fermi, a memória global passou a possuir

caches L1 (48KB por multiprocessador) e L2 (768KB compartilhados pelos

multiprocessadores), melhorando o tempo de acesso para dados que já estão em

cache.

Memória Compartilhada (shared memory): Memória presente em cada

multiprocessador e possui acesso para leitura e escrita apenas para os threads que

estão no bloco que está sendo executado pelo multiprocessador. É a memória com

acesso mais rápido, pois está presente dentro do chip da GPU, e por padrão possui

16KB para cada multiprocessador, podendo ser configurada para até 48KB para

GPUs Fermi. [3]

Memória Constante (constant cached memory): Memória somente-leitura de

64KB que possui 8KB de cache por multiprocessador. As vantagens de seu uso são:

capacidade de executar meio warp com apenas um acesso a memória, caso os

dados a serem lidos sejam os mesmos para todos os threads; cache ser

relativamente grande em relação ao tamanho total, possibilitando que boa parte dos

acessos sejam feitos diretamente do cache. [2] [3]

Memória de Textura (texture cached memory): Memória somente-leitura com

cache de até 8KB que tem a propriedade de ter acessos mais rápidos quando um

thread acessa dados que estão na vizinhança 2D do thread executado

Page 18: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

13

anteriormente. Sua utilização é recomendada quando um programa exibe um padrão

de acesso espacial a memória. [2]

Registradores: Memória ultra-rápida que armazena os dados de um thread

necessários para execução de suas instruções. As GPUs Fermi possuem 63

registradores por thread. [3]

Memória Local: Memória alocada automaticamente por um thread quando a

quantidade de registradores disponíveis é menor que o necessário. [3]

Para aplicações CUDA, também existem diversas formas de mapear a

memória que será utilizada no host. Essas formas são apresentadas abaixo:

Paginada: É a forma padrão utilizada na linguagem C. Aloca memória

paginável pelo sistema operacional, ou seja, o seu endereço físico pode ser alterado

a qualquer momento dependendo da necessidade do sistema operacional.

Page-Locked Host Memory: Também conhecida como memória pinada, foi

introduzida na linguagem CUDA, permitindo a criação de memória não paginável

pelo sistema operacional. Como seu endereço físico não é alterado, sua utilização

permite que a GPU tenha acesso direto a memória (DMA) do host, não necessitando

a utilização da CPU, e assim proporcionando um acesso mais rápido e seguro.

Porém, seu uso deve ser feito com cautela, pois ela limita os recursos de memória

do sistema para outras aplicações. [2]

Zero-Copy Host Memory: Memória com as mesmas características da memória

pinada, porém permite que seja acessada e modificada diretamente pelo kernel, não

necessitando ser copiada para a memória do device antes do processamento dos

dados. Sua utilização é recomendada quando os dados só serão acessados uma

vez pelo kernel, economizando o tempo de cópia da memória, ou quando o sistema

possui unidades CUDA na própria placa mãe e já utiliza uma parte da memória host

como sendo do device. [2]

Portable Pinned Memory: Memória igual à pinada, porém seu uso se estende a

mais de um device, sendo utilizada em programas que usam mais de uma GPU.

Page 19: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

14

Caso todos os devices do sistema utilizem o recurso de espaço de endereço virtual

unificado (UVA) introduzido a partir da versão 2.0, seu uso tornou-se desnecessário,

já que o recurso UVA permite o acesso direto da memória de um device a partir de

outro device, sem precisar de um buffer no host para realizar a troca de dados. [3]

2.3 Utilização de POSIX Threads com CUDA

Também é possível dividir o processamento de um programa CUDA em POSIX

threads, também conhecido como Pthreads. A utilização de Pthreads permite que a

execução de uma parte do programa seja feita em um núcleo do processador

enquanto outro núcleo executa outra parte em paralelo. Em um programa CUDA as

Pthreads podem ser utilizadas para executar algo que necessite da CPU enquanto

ocorre o processamento na GPU, por exemplo, salvar um arquivo de saída enquanto

a GPU continua a processar os dados do próximo arquivo de saída. [11]

2.4 Programação CUDA em Múltiplas GPUs

Atualmente é possível ter até oito GPUs conectadas em uma mesma placa-

mãe, sendo possível distribuir a execução de um programa CUDA utilizando mais de

um device em cada nó de um cluster.

Para implementar programas em múltiplas GPUs, primeiro é necessário definir

uma estrutura com todas as variáveis que são alocadas na memória da GPU, sendo

criado um vetor dessas estruturas com o tamanho da quantidade de GPUs

presentes no sistema.

Antes da execução do kernel, deve-se definir qual GPU irá executá-lo através

da função cudaSetDevice() e depois chamar o kernel passando como parâmetro

a estrutura que está alocada na GPU que foi definida.

2.5 Programação CUDA em clusters

Também é possível distribuir o processamento em múltiplas estações com

múltiplas GPUs, através da programação em Message Passing Interface (MPI). O

MPI é uma extensão da linguagem C assim como CUDA, que possui um conjunto de

Page 20: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

15

funções utilizadas para a comunicação entre computadores [5]. Felizmente é

possível realizar chamadas de aplicações CUDA dentro de um programa em MPI,

facilitando muito a adaptação de um programa CUDA em MPI.

As funções de MPI basicamente realizam o envio e o recebimento de

mensagens de dados entre processos. Quando um programa em MPI é executado,

é definido pelo usuário o número de processos que serão iniciados e como eles

serão distribuídos, podendo ser entre os diversos núcleos de um processador ou

entre os nós de um cluster. Quando o programa é iniciado, é atribuído um número

de posição para cada processo, sendo este número que define o que será

executado por cada processo, possibilitando distribuir o processamento de acordo

com as necessidades do programa.

Page 21: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

16

Capítulo 3

O Método Lattice Boltzmann

Ludwig Boltzmann (1844-1906) foi um físico austríaco que desenvolveu

métodos estatísticos para descrever o comportamento de partículas. A ideia

fundamental de seu trabalho é que as interações de partículas de um gás podem ser

descritas baseada na mecânica clássica, e, pelo fato de existir muitas partículas, é

necessário um tratamento estatístico adequado das interações. [6]

3.1 A equação de Boltzmann

Um sistema dinâmico pode ser descrito como diversas partículas que possuem

um vetor de posição (x) e um momento linear (p) para cada partícula em

determinado instante de tempo. Essas informações fornecem o estado dinâmico

atual do sistema que, através das leis da mecânica clássica, torna-se possível

prever qualquer estado futuro. [6]

Sendo assim, um sistema pode ser descrito como uma função de distribuição

f( n )

(xn,pn, t ) , com n igual ao número de partículas. Quando se quer analisar

propriedades que não dependem da posição, pode-se definir uma função de

distribuição f ( 1 )(x,p , t ) para descrever o sistema. Essa função de distribuição indica

a probabilidade de encontrar uma partícula em uma dada posição e momento linear.

[6]

A determinação de f( 1 ) num tempo t+dt e dada pela equação que define o

processo de propagação:

. (1)

Porém, é preciso levar em conta também que ocorrem colisões. Considerando

Γ ( -)dxdpdt como o número de partículas que não foram para a condição esperada

e Γ(+)dxdpdt o de partículas que não vieram da condição esperada, modifica-se a

equação (1) para:

Page 22: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

17

. (2)

Realizando uma expansão de primeira ordem da série de Taylor, chega-se na

equação de Boltzmann:

. (3)

Como é muito difícil de encontrar uma solução para equação de Boltzmann, o

Método de Lattice Boltzmann utiliza a discretização da equação (2) para se

encontrar uma solução aproximada [6]. O método lattice Boltzmann utiliza uma

versão aproximada da equação original, discretizada no espaço, tempo e momento e

projetada em um reticulado.

3.2 O Modelo Lattice Boltzmann

O modelo lattice Boltzmann considera a posição de cada partícula como um nó

do lattice e sua velocidade distribuída nas direções dos nós vizinhos, de forma que

cada direção tem um peso apropriado [6]. Os modelos mais utilizados são o D2Q9

para duas dimensões e nove direções e o D3Q19 para três dimensões e dezenove

direções. O modelo de um nó D2Q9 é apresentado na figura 3.1 e o D3Q19 na

figura 3.2, sendo e a as velocidades microscópicas em cada direção a . A distância

entre nós é definida por unidades lattice (lu ) e o tempo em passos de tempo (t s).

Page 23: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

18

Figura 3.1. Modelo lattice Boltzmann D2Q9. [6]

Figura 3.2. Modelo lattice Boltzmann D3Q19. [10]

Considerando f a como a densidade do fluido em cada direção a , e q sendo a

quantidade de direções do método, tem-se que a densidade macroscópica é:

∑ (4)

Page 24: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

19

A velocidade macroscópica u também pode ser calculada como a somatória

das médias das velocidades microscópicas, sendo:

(5)

3.3 O Método Lattice Boltzmann

Como comentado anteriormente, o método lattice Boltzmann (LBM) baseia-se

na discretização da equação (2), para isso primeiro é necessário uma aproximação

para o termo da colisão. Em 1954 Bhatagner, Gross e Krook (BGK) definiram o

seguinte modelo simplificado para o termo da colisão:

(6)

Sendo τ denominado fator de relaxamento e fe q a função de distribuição de

Maxwell-Boltzmann, a qual define o equilíbrio local. [7]

Substituindo o termo da colisão e discretizando a equação (2) chega-se na

equação que define o método:

(7)

Onde o termo direito do lado direito da igualdade representa a colisão e o

restante representa a propagação. No método, primeiramente é feito o cálculo da

propagação e depois acrescentado a colisão, que dependerá das condições de

fronteira da partícula.

Durante a propagação o que ocorre é o transporte da densidade f a para o nó

lattice mais próximo de acordo com sua direção.

Já para a colisão, primeiro é necessário calcular a função de distribuição de

Maxwell-Boltzmann, que é definida da seguinte forma:

Page 25: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

20

[

] (8)

Para o modelo D2Q9, os pesos w a são 16/36 para a=0 , 4/36 para a=1. . .4 , e

1/36 para a=5. . .8 , e c é a velocidade de propagação em lu/ ts . [8] Já para o

modelo D3Q19, esse pesos ficam sendo 12/36 para a=0 , 2/36 para a=1. . .6 e

1/36 para a=7. . .18 . [10]

A partir da equação (8) é possível calcular o termo da colisão para interações

entre as partículas do fluído. Mas caso ocorra colisão entre a partícula de um fluído

e um sólido, se torna necessária a aplicação das condições de fronteira antes de

realizar a colisão, por isso a necessidade de dividir o calculo da equação (7) em

duas partes.

3.4 Condições de Fronteira

Um nó lattice pode ter diversas formas de condições de fronteira, sendo as

mais básicas a condição de fronteira periódica e a bounce back.

A condição de fronteira periódica é o tipo mais simples que pode ser aplicado.

Aplicando uma condição deste tipo, basicamente faz-se a ligação de extremidades

do domínio de acordo com as direções desejadas. A figura 3.3 mostra as topologias

dos domínios quando aplicadas condições de fronteira para uma direção e para

duas direções em um modelo de duas dimensões.

Figura 3.3. Topologia de domínios para uma direção (esq.) e duas direções (dir.). [6]

Page 26: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

21

A condição de fronteira bounce back trata a interação das partículas do fluido

com sólidos. Quando uma partícula tem um nó laticce sólido como vizinho, este nó

armazena as densidades vindas da partícula durante a propagação e as devolvem

na direção oposta durante a propagação do próximo passo de tempo. A figura 8

ilustra esse procedimento.

Figura 3.4. Movimento das densidades f a para a condição de

fronteira bounce back. [6]

3.5 Viscosidade

A viscosidade cinemática ν , dada em lu2/ t s , se relaciona com o fator de

relaxamento τ de acordo com a seguinte equação:

(

) (9)

Pela análise da equação é possível perceber que τ deve ser maior ou igual 0,5

para garantir a viscosidade positiva. Também se percebe que o limite para τ → 0,5

Page 27: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

22

representa um fluxo invíscido e que o limite para τ → ∞ a condição de fluxo de

Stokes. Para o caso de fluxo invíscido pode se ter problemas de estabilidade para se

utilizar o LBM, pois se a resolução do lattice não for suficientemente pequena, as

variações de velocidade podem se tornar muito grandes e o modelo pode não

conseguir dissipar toda a energia devido à baixa viscosidade. [9]

Page 28: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

23

Capítulo 4

Programa latibol

O programa latibol é um simulador do método lattice Boltzmann desenvolvido

em CUDA C e MPI que permite a execução paralela em múltiplas GPUs e em

diversos nós de um cluster.

O simulador permite realizar simulações para o modelo lattice Boltzmann D2Q9

ou D3Q19 de um tipo de fluido com suporte a sólidos com condição de fronteira

bounce-back e em um domínio periódico.

O programa deve ser compilado e executado em sistema operacional Linux.

Para compilar é necessário ter instalado o Cuda Toolkit na versão 4.0 ou superior e o

OpenMPI, bastando executar o comando make dentro da pasta latibol.

Após a compilação, para executar o programa deve-se entrar na pasta

‘\bin\linux\release’, e chamar o programa através do comando mpiexec no

terminal conforme descrito abaixo.

mpiexec -n X --hostfile <arquivo de hosts> -x LD_LIBRARY_PATH

latibol

Este comando executa o programa latibol em X processos, sendo os processos

distribuídos de acordo com o <arquivo de hosts>, que deve ser preenchido com o

nome das máquinas que correspondem a cada nó, podendo ser indicados a

quantidade de processos que serão iniciados em cada máquina da seguinte forma:

node00

node01 slots=2

node02 slots=4

Neste caso o computador node00 executará o primeiro processo, o node01

executará os 2 seguintes e o node02 os 4 próximos, sendo que caso seja solicitado

Page 29: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

24

a execução de mais que 7 processos, o oitavo será executado pelo node00,

seguindo a mesma ordem anterior até o último processo.

Um fator importante para que a execução do programa em mais de um nó

ocorra com sucesso, é a necessidade de que a pasta em que o programa esteja

sendo executado esteja compartilhada entre os nós, podendo utilizar, por exemplo,

um servidor NFS (Network File System).

Durante a execução, as condições iniciais são lidas do arquivo texto

‘parameters.ini’ e do arquivo de imagem ‘input.bmp’ em formato bitmap

monocromático.

O arquivo ‘parameters.ini’ deve conter os seguintes dados na seguinte ordem:

- Quantidade de devices por nó (0 indica a execução pela CPU); - Profundidade do domínio (> 1 define a execução do método D3Q19) [lu ]; - Velocidade inicial do fluido em x [lu/ts ]; - Velocidade inicial do fluido em y [lu/ ts ]; - Velocidade inicial do fluido em z [lu/ ts ]; - Viscosidade do fluido [lu

2/ t s ];

- Densidade inicial do fluido [mu/lu3 ];

- Número total de passos de tempo [t s ]; - Número de passos entre cada arquivo de saída salvo [t s ];

Abaixo é apresentado um exemplo em que é executado o modelo D2Q9 em

uma GPU com velocidade em x de 0,05 lu/ ts , velocidade nula em y , viscosidade de

0,01 lu2/ t s , densidade de 1 mu/lu

3 e sendo executados 20.000 passos de tempo,

com a saída de dados salva apenas no início e no final da execução.

‘parameters.ini’: 1

1

0.05

0.00

0.00

0.01

1.00

20000

20000

Page 30: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

25

O arquivo de imagem serve para definir o tamanho do domínio em x e y e

definir quais nós são sólidos. É importante olhar o tamanho do domínio que o

programa detectou em uma primeira execução teste, para definir o tamanho da

imagem em bitmap, pois devido a distribuição do domínio ser feita de forma igual

entre as GPUs, pode ocorrer a perda de partes das bordas esquerda e inferior na

execução do programa. Na imagem, os pixels em preto definem o nós sólidos e os

brancos definem o fluido. Um exemplo de imagem para um domínio de 128x64 com

sólidos nas bordas superiores e inferiores, simulando a condição no-slip, é

apresentado na figura abaixo.

Figura 4.1. Imagem ‘input.bmp’ de tamanho 128x64 simulando condição no-slip

Os arquivos de saída são do tipo VTK Parallel Image Data nomeados como

‘rxxxx.pvti’, sendo xxxx o número da saída em ordem crescente. Esses arquivos

podem ser lidos através do programa Paraview.

Durante a execução, primeiramente é mostrado no terminal quantos devices o

sistema possui e os valores dos parâmetros lidos das entradas na seguinte ordem:

largura, altura, profundidade, velocidade em x , velocidade em y , velocidade em z ,

viscosidade, densidade, o valor de τ devido à viscosidade, número de passos e

passos entre cada saída gerada. Depois, conforme o programa salva as saídas, é

mostrado o nome do arquivo de saída, seguido do tempo para salvar a saida, do

tempo de execução atual e da velocidade com que foi calculado cada passo de

tempo em t s /s .

Como exemplo, será executado o programa com o arquivo ‘parameters.ini’ e a

imagem ‘input.bmp’ dados como exemplo acima em um sistema com um device. Na

figura 4.2 é mostrada a saída que o terminal apresentou após a execução do

exemplos e na figura 4.3 os dois arquivos de resultados abertos no Paraview.

Page 31: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

26

Figura 4.2. Exibição do terminal após execução do exemplo

Figura 4.3. Arquivos de saídas ‘r0000.pvti’ (acima) e ‘r0001.pvti’ (abaixo)

Como o exemplo apresentado simula o fluxo de um fluído entre duas paredes,

é possível validar a simulação comparando com um caso real em que a curva de

velocidades apresenta um comportamento parabólico. Observando a figura abaixo,

percebe-se que o perfil de velocidades obtido na simulação é muito próximo da

aproximação para uma parábola, confirmando o sucesso da simulação.

Page 32: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

27

Figura 4.4. Perfil de velocidades simulado (azul) e aproximação quadrática (preto)

Para realizar uma simulação em 3 dimensões, basta definir um valor maior que

1 de profundidade no arquivo ‘paramters.ini’, conforme exemplo abaixo:

‘parameters.ini’: 1

128

0.00

0.00

0.05

0.01

1.00

10000

10000

Executando o programa com o arquivo ‘parameters.ini’ apresentado acima e a

imagem ‘input.bmp’ conforme a figura 4.5, obtém-se a tela da figura 4.6 e o resultado

do arquivo ‘r0001.pvti’ conforme figura 4.7.

Page 33: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

28

Figura 4.5. Arquivo ‘input.bmp’ utilizado no exemplo 3D

Figura 4.6. Exibição no terminal após execução do exemplo 3D

Figura 4.7. Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x

Page 34: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

29

O comportamento de curva de velocidade resultando no caso D3Q19 também

se aproxima muito do resultado esperado, conforme figura 4.8.

Figura 4.8. Perfil de velocidades simulado (azul) e aproximação

quadrática (preto) para o exemplo 3D

4.1 Funcionamento do programa latibol

O programa latibol é implementado em linguagem CUDA C, utilizando recursos

de programação MPI e POSIX Threads. O programa é implementado de forma a

utilizar os recursos de endereço de memória compartilhado, necessitando uma

GPUs com capacidade de computação na versão 2.0 ou superior.

A estrutura básica do programa consiste de:

Inicialização;

Repete para a quantidade de passos definido:

o Calculo da fase de propagação;

o Troca de dados entre GPUs e entre nós do cluster;

o Aplicação das condições de fronteira;

o Cálculo da fase de colisão;

o Salva resultado no formato VTK conforme definido;

Finalização;

Page 35: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

30

As etapas da estrutura do programa são abordadas nas próximas subseções.

4.2 Inicialização

Na etapa de inicialização do programa o que ocorre são a criação das variáveis

que serão utilizadas pela CPU, a leitura dos arquivos de entrada ‘parameters.ini’ e

‘input.bmp’, a alocação das memórias utilizadas pela GPU e CPU e a transferência

dos dados da condição inicial para a memória de uma ou mais GPUs.

O arquivo ‘input.bmp’ é lido de forma a definir os sólidos nas direções x e y .

Quando é definida uma profundidade z para o sistema, os sólidos são repetidos

igualmente para cada valor de z . A divisão do arquivo de sólidos entre os devices ou

as CPUs é feita de forma a dividir igualmente o domínio realizando cortes em x ,

conforme mostrado na figura abaixo.

Caso o programa seja executado em GPUs, o tamanho dos blocos CUDA que

é definido é de 32x4x1. Sendo assim os domínios são dividos de forma a serem

múltiplos de 32 em x e 4 em y , podendo ocorrer perda de solídos definidos no

arquivo ‘input.bmp’.

Para realizar a distribuição do programa entre diversas GPUs, é definida uma

estrutura de dados DataStruct contendo todas as variáveis que serão necessárias

pela GPU. Essas variáveis são apresentadas na tabela abaixo, sendo que as

variáveis que são definidas como ponteiros, são alocadas em forma de matriz com o

tamanho da quantidade de nós lattice do domínio que será executado na GPU.

1 2 3 5 4 6

x

y

z

Figura 4.9. Distribuição de um domínio entre 6 devices

Page 36: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

31

Tabela 4.1. Estrutura de dados DataStruct

Descrição Tipo Variável

Velocidade float *u

Sólidos bool *solid

Sólidos sem fronteira com o fluído bool *solid_bound

Densidades atuais do fluido em cada direção

float *fa (D2Q9: a = 0...8; D3Q19: a = 0...18)

Densidades novas do fluido em cada direção

float *fa_new (D2Q9: a = 0...8; D3Q19: a = 0...18)

Bordas compartilhadas float *border_fa (D2Q9: a = 1,3,5,6,7,8; D3Q19: a = 1,2,7...14)

Bordas compartilhadas do vizinho float *nb_border_fa (D2Q9: a = 1,3,5,6,7,8; D3Q19: a = 1,2,7...14)

Tamanho do domínio na GPU para cada dimensão

int width, height, length (3D)

Também é definida uma estrutura SaveStruct que é usada para salvar os

arquivos de saída. A definição dessa estrutura se torna necessária para poder

utilizar Pthreads para realizar essa função, e assim poder continuar processando

dados pela GPU enquanto a CPU salva o arquivo. As variáveis presentes nessa

estrutura são mostrada na tabela abaixo.

Tabela 4.2. Estrutura de dados SaveStruct

Descrição Tipo Variável

Velocidades float *u

Sólidos bool *solid

Nome do arquivo char *name

Passo atual int step

Número do processo MPI int commRank

Número do device int deviceID

Quantidade total de devices int deviceCount

Tamanho do domínio na GPU para cada dimensão

int width, height, length (3D)

A função initialize() é que realiza a alocação de dados na CPU e na

GPU, lê o arquivo de sólidos, calcula os valores das funções de densidade e das

velocidades para condição inicial e, caso especificado o processamento em GPU,

realiza a transferência dos dados iniciais para o device.

Page 37: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

32

Caso o programa seja executado em um cluster com vários nós, é também

realizada a alocação de buffers border_fa_buffer, para transferência de dados

das bordas compartilhadas entre os nós do cluster.

4.3 Arquivos de saída

Os arquivos de saída são salvos no formato VTK XML ImageData, sendo que é

necessária a criação de arquivos de dois tipos, paralelo e serial. O tipo serial, de

extensão vti, reúne um conjunto de dados em ponto flutuante, sendo que cada

dado representa a velocidade de um nó lattice. Cada processo MPI salva um arquivo

vti com os dados do domínio relativo a esse processo, sendo a união dos dados

realizada pelo arquivo paralelo. O tipo paralelo possui extensão pvti e nele contém

quais são os arquivos do tipo serial que serão unidos para formar a imagem final,

sendo salvo apenas pelo primeiro processo MPI através da função psave().

Conforme mencionado anteriormente, os arquivos são salvos a partir de uma

estrutura contendo os dados de sólidos e velocidades, sendo criada uma Pthread

que executa a função save() em cada processo MPI. Antes de salvar os arquivos,

os dados de velocidades são transferidos da estrutura DataStruct. Como quem

salva os arquivos é a CPU, enquanto a Pthread estiver sendo executada, o

programa continuará a computar os dados do método lattice Boltzmann

independetemente, já que os dados da estrutura SaveStruct não são influenciados

pelo método até que seja necessário salvar um novo arquivo.

4.4 Propagação

A etapa de propagação é realizada pela função streaming() que apenas

copia os valores da densidade de fa para próximo nó lattice na direção a em

fa_new. Quando este nó não existe, pelo fato de o nó atual estar em alguma

fronteira do grid, é feita a cópia para o nó da fronteira oposta, aplicando a condição

de fronteira periódica ao sistema.

Caso o programa seja executado de forma distribuída, para finalizar o processo

de propagação, é necessário compartilhar os valores de fa_new para os nós que

Page 38: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

33

se encontram na fronteira entre devices ou processos MPI. O processo de

compartilhamento de fronteira é descrito nos próximos parágrafos, sendo

apresentadas todas as etapas deste processo na figura 4.6, mostrando o caso de

um domínio divido em dois processos MPI com dois devices em cada. Nesta figura é

demonstrado onde as fronteiras de fa estão alocadas nas outras variáveis, sendo

indicado o número do processo p x , o número do device d x e se o valor da

coordenada x é o inicial ou o final.

Para realizar esse processo, primeiramente é copiada os dados das bordas

que vão ser compartilhadas para a estrutura border_fa através da função

get_border(). Essa cópia se torna necessária para poder realizar a troca de

apenas os dados necessários entre as GPUs e os processos MPI, restringindo ao

máximo o fluxo de dados entre eles, já que esse fluxo se torna um gargalo para o

programa devido às larguras de banda menores.

Após os valores de border_fa estarem completos, é feita a cópia de

border_fa para nb_border_fa do device que vai receber os dados através da

função copy_border_devices(). Essa cópia é realizada através de funções

cuda_MemcpyAsync(), que copiam os dados de forma assíncrona. Essa função é

chamada de forma a utilizar os recursos de endereço de memória unificado (UVA),

sendo informado o tipo de transferência cudaMemcpyDefault.

Caso a execução do programa também esteja distribuída entre nós de um

cluster, é realizada a troca das informações de nb_border_fa() nas fronteiras dos

processos MPI. Para realizar essa troca de dados é utilizada a função

copy_border_nodes(), que copia os dados de nb_border_fa() do device para

os buffers border_fa_buffer no host, realiza chamadas

MPI_Sendrecv_replace(), que faz a troca dos dados dos buffers entre os nós

vizinhos, e transfere novamente os dados para os devices.

Depois os dados de nb_broder_fa são copiados de volta para os seus

lugares em fa_new pela função apply_border(), completando o processo de

propagação.

Page 39: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

34

Figura 4.10. Distribuição das fronteiras durante a

propagação em dois nós com dois devices cada

4.5 Colisão

Com os valores das funções de densidade devidamente propagados para

fa_new, pode-se então realizar a etapa de colisão. A colisão consiste em aplicar a

aplicar o termo de colisão nas funções de densidade fa sendo executada através da

função collision().

Existem duas possibilidades de se aplicar a colisão. Se o nó lattice é um sólido

que possue alguma fronteira com o fluído é aplicada a condição de fronteira bounce-

back. Caso o nó seja fluído, são calculados os valores da densidade do fluido de

Page 40: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

35

acordo com a equação (4), das velocidades em cada direção e da velocidade total

que será enviada para o arquivo de saída conforme a equação (5). Com esses

valores calculados, é possível obter a função de densidade de equilíbrio pela

equação (8) e aplicá-la em fa através da resolução da equação (6), concluindo o

processo de colisão.

4.6 Finalização

Após executados todos os passos e salvos os arquivos de saída conforme

definido pelo arquivo de inicialização, é necessário desalocar as memórias dos

devices e hosts. As memórias dos devices são desalocadas pela função

cudaFree_data() que desaloca todos os ponteiros definidos em dataStruct.

Para terminar é finalizado os processos MPI através da chamada

MPI_Finalize().

Page 41: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

36

Capítulo 5

Método de Ensaio

Para avaliar o desempenho da execução do simulador, foram realizados testes

para diferentes quantidades de GPUs e tamanhos de domínios para os casos D2Q9

e D3Q19.

O cluster utilizado para o teste possui 16 GPUs Geforce GTX 580, com 1,5 GB

de memória cada e 1,58 TFLOPs de capacidade de processamento cada,

totalizando 24 GB de memória e 25,3 TFLOPs. Os testes foram realizados para 1, 2,

4, 8, 12 e 16 GPUs. Para comparação, também foram executados testes utilizando

os quatro núcleos de um processador Intel Core i7 950 a 3,06 GHz que possui cerca

de 49 GFLOPs de capacidade de processamento, cerca de 32 vezes menor que

uma GPU.

Os tamanhos de domínios foram escolhidos de forma a comparar o ganho de

desempenho obtido desde um domínio pequeno, que utiliza pouca memória, até um

domínio que utiliza praticamente todos os 18 GB disponível de memória. Todos os

tamanhos de domínio utilizados e a quantidade de memória ocupada são

demonstrados nas tabelas 5.1 e 5.2.

Tabela 5.1. Tamanhos dos domínios utilizados nos testes 2D

Tamanho do Domínio

Memória Utilizada (MB)

600x128 6

1200x256 23

2400x512 91

4800x1024 366

9600x2048 1463

12800x3072 2925

19200x4096 5850

25600x6144 11700

32960x7168 17574

38400x8192 23400

Page 42: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

37

Tabela 5.2. Tamanhos dos domínios utilizados nos testes 3D

Tamanho do Domínio

Memória Utilizada (MB)

128x24x24 11

192x36x36 38

256x52x52 104

384x76x76 334

640x120x120 1389

832x148x148 2746

1024x188x188 5454

1280x240x240 11110

1408x280x280 16635

1536x310x310 22244

Page 43: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

38

Capítulo 6

Resultados

Os resultados dos testes, dados em passos de tempo processados por

segundo, são demonstrados na tabela 6.1 para o modelo D2Q9 e na tabela 6.2 para

o D3Q19. Também são apresentados gráficos em escala logarítmica relacionando a

velocidade de execução com o tamanho do domínio nas figuras 6.1 e 6.2.

Tabela 6.1. Velocidade de execução dos testes (D2Q9)

Tamanho do Domínio

CPU (ts/s)

1 GPU (ts/s)

2 GPUs (ts/s)

4 GPUs (ts/s)

8 GPUs (ts/s)

12 GPUs (ts/s)

16 GPUs (ts/s)

600x128 850 11980 3360 1830 485 - -

1200x256 91,1 3148 2270 1700 466 - 494

2400x512 29,7 755 983 1152 458 640 673

4800x1024 9 175 298 507 486 500 534

9600x2048 1,84 43,1 80 162 218 270 333

12800x3072 0,75 - 40,6 74 120 134,3 203

19200x4096 - - - 39,6 65,06 93,25 113

25600x6144 - - - - 34,7 48,4 57

32960x7168 - - - - - 31,8 38,3

38400x8192 - - - - - - 32,9

Tabela 6.2. Velocidade de execução dos testes (D3Q19)

Tamanho do Domínio

CPU (ts/s)

1 GPU (ts/s)

2 GPUs (ts/s)

4 GPUs (ts/s)

8 GPUs (ts/s)

12 GPUs (ts/s)

16 GPUs (ts/s)

128x24x24 302 4556 1844 1099 - - -

192x36x36 129 1395 1201 975 - - -

256x52x52 31,2 505 627 657 310 - -

384x76x76 10 154 248 334 194 192 -

640x120x120 1,74 36,9 66,6 111 80,4 94,3 91,2

832x148x148 0,79 - 33,4 64,6 35,6 41,1 47,6

1024x188x188 - - - 34,2 22,1 31,6 27,7

1280x240x240 - - - - 10,8 15,6 17,9

1408x280x280 - - - - - 10,4 13,6

1536x310x310 - - - - - - 9,82

Page 44: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

39

Figura 6.1. Velocidade de execução dos testes em relação à área do

domínio para diferentes sistemas (D2Q9)

Figura 6.2. Velocidade de execução dos testes em relação à área do

domínio para diferentes sistemas (D3Q19)

Analisando o gráfico do caso 2D percebe-se que para um domínio de até

70.000 lu2 não há aumento de velocidade em se usar mais de uma GPU, e que para

domínios de até 600.000 lu2 não há aumento de velocidade em se utilizar mais de

um nó. Também se observa que adicionando GPUs para executar o programa,

possibilita a execução de domínios cada vez maiores, ampliando a gama de

problemas que podem ser resolvidos.

Page 45: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

40

Para o caso 3D nota-se que para domínios a partir de 40.000 lu3 passou-se a

obter ganho de velocidade em se adicionar mais GPUs, mas em nenhum momento

houve aumento de velocidade em se utilizar mais de um nó, limitando seu uso

apenas para domínios em que necessite de mais de 6GB de memória. Este fato

ocorre pois a quantidade de dados das fronteiras compartilhadas é muito maior no

caso 3D que no 2D, exigindo um maior tráfego de dados pela rede, que possui um

tempo de resposta relativamente alto e uma largura de banda de 1GB/s, 8 vezes

menor que a largura de banda entre as GPUs.

A partir dos dados obtidos, também foram feitos alguns gráficos para análises

de fatores mais pontuais. O primeiro gráfico é apresentado na figura 6.3, mostrando

o ganho de velocidade do uso de até 4 GPUs em um mesmo nó em relação a

execução na CPU para os diversos domínios testados no caso 2D. Analisando o

gráfico, observa-se que a execução em uma GPU chegou a ser até 35 vezes mais

rápida que na CPU, ficando dentro da expectativa da GPU possuir um poder de

processamento cerca de 32 vezes maior que a CPU utilizada. Para o caso de 2

GPUs, a execução foi 54 vezes mais rápida e para 4 GPUs, 99 vezes,

demonstrando que mesmo com a troca de dados das fronteiras compartilhadas,

pode-se obter grandes ganhos de velocidades adicionando mais devices. Um gráfico

similar é apresentado para o caso 3D na figura 6.4, indicando que a execução

chegou a ser 21 vezes mais rápida utilizando 1 GPU e até 82 vezes maior utilizando

4 GPUs, obtendo um desempenho relativamente menor em relação ao caso 2D.

Page 46: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

41

Figura 6.3. Ganho de velocidade de execução em relação a CPU (D2Q9)

Figura 6.4. Ganho de velocidade de execução em relação a CPU (D3Q19)

Os gráficos apresentados nas figuras 6.5 e 6.6 demonstram o ganho em se

utilizar até 12 GPUs em relação a mínimo possível de GPUs utilizáveis para um

determinado tamanho de domínio. Também são mostrados gráficos avaliando a

eficiência da execução do programa em relação a menor quantidade de GPUs

utilizáveis para os domínios, nas figuras 6.7 e 6.8. A partir destes gráficos nota-se

que quanto maior o domínio, menor é a perda de eficiência em se utilizar mais GPU,

chegando sempre a valores maiores que 90%, isso ocorre porque o tamanho da

fronteira compartilhada se torna relativamente menor em relação à área do domínio

processada nas GPUs conforme o domínio aumenta. Sendo assim é recomendado

sempre minimizar o máximo possível o tamanho da borda compartilhada antes de

executar um problema.

Page 47: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

42

Figura 6.5. Ganho de velocidade em relação ao

mínimo necessário de GPUs (D2Q9)

Figura 6.6. Ganho de velocidade em relação ao

mínimo necessário de GPUs (D3Q19)

Page 48: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

43

Figura 6.7.Eficiência do processamento em relação

ao mínimo necessário de GPUs (D2Q9)

Figura 6.8. Eficiência do processamento em relação

ao mínimo necessário de GPUs (D3Q19)

Abaixo são apresentados gráficos que relacionam o ganho de velocidade e a

eficiência de até quatro nós de um cluster em relação ao mínimo possível de nós

para execução dos domínios. Pela análise dos gráficos para o caso D2Q9, percebe-

se que não se consegue uma eficiência maior que 80% quando se utiliza mais de

um nó, devido a menor largura de banda na transferência dos dados entre os nós e

da necessidade da transferência dos dados para o host para realizar a troca de

Page 49: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

44

informação entre os processos MPI. Para o caso do domínio de 25600x6144, que

não pode ser executado em apenas um nó devido a disponibilidade de memória, a

eficiência da utilização de 3 nós chega aos 90% em relação a 2 nós, já que em

ambos os casos existe a necessidade de copiar os dados para o host. Já para o

caso D3Q19, percebe-se melhor o quanto se perde de velocidade ao se utilizar mais

de um nó, chegando-se ao máximo de 30% de eficiência. Porém em domínios que

necessitem de mais de um nó, a utilização de um terceiro nó obteve uma eficiência

de cerca de 90% justificando o uso de vários nós para esses tamanhos de domínio.

Figura 6.9. Ganho de velocidade em relação ao mínimo necessário de nós (D2Q9)

Page 50: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

45

Figura 6.10. Ganho de velocidade em relação ao mínimo necessário de nós (D3Q19)

Figura 6.11. Eficiência do processamento em relação

ao mínimo necessário de nós (D2Q9)

Page 51: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

46

Figura 6.12. Eficiência do processamento em relação

ao mínimo necessário de nós (D3Q19)

Outro item avaliado foi a diferença entre se usar um nó com 4 GPUs, dois nós

com duas GPUs cada e 4 nós com uma GPU cada. Nos gráficos abaixo são

mostradas a velocidade de execução e a eficiência para os diferentes tamanhos de

domínio para os dois casos analisados, demonstrando que para domínios

suficientemente grandes, a perda de eficiência com a utilização de dois ou mais nós

se torna mínima para o caso D2Q9, mas é significativa no caso D3Q19, que realiza

uma quantidade muito maior de troca de informações entre os nós.

Page 52: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

47

Figura 6.13. Velocidade de execução dos testes em relação à área do domínio

comparando 1 nó com 4 devices e 2 nós com 2 devices cada (D2Q9)

Figura 6.14. Velocidade de execução dos testes em relação à área do domínio

comparando 1 nó com 4 devices e 2 nós com 2 devices cada (D3Q19)

Page 53: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

48

Figura 6.15. Eficiência do processamento em relação a 1 nó com 4 devices (D2Q9)

Figura 6.16 Eficiência do processamento em relação a 1 nó com 4 devices (D3Q19)

Outro aspecto importante para avaliação do desempenho do programa é o

tempo para salvar o arquivo de saída, que influencia no tempo de execução total do

programa. Abaixo é apresentado o gráfico relacionando o tempo para salvar um

arquivo de saída com a quantidade de nós lattice do domínio para os casos 2D e 3D.

Page 54: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

49

Figura 6.17. Tempo para salvar arquivo de saída

em relação a área do domínio (D2Q9)

Figura 6.18 Tempo para salvar arquivo de saída

em relação a área do domínio (D3Q19)

Pela análise dos gráficos conclui-se que a utilização de mais de um nó

proporciona um menor tempo para saída ser salva, devido à distribuição dos

domínios entre os nós e a largura de banda maior que mais nós proporcionam, já

que cada nó salva um arquivo diferente, sendo que com 2 nós os arquivos são

salvos 2 vezes mais rápidos e com 3 nós, 2,5 vezes mais rápido. Para quatro nós

Page 55: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

50

observa-se que a saída já é salva em um tempo maior que com três nós, devido a

um maior tempo de acesso que quatro nós acessando um disco rígido ao mesmo

tempo proporciona.

Para o caso da CPU percebe-se uma vantagem em relação a um device,

devido aos dados serem distribuídos em quatro núcleos e já estarem sendo

executados pela CPU, porém vê-se uma diferença, que não é vista pelas GPUs,

entre o caso 2D e 3D, sendo que no caso 3D o tempo foi 25% menor.

Page 56: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

51

Capítulo 7

Conclusão

A utilização de computação paralela com placas de vídeo demonstrou-se muito

eficiente na resolução de problemas como o do método lattice Boltzmann, sendo

muito superior a computação tradicional, chegando a executar o método até 30

vezes mais rápido em relação ao processador utilizando apenas uma GPU. Com a

distribuição do domínio em várias GPUs, além de se obter velocidades de

processamento maiores, aumenta-se a quantidade de memória disponível para a

resolução do método, possibilitando a execução em domínios maiores.

Porém o ganho de velocidade alcançado aumentando a quantidade de GPUs

depende do tamanho do domínio processado, sendo que para domínios

relativamente pequenos, perde-se desempenho adicionando mais GPUs, devido ao

tempo gasto para as trocas de dados entre as GPUs e os nós não compensar. Por

isso é importante definir a quantidade de GPUs utilizadas para resolver um

determinado problema de acordo com o tamanho de seu domínio.

Também foi observado que para o caso D3Q19 não se obtém um aumento de

velocidade ao se utilizar mais de um nó, devido à baixa largura de banda disponível

entre os nós do cluster, justificando seu uso apenas para domínios que necessitam

mais memória que a disponível em um nó. Uma das possibilidades de melhorar esse

desempenho é através do aumento da largura de banda da rede ethernet utilizada,

que pode ser dobrada para 2GB/s utilizando os dois canais que cada nó possui em

paralelo. Há margem, ainda, para melhoria de desempenho pelo refinamento do

software, utilizando técnicas mais eficientes de programação.

Este trabalho terá continuidade como tema de dissertação de mestrado do

autor, também sob orientação do Prof. Luiz Otávio.

Page 57: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

52

Referência Bibliográfica

[1] NVIDIA, NVIDIA CUDA C Programming Guide 4.2. 2012.

[2] SANDER, Jason. CUDA by Example: An Introduction to General-Purpose GPU

Programming. nVidia, 2011.

[3] NVIDIA, CUDA C Best Practices Guide 4.1. 2012.

[4] NVIDIA, Fermi Compute Architecture Whitepaper 1.1. 2009.

[5] TREVISAN, J. Programação Paralela Utilizando MPI: aspectos práticos.

GRUCAD, UFSC, Florianópolis, 2005. Disponível em:

<http://www.grucad.ufsc.br/julio/doc/mpi.htm>. Acesso em: mai/2012.

[6] SUKOP, Michael. Lattice Boltzmann Modeling: An Introduction for Geoscientists

and Engineers. 2ª edição, Springer-Verlag, 2007.

[7] MOHAMED, A. A.. Lattice Boltzmann Method: Fundamentals and Engineering

Applications with Computer Codes. 1ª edição, Springer-Verlag, 2011.

[8] HE, X. and LUO, L. S.. Theory of the lattice Boltzmann method: From the

Boltzmann equation to the lattice Boltzmann equation. Phys. Rev. E, 56(6):6811–

6817, 1997.

[9] CHIRILA, Dragos. Introduction to Lattice Boltzmann Methods. Alfred Wegener

Institute, 2010.

[10] HECHT, M.; HARTING, J.. Implementation of on-site velocity boundary

conditions for D3Q19 lattice Boltzmann simulations. Journal of Statistical Mechanics:

Theory and Experiment, 28 de Janeiro de 2010.

[11] RAUBER, T; GUDULA, R.. Parallel Programming for Multicore and Cluster

Systems. 2ª edição, Springer-Verlag, 2010.

Page 58: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

53

Apêndice

latibol.h:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* This header file defines the memory structure that will be allocated in each device.

* Each variable is an array of the domain total dimension, except the border variables.

**************************************************************************************************/

#ifndef LB_H

#define LB_H

typedef struct {

// Velocity [lu/ts]

float *u;

// Solid node

bool *solid, *solid_bound;

// Density functions

float *f0, *f1, *f2, *f3, *f4;

float *f5, *f6, *f7, *f8;

// Density function for the next time step

float *f0_new, *f1_new, *f2_new, *f3_new, *f4_new;

float *f5_new, *f6_new, *f7_new, *f8_new;

// Border Density Functions

float *border_f1, *border_f5, *border_f8;

float *border_f3, *border_f6, *border_f7;

// Neighbor border density function

float *nb_border_f1, *nb_border_f5, *nb_border_f8;

float *nb_border_f3, *nb_border_f6, *nb_border_f7;

// Size of the domain

int width, height;

// Stream

cudaStream_t stream;

} DataStruct;

typedef struct {

// Velocity [lu/ts]

float *u;

// Solid node

bool *solid, *solid_bound;

// Density functions

float *f0, *f1, *f2, *f3, *f4;

float *f5, *f6, *f7, *f8, *f9;

float *f10, *f11, *f12, *f13, *f14;

float *f15, *f16, *f17, *f18;

// Density function for the next time step

float *f0_new, *f1_new, *f2_new, *f3_new, *f4_new;

float *f5_new, *f6_new, *f7_new, *f8_new, *f9_new;

float *f10_new, *f11_new, *f12_new, *f13_new, *f14_new;

float *f15_new, *f16_new, *f17_new, *f18_new;

// Border Density Functions

float *border_f1, *border_f7, *border_f8, *border_f9, *border_f10;

float *border_f2, *border_f11, *border_f12, *border_f13, *border_f14;

// Neighbor border density function

float *nb_border_f1, *nb_border_f7, *nb_border_f8, *nb_border_f9, *nb_border_f10;

float *nb_border_f2, *nb_border_f11, *nb_border_f12, *nb_border_f13, *nb_border_f14;

// Size of the domain

int width, height, length;

// Stream

cudaStream_t stream;

Page 59: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

54

} DataStruct3d;

typedef struct {

// Velocity [lu/ts]

float *u;

// Solid node

bool *solid, *solid_bound;

// Density functions

float *f0, *f1, *f2, *f3, *f4;

float *f5, *f6, *f7, *f8;

// Density function for the next time step

float *f0_new, *f1_new, *f2_new, *f3_new, *f4_new;

float *f5_new, *f6_new, *f7_new, *f8_new;

// Border Density Functions

float *border_f1, *border_f5, *border_f8;

float *border_f3, *border_f6, *border_f7;

// Size of the domain

int width, height;

} DataStructCpu;

typedef struct {

// Velocity [lu/ts]

float *u;

// Solid node

bool *solid, *solid_bound;

// Density functions

float *f0, *f1, *f2, *f3, *f4;

float *f5, *f6, *f7, *f8, *f9;

float *f10, *f11, *f12, *f13, *f14;

float *f15, *f16, *f17, *f18;

// Density function for the next time step

float *f0_new, *f1_new, *f2_new, *f3_new, *f4_new;

float *f5_new, *f6_new, *f7_new, *f8_new, *f9_new;

float *f10_new, *f11_new, *f12_new, *f13_new, *f14_new;

float *f15_new, *f16_new, *f17_new, *f18_new;

// Border Density Functions

float *border_f1, *border_f7, *border_f8, *border_f9, *border_f10;

float *border_f2, *border_f11, *border_f12, *border_f13, *border_f14;

// Size of the domain

int width, height, length;

} DataStruct3dCpu;

typedef struct {

// Velocity [lu/ts]

float *u;

// Solid node

bool *solid;

// Name

char *name;

// ID variables

int step, commRank, deviceID, deviceCount;

// Size of the domain

int width, height, length;

} SaveStruct;

#endif

Page 60: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

55

latibol.cu:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* Main function

**************************************************************************************************/

// Include standard libraries

#include <stdio.h>

#include <stdlib.h>

#include <cutil.h>

#include <cuda_runtime_api.h>

#include <time.h>

#include <cmath>

#include <mpi.h>

#include <pthread.h>

#include <error.h>

#include <iostream>

using std::cout;

using std::cerr;

using std::endl;

// Include functions files

#include "latibol.h"

#include "initialize.h"

#include "save.h"

#include "cudaFree_data.h"

#include "streaming.h"

#include "get_border.h"

#include "copy_border.h"

#include "apply_border.h"

#include "collision.h"

// Define CUDA block width and height

#define BLOCK_X 32

#define BLOCK_Y 4

#define BLOCK_Z 1

// Error handling macros

#define MPI_CHECK(call) \

if((call) != MPI_SUCCESS) { \

cerr << "MPI error calling \""#call"\"\n"; \

my_abort(-1); }

// Shut down MPI cleanly if something goes wrong

void my_abort(int err)

{

cout << "\nMPI error!\n";

MPI_Abort(MPI_COMM_WORLD, err);

}

int main(int argc, char **argv)

{

// Initialize MPI state

MPI_CHECK(MPI_Init(&argc, &argv));

// Get our MPI node number and node count

int commSize, commRank;

MPI_CHECK(MPI_Comm_size(MPI_COMM_WORLD, &commSize));

MPI_CHECK(MPI_Comm_rank(MPI_COMM_WORLD, &commRank));

MPI_Status Stat;

pthread_t thread;

/**********************************************************************************************

* Create variables

**********************************************************************************************/

// Inputs

float tau, tau_inv, ux, uy, uz, rho, viscosity;

int length, width, height;

int step, iterations;

// Number of devices

int deviceCount;

// Time variables

double t, t_start, t_last, t_save;

// Counters

int i, j, k;

Page 61: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

56

// Weights for equilibrium distribution function

float w0, w1, w2;

// Auxiliary

char *temp;

int left, right;

int last, sysDevs;

int read;

bool gpu;

bool is_3d;

int img_width, img_height;

dim3 grid, block;

// Files variables

FILE *parameters, *input, *out;

// Devices data structure

DataStruct *data;

DataStruct3d *data_3d;

DataStructCpu *data_cpu;

DataStruct3dCpu *data_3d_cpu;

SaveStruct *saveData;

// Border buffers

float *border_f1_buf, *border_f2_buf, *border_f3_buf, *border_f5_buf, *border_f6_buf;

float *border_f7_buf, *border_f8_buf, *border_f9_buf, *border_f10_buf;

float *border_f11_buf, *border_f12_buf, *border_f13_buf, *border_f14_buf;

/**********************************************************************************************

* Read input files

**********************************************************************************************/

read = 0;

t_start = MPI_Wtime();

// Read the parameters from 'parameters.ini' file

parameters=fopen("parameters.ini","r");

if (parameters == NULL) {printf("\nparameters.ini not found\n\n"); return 0;}

read=fscanf(parameters, "%d\n", &deviceCount) + read;

read=fscanf(parameters, "%d\n", &length) + read;

read=fscanf(parameters, "%f\n", &ux) + read;

read=fscanf(parameters, "%f\n", &uy) + read;

read=fscanf(parameters, "%f\n", &uz) + read;

read=fscanf(parameters, "%f\n", &viscosity) + read;

read=fscanf(parameters, "%f\n", &rho) + read;

read=fscanf(parameters, "%d\n", &iterations) + read;

read=fscanf(parameters, "%d\n", &step) + read;

fclose(parameters);

if (read != 9) {printf("\nerror: verify parameters.ini\n\n"); return 0;}

// Define GPU or CPU mode

if (deviceCount > 0) gpu = 1;

else gpu = 0;

// Verify if the system has enough devices

if (gpu)

{

CUDA_SAFE_CALL( cudaGetDeviceCount( &sysDevs ) );

if (sysDevs < deviceCount) {printf("\ninsufficient devices: verify parameters.ini\n\n"); return 0;}

}

if (length >= BLOCK_Z && length > 1) is_3d = 1;

else is_3d = 0;

// Calculate tau and its inverse

tau = 3.*viscosity/rho + 0.5;

tau_inv = 1./tau;

// Calculate constants

if (is_3d)

{

w0 = 12.f/36.f;

w1 = 2.f/36.f;

w2 = 1.f/36.f;

}

else

{

w0 = 16.f/36.f;

w1 = 4.f/36.f;

w2 = 1.f/36.f;

}

// Allocate temporary variable that will read from input file

temp = (char *)malloc(2*sizeof(char));

// Get the lattice width and height for one device from 'input00.bmp'

// monochromatic bitmap file

img_width=0;

Page 62: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

57

img_height=0;

input = fopen("input.bmp","r");

// Read the width

if (input == NULL) {printf("\ninput.bmp not found\n\n"); return 0;}

fseek(input, 0x12, SEEK_SET);

read=fscanf(input, "%c", &temp[0]) + read;

read=fscanf(input, "%c", &temp[1]) + read;

for (i=0; i<8; i++) {

if ( ( (int)temp[0] & (1 << i) ) >> i ) img_width = img_width + pow(2.0, i);

if ( ( (int)temp[1] & (1 << i) ) >> i ) img_width = img_width + pow(2.0, i)*256;

}

// Read the height

fseek(input, 0x16, SEEK_SET);

read=fscanf(input, "%c", &temp[0]) + read;

read=fscanf(input, "%c", &temp[1]) + read;

for (i=0; i<8; i++) {

if ( ( (int)temp[0] & (1 << i) ) >> i ) img_height = img_height + pow(2.0, i);

if ( ( (int)temp[1] & (1 << i) ) >> i ) img_height = img_height + pow(2.0, i)*256;

}

if (read != 13) {printf("\nerror: verify input.bmp\n\n"); return 0;}

// Close the file and free temporary variable

fclose( input );

free( temp );

// Get the number of devices

if (commRank == 0)

{

// Write all the parameters at the screen

printf ("\nnodes = %d\n", commSize);

printf ("devices = %d\n", deviceCount);

}

// Calculate the size of the domain

if (!gpu) deviceCount = 1;

width = (img_width-1 + commSize*deviceCount) / (commSize*deviceCount);

width = (width/BLOCK_X)*BLOCK_X;

height = (img_height/BLOCK_Y)*BLOCK_Y;

length = (length/BLOCK_Z)*BLOCK_Z;

if (length <= BLOCK_Z) length = 1;

if (commRank == 0)

{

printf ("width = %d\n", (width-1)*commSize*deviceCount+1);

printf ("height = %d\n", height);

printf ("length = %d\n", length);

printf ("ux = %f\n", ux);

printf ("uy = %f\n", uy);

printf ("uz = %f\n", uz);

printf ("viscosity = %f\n", viscosity);

printf ("rho = %f\n", rho);

printf ("tau = %f\n", tau);

printf ("iterations = %d\n", iterations);

printf ("step = %d\n\n", step);

}

/**********************************************************************************************

* Initialize devices

**********************************************************************************************/

// Initialize data structure and allocate devices memories

if (is_3d)

if (gpu)

{

saveData = (SaveStruct *) malloc (deviceCount*sizeof(SaveStruct));

data_3d = (DataStruct3d *)malloc(deviceCount*sizeof(DataStruct3d ));

initialize_3d( data_3d, saveData, deviceCount, ux, uy, uz, rho, w0, w1, w2,

width, height, length, img_width, commRank );

}

else

{

saveData = (SaveStruct *) malloc (sizeof(SaveStruct));

data_3d_cpu = (DataStruct3dCpu *)malloc(sizeof(DataStruct3dCpu ));

initialize_3d_cpu( data_3d_cpu, saveData, ux, uy, uz, rho, w0, w1, w2,

width, height, length, img_width, commRank );

}

else

if (gpu)

{

saveData = (SaveStruct *) malloc (deviceCount*sizeof(SaveStruct));

data = (DataStruct *)malloc(deviceCount*sizeof(DataStruct ));

initialize( data, saveData, deviceCount, ux, uy, rho, w0, w1, w2, width, height, img_width,

commRank );

}

else

{

Page 63: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

58

saveData = (SaveStruct *) malloc (sizeof(SaveStruct));

data_cpu = (DataStructCpu *)malloc(sizeof(DataStructCpu ));

initialize_cpu ( data_cpu, saveData, ux, uy, rho, w0, w1, w2, width, height, img_width,

commRank );

}

if (is_3d)

{

// Calculate the grid and block dimension

grid = dim3(width/BLOCK_X, height/BLOCK_Y, length/BLOCK_Z);

block = dim3(BLOCK_X, BLOCK_Y, BLOCK_Z);

// Allocate CPU border buffers

border_f1_buf = (float *)malloc(height*length*sizeof(float));

border_f7_buf = (float *)malloc(height*length*sizeof(float));

border_f8_buf = (float *)malloc(height*length*sizeof(float));

border_f9_buf = (float *)malloc(height*length*sizeof(float));

border_f10_buf= (float *)malloc(height*length*sizeof(float));

border_f2_buf = (float *)malloc(height*length*sizeof(float));

border_f11_buf= (float *)malloc(height*length*sizeof(float));

border_f12_buf= (float *)malloc(height*length*sizeof(float));

border_f13_buf= (float *)malloc(height*length*sizeof(float));

border_f14_buf= (float *)malloc(height*length*sizeof(float));

}

else

{

// Calculate the grid and block dimension

grid = dim3(width/BLOCK_X, height/BLOCK_Y);

block = dim3(BLOCK_X, BLOCK_Y);

// Allocate CPU border buffers

border_f1_buf = (float *)malloc(height*sizeof(float));

border_f5_buf = (float *)malloc(height*sizeof(float));

border_f8_buf = (float *)malloc(height*sizeof(float));

border_f3_buf = (float *)malloc(height*sizeof(float));

border_f6_buf = (float *)malloc(height*sizeof(float));

border_f7_buf = (float *)malloc(height*sizeof(float));

}

out=fopen("out.txt","w");

/**********************************************************************************************

* Run Lattice Boltzmann Method and save output files

**********************************************************************************************/

for(i=0; i < (iterations/step)+1; i++)

{

// Save data file and show saving time

if (gpu) CUDA_SAFE_CALL( cudaDeviceSynchronize() );

t = MPI_Wtime() - t_start;

if (commRank == 0) psave(width, height, length, i, deviceCount, commSize);

for (k=0; k < deviceCount; k++)

{

// Wait previous thread ends

if (i != 0) pthread_join (thread, NULL);

// Copy the result to saveData

if (gpu)

{

CUDA_SAFE_CALL( cudaSetDevice( k ) );

if (is_3d) {CUDA_SAFE_CALL( cudaMemcpyAsync(saveData[k].u, data_3d[k].u,

sizeof(float)*width*height*length, cudaMemcpyDefault, data_3d[k].stream));}

else {CUDA_SAFE_CALL( cudaMemcpyAsync(saveData[k].u, data[k].u,

sizeof(float)*width*height, cudaMemcpyDefault, data[k].stream));}

}

else

{

if (is_3d) for (j=0; j < width*height*length; j++) saveData->u[j] = data_3d_cpu->u[j];

else for (j=0; j < width*height; j++) saveData->u[j] = data_cpu->u[j];

}

saveData[k].step = i;

saveData[k].deviceID = k;

saveData[k].deviceCount = deviceCount;

// Execute the Save Pthread

pthread_create (&thread, NULL, save, &saveData[k]);

if (i == iterations/step) pthread_join (thread, NULL);

}

t_save = MPI_Wtime() - t_start - t;

t = MPI_Wtime() - t_start;

if (commRank == 0)

{

printf("- t_save=%.2f ms ", t_save*1000);

fprintf(out, "r%04d.pvti - t_save=%.2f ms ", i, t_save*1000);

if (i == 0)

{

printf ("(%.2f s) \n", t);

fprintf (out, "(%.2f s) \n", t);

Page 64: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

59

}

else

{

printf ("(%.2fs - %.2f tsps) \n", t, step/((float)(t-t_last-t_save)));

fprintf (out, "(%.2fs - %.2f tsps) \n", t, step/((float)(t-t_last-t_save)));

}

}

t_last=t;

// Run 'step' times without saving

if (i < (iterations/step)) for (j=0; j < step; j++)

{

// Run streaming process for each device

if (gpu) for (k=0; k < deviceCount; k++)

{

CUDA_SAFE_CALL( cudaSetDevice( k ) );

if (is_3d) streaming_3d( &data_3d[k], grid, block );

else streaming ( &data[k], grid, block );

}

else if (is_3d) streaming_3d_cpu ( data_3d_cpu );

else streaming_cpu ( data_cpu );

if (deviceCount > 1 || commSize > 1)

{

// Get the border for each device

if (gpu) for (k=0; k < deviceCount; k++)

{

CUDA_SAFE_CALL( cudaSetDevice( k ) );

if (is_3d) get_border_3d ( &data_3d[k], grid, block );

else get_border ( &data[k], grid, block );

}

else if (is_3d) get_border_3d_cpu ( data_3d_cpu );

else get_border_cpu ( data_cpu );

// Copy border data between neighbor devices

if (gpu)

{

if (is_3d) for (k = 0; k < deviceCount; k++)

{

if (deviceCount == 1) { left = 0; right = 0; last = 0; }

else

{

if (k > 0 && k < (deviceCount-1)) { left = k-1; right = k+1; }

if (k == 0) { left = deviceCount-1; right = k+1; }

if (k == deviceCount-1) { left = k-1; right = 0; }

}

CUDA_SAFE_CALL( cudaSetDevice( k ) );

copy_border_3d_devices(data_3d[k].nb_border_f1, data_3d[k].nb_border_f7,

data_3d[k].nb_border_f8,

data_3d[k].nb_border_f9, data_3d[k].nb_border_f10,

data_3d[k].nb_border_f2, data_3d[k].nb_border_f11,

data_3d[k].nb_border_f12,

data_3d[k].nb_border_f13, data_3d[k].nb_border_f14,

data_3d[left].border_f1, data_3d[left].border_f7,

data_3d[left].border_f8,

data_3d[left].border_f9, data_3d[left].border_f10,

data_3d[right].border_f2, data_3d[right].border_f11,

data_3d[right].border_f12,

data_3d[right].border_f13, data_3d[right].border_f14,

height, length, data_3d[k].stream);

}

else for (k = 0; k < deviceCount; k++)

{

if (deviceCount == 1) { left = 0; right = 0; last = 0; }

else

{

if (k > 0 && k < (deviceCount-1)) { left = k-1; right = k+1; }

if (k == 0) { left = deviceCount-1; right = k+1; }

if (k == deviceCount-1) { left = k-1; right = 0; }

}

CUDA_SAFE_CALL( cudaSetDevice( k ) );

copy_border_devices(data[k].nb_border_f1, data[k].nb_border_f5,

data[k].nb_border_f8,

data[k].nb_border_f3, data[k].nb_border_f6,

data[k].nb_border_f7,

data[left].border_f1, data[left].border_f5,

data[left].border_f8, data[right].border_f3,

data[right].border_f6, data[right].border_f7,

height, data[k].stream);

}

}

// Copy border data between neighbor nodes

Page 65: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

60

if (commRank == 0) { left = commSize-1; right = commRank+1; }

if (commRank == (commSize-1)) { left = commRank-1; right = 0; }

if (commRank > 0 && commRank < (commSize-1)) { left = commRank-1; right = commRank+1; }

last = deviceCount-1;

if (gpu) CUDA_SAFE_CALL( cudaDeviceSynchronize() );

if (commSize > 1)

{

if (gpu)

{

if (is_3d)

{

CUDA_SAFE_CALL( cudaSetDevice( 0 ) );

copy_border_3d_nodes( border_f1_buf, border_f7_buf, border_f8_buf,

border_f9_buf, border_f10_buf,

data_3d[0].nb_border_f1, data_3d[0].nb_border_f7,

data_3d[0].nb_border_f8,

data_3d[0].nb_border_f9, data_3d[0].nb_border_f10,

height, length, right, left, Stat, data_3d[0].stream);

CUDA_SAFE_CALL( cudaSetDevice( last ) );

copy_border_3d_nodes( border_f2_buf, border_f11_buf, border_f12_buf,

border_f13_buf, border_f14_buf,

data_3d[last].nb_border_f2, data_3d[last].nb_border_f11,

data_3d[last].nb_border_f12, data_3d[last].nb_border_f13,

data_3d[last].nb_border_f14,

height, length, left, right, Stat, data_3d[last].stream);

}

if (!is_3d)

{

CUDA_SAFE_CALL( cudaSetDevice( 0 ) );

copy_border_nodes( border_f1_buf, border_f5_buf, border_f8_buf,

data[0].nb_border_f1, data[0].nb_border_f5,

data[0].nb_border_f8,

height, right, left, Stat, data[0].stream);

CUDA_SAFE_CALL( cudaSetDevice( last ) );

copy_border_nodes( border_f3_buf, border_f6_buf, border_f7_buf,

data[last].nb_border_f3, data[last].nb_border_f6,

data[last].nb_border_f7,

height, left, right, Stat, data[last].stream);

}

}

else

{

if (is_3d)

{

copy_border_3d_nodes_cpu( data_3d_cpu->border_f1, data_3d_cpu->border_f7,

data_3d_cpu->border_f8,

data_3d_cpu->border_f9, data_3d_cpu->border_f10,

height, length, right, left, Stat );

copy_border_3d_nodes_cpu( data_3d_cpu->border_f2, data_3d_cpu->border_f11,

data_3d_cpu->border_f12,

data_3d_cpu->border_f13, data_3d_cpu->border_f14,

height, length, left, right, Stat );

}

else

{

copy_border_nodes_cpu( data_cpu->border_f1, data_cpu->border_f5, data_cpu-

>border_f8,

height, right, left, Stat );

copy_border_nodes_cpu( data_cpu->border_f3, data_cpu->border_f6, data_cpu-

>border_f7,

height, left, right, Stat );

}

}

}

// Apply the border received from neighbor devices

if (gpu) for (k=0; k < deviceCount; k++)

{

CUDA_SAFE_CALL( cudaSetDevice( k ) );

if(is_3d) apply_border_3d ( &data_3d[k], grid, block );

else apply_border ( &data[k], grid, block );

}

else if(is_3d) apply_border_3d_cpu ( data_3d_cpu );

else apply_border_cpu ( data_cpu );

}

// Apply bounce-back, calculate the velocities and the equilibrium distribution function

// Run collision process

if (gpu) for (k=0; k < deviceCount; k++)

{

CUDA_SAFE_CALL( cudaSetDevice( k ) );

if (is_3d) collision_3d( &data_3d[k], w0, w1, w2, tau_inv, grid, block );

Page 66: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

61

else collision ( &data[k], w0, w1, w2, tau_inv, grid, block );

}

else if (is_3d) collision_3d_cpu ( data_3d_cpu, w0, w1, w2, tau_inv );

else collision_cpu ( data_cpu, w0, w1, w2, tau_inv );

}

}

/**********************************************************************************************

* Clear devices and exit

**********************************************************************************************/

if (gpu)

if (is_3d)

{

cudaFree_data_3d( data_3d, deviceCount );

free( data_3d );

}

else

{

cudaFree_data( data, deviceCount );

free( data );

}

else if (is_3d) free( data_3d_cpu );

else free( data_cpu );

if (is_3d)

{

free( border_f1_buf );

free( border_f7_buf );

free( border_f8_buf );

free( border_f9_buf );

free( border_f10_buf );

free( border_f2_buf );

free( border_f11_buf );

free( border_f12_buf );

free( border_f13_buf );

free( border_f14_buf );

}

else

{

free( border_f1_buf );

free( border_f5_buf );

free( border_f8_buf );

free( border_f3_buf );

free( border_f6_buf );

free( border_f7_buf );

}

free( saveData );

MPI_CHECK(MPI_Finalize());

if ( commRank == 0 ) printf("\n");

return 0;

}

Page 67: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

62

initialize.h:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 28.11.2012

* UNICAMP - Universidade Estadual de Campinas

*

* This header file defines the initialize function. This function allocates the memory in the

* devices, reads the solid inputs from "input.bmp" and passes the initial conditions to

* the devices.

**************************************************************************************************/

#ifndef INIT_H

#define INIT_H

extern "C"

void initialize( DataStruct *data, SaveStruct *saveData, int deviceCount, float ux, float uy,

float rho, float w0, float w1, float w5, int width, int height, int img_width,

int commRank);

extern "C"

void initialize_3d( DataStruct3d *data, SaveStruct *saveData, int deviceCount, float ux, float uy,

float uz, float rho, float w0, float w1, float w5, int width, int height, int length,

int img_width, int commRank);

extern "C"

void initialize_cpu( DataStructCpu *data, SaveStruct *saveData, float ux, float uy,

float rho, float w0, float w1, float w5, int width, int height,

int img_width, int commRank);

extern "C"

void initialize_3d_cpu( DataStruct3dCpu *data, SaveStruct *saveData, float ux, float uy,

float uz, float rho, float w0, float w1, float w5, int width, int height,

int length, int img_width, int commRank);

#endif

initialize.cu:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* Initialize funtion

**************************************************************************************************/

#include <cutil.h>

#include <cuda_runtime_api.h>

#include "latibol.h"

extern "C"

void initialize( DataStruct *data, SaveStruct *saveData, int deviceCount, float ux, float uy,

float rho, float w0, float w1, float w5, int width, int height, int img_width,

int commRank)

{

// Create variables on host side

float *f0, *f1, *f2, *f3, *f4, *f5, *f6, *f7, *f8, *u;

bool *solid, *solid_bound;

// Create auxiliary variables

char temp;

int read = 0;

// Counters

int i, j, k;

int xn, x, xp, yn, y, yp;

// Input file

FILE *input;

// Allocate memories on host side

u = (float *)malloc(width*height*sizeof(float));

solid = (bool *)malloc(width*height*sizeof(bool));

solid_bound = (bool *)malloc(width*height*sizeof(bool));

f0 = (float *)malloc(width*height*sizeof(float));

f1 = (float *)malloc(width*height*sizeof(float));

Page 68: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

63

f2 = (float *)malloc(width*height*sizeof(float));

f3 = (float *)malloc(width*height*sizeof(float));

f4 = (float *)malloc(width*height*sizeof(float));

f5 = (float *)malloc(width*height*sizeof(float));

f6 = (float *)malloc(width*height*sizeof(float));

f7 = (float *)malloc(width*height*sizeof(float));

f8 = (float *)malloc(width*height*sizeof(float));

// Calculate initial density functions for initial velocity condition

for (i=0; i<width*height; i++)

{

f0[i] = w0*rho*( 1. - 1.5*(ux*ux+uy*uy) );

f1[i] = w1*rho*( 1. + 3.*(+ux ) + 4.5*(+ux )*(+ux ) - 1.5*(ux*ux+uy*uy) );

f2[i] = w1*rho*( 1. + 3.*( +uy) + 4.5*( +uy)*( +uy) - 1.5*(ux*ux+uy*uy) );

f3[i] = w1*rho*( 1. + 3.*(-ux ) + 4.5*(-ux )*(-ux ) - 1.5*(ux*ux+uy*uy) );

f4[i] = w1*rho*( 1. + 3.*( -uy) + 4.5*( -uy)*( -uy) - 1.5*(ux*ux+uy*uy) );

f5[i] = w5*rho*( 1. + 3.*( ux+uy) + 4.5*( ux+uy)*( ux+uy) - 1.5*(ux*ux+uy*uy) );

f6[i] = w5*rho*( 1. + 3.*(-ux+uy) + 4.5*(-ux+uy)*(-ux+uy) - 1.5*(ux*ux+uy*uy) );

f7[i] = w5*rho*( 1. + 3.*(-ux-uy) + 4.5*(-ux-uy)*(-ux-uy) - 1.5*(ux*ux+uy*uy) );

f8[i] = w5*rho*( 1. + 3.*( ux-uy) + 4.5*( ux-uy)*( ux-uy) - 1.5*(ux*ux+uy*uy) );

u[i] = sqrtf(ux*ux+uy*uy);

}

// Do this for each device

for (i = 0; i < deviceCount; i++)

{

read = 0;

data[i].width = width;

data[i].height = height;

saveData[i].u = (float *)malloc(width*height*sizeof(float));

saveData[i].solid = (bool *)malloc(width*height*sizeof(bool));

saveData[i].name = (char *)malloc(13*sizeof(char));

saveData[i].width = width;

saveData[i].height = height;

saveData[i].length = 1;

saveData[i].commRank = commRank;

// Initialize with no solids (0 state)

for (j=0; j<width*height; j++)

{

solid[j] = 0;

solid_bound[j] = 0;

}

// Open the input file and set at initial data point

input = fopen("input.bmp","r");

for (y=0; y<height; y++)

{

fseek(input, 0x3E + y*img_width/8 + (i+commRank*deviceCount)*width/8, SEEK_SET);

for (x=0; x < width/8; x++)

{

// Read if current point is solid and save to 'solid'

read=fscanf(input, "%c", &temp) + read;

for (k = 8; k > 0; k--)

if ( !( ( (int)temp & (1 << k-1) ) >> k-1 ) )

solid[(x + y*width/8)*8+(8-k)] = 1;

}

}

if (read != width*height/8) printf("\nerror: verify input.bmp\n\n");

// Close input file

fclose(input);

for (j=0; j < width*height; j++) saveData[i].solid[j] = solid[j];

// If all boundaries are solid, solid_bound=1;

for (y=0; y<height; y++)

for (x=1; x<width-1; x++)

{

xn = x-1;

xp = x+1;

// If y is the most top node, the top neighbor will be the most bottom node

yn = (y > 0 ) ? (y-1) : (height-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

yp = (y < (height-1)) ? (y+1) : (0 );

if (solid[x+y*width] && solid[xp+y*width] && solid[x+yp*width] && solid[xn+y*width] &&

solid[x+yn*width] &&

solid[xp+yp*width] && solid[xn+yp*width] && solid[xn+yn*width] && solid[xp+yn*width])

solid_bound[x+y*width] = 1;

}

for (y=0; y<height; y++)

for (x=0; x<width; x++)

{

Page 69: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

64

xn = (x > 0 ) ? (x-1) : (width-1);

xp = (x < (width-1) ) ? (x+1) : (0 );

// If y is the most top node, the top neighbor will be the most bottom node

yn = (y > 0 ) ? (y-1) : (height-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

yp = (y < (height-1)) ? (y+1) : (0 );

if (solid_bound[x+y*width] && solid_bound[xp+y*width] && solid_bound[x+yp*width] &&

solid_bound[xn+y*width] && solid_bound[x+yn*width] &&

solid_bound[xp+yp*width] && solid_bound[xn+yp*width] && solid_bound[xn+yn*width] &&

solid_bound[xp+yn*width])

solid_bound[x+y*width] = 1;

}

// Set current device

CUDA_SAFE_CALL( cudaSetDevice( i ) );

// Allocate all memory from data structure to current device

CUDA_SAFE_CALL( cudaMalloc( &data[i].u, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].solid, sizeof(bool)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].solid_bound, sizeof(bool)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f0, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f1, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f2, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f3, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f4, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f5, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f6, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f7, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f8, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f0_new, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f1_new, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f2_new, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f3_new, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f4_new, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f5_new, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f6_new, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f7_new, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f8_new, sizeof(float)*width*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f1, sizeof(float)*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f5, sizeof(float)*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f8, sizeof(float)*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f3, sizeof(float)*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f6, sizeof(float)*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f7, sizeof(float)*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f1, sizeof(float)*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f5, sizeof(float)*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f8, sizeof(float)*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f3, sizeof(float)*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f6, sizeof(float)*height ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f7, sizeof(float)*height ) );

// Create the stream for current device

CUDA_SAFE_CALL( cudaStreamCreate(&data[i].stream) );

// Copy initial data from host to the device

CUDA_SAFE_CALL( cudaMemcpy( data[i].u, u , sizeof(float)*width*height, cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].solid, solid, sizeof(bool)*width*height, cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].solid_bound, solid_bound, sizeof(bool)*width*height,

cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f0, f0, sizeof(float)*width*height, cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f1, f1, sizeof(float)*width*height, cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f2, f2, sizeof(float)*width*height, cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f3, f3, sizeof(float)*width*height, cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f4, f4, sizeof(float)*width*height, cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f5, f5, sizeof(float)*width*height, cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f6, f6, sizeof(float)*width*height, cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f7, f7, sizeof(float)*width*height, cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f8, f8, sizeof(float)*width*height, cudaMemcpyDefault ) );

}

// Deallocate host memories

free( u );

free( solid );

free( solid_bound );

free( f0 );

free( f1 );

free( f2 );

free( f3 );

free( f4 );

free( f5 );

free( f6 );

free( f7 );

Page 70: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

65

free( f8 );

}

extern "C"

void initialize_3d( DataStruct3d *data, SaveStruct *saveData, int deviceCount, float ux, float uy,

float uz, float rho, float w0, float w1, float w7, int width, int height, int length,

int img_width, int commRank)

{

// Create variables on host side

float *f0, *f1, *f2, *f3, *f4, *f5, *f6, *f7, *f8, *f9;

float *f10, *f11, *f12, *f13, *f14, *f15, *f16, *f17, *f18;

float *u;

bool *solid, *solid_bound;

// Create auxiliary variables

char temp;

int read = 0;

// Counters

int i, j, k;

int xn, x, xp, yn, y, yp, zn, z, zp;

// Input file

FILE *input;

// Allocate memories on host side

u = (float *)malloc(width*height*length*sizeof(float));

solid = (bool *)malloc(width*height*length*sizeof(bool));

solid_bound = (bool *)malloc(width*height*length*sizeof(bool));

f0 = (float *)malloc(width*height*length*sizeof(float));

f1 = (float *)malloc(width*height*length*sizeof(float));

f2 = (float *)malloc(width*height*length*sizeof(float));

f3 = (float *)malloc(width*height*length*sizeof(float));

f4 = (float *)malloc(width*height*length*sizeof(float));

f5 = (float *)malloc(width*height*length*sizeof(float));

f6 = (float *)malloc(width*height*length*sizeof(float));

f7 = (float *)malloc(width*height*length*sizeof(float));

f8 = (float *)malloc(width*height*length*sizeof(float));

f9 = (float *)malloc(width*height*length*sizeof(float));

f10 = (float *)malloc(width*height*length*sizeof(float));

f11 = (float *)malloc(width*height*length*sizeof(float));

f12 = (float *)malloc(width*height*length*sizeof(float));

f13 = (float *)malloc(width*height*length*sizeof(float));

f14 = (float *)malloc(width*height*length*sizeof(float));

f15 = (float *)malloc(width*height*length*sizeof(float));

f16 = (float *)malloc(width*height*length*sizeof(float));

f17 = (float *)malloc(width*height*length*sizeof(float));

f18 = (float *)malloc(width*height*length*sizeof(float));

// Calculate initial density functions for initial velocity condition

for (i=0; i<width*height*length; i++)

{

f0[i] = w0*rho*( 1. - 1.5*(ux*ux+uy*uy+uz*uz) );

f1[i] = w1*rho*( 1. + 3.*(+ux ) + 4.5*(+ux )*(+ux ) - 1.5*(ux*ux+uy*uy+uz*uz) );

f2[i] = w1*rho*( 1. + 3.*(-ux ) + 4.5*(-ux )*(-ux ) - 1.5*(ux*ux+uy*uy+uz*uz) );

f3[i] = w1*rho*( 1. + 3.*( +uy ) + 4.5*( +uy )*( +uy ) - 1.5*(ux*ux+uy*uy+uz*uz) );

f4[i] = w1*rho*( 1. + 3.*( -uy ) + 4.5*( -uy )*( -uy ) - 1.5*(ux*ux+uy*uy+uz*uz) );

f5[i] = w1*rho*( 1. + 3.*( +uz) + 4.5*( +uz)*( +uz) - 1.5*(ux*ux+uy*uy+uz*uz) );

f6[i] = w1*rho*( 1. + 3.*( -uz) + 4.5*( -uz)*( -uz) - 1.5*(ux*ux+uy*uy+uz*uz) );

f7[i] = w7*rho*( 1. + 3.*(+ux+uy ) + 4.5*(+ux+uy )*(+ux+uy ) - 1.5*(ux*ux+uy*uy+uz*uz) );

f8[i] = w7*rho*( 1. + 3.*(+ux-uy ) + 4.5*(+ux-uy )*(+ux-uy ) - 1.5*(ux*ux+uy*uy+uz*uz) );

f9[i] = w7*rho*( 1. + 3.*(+ux +uz) + 4.5*(+ux +uz)*(+ux +uz) - 1.5*(ux*ux+uy*uy+uz*uz) );

f10[i]= w7*rho*( 1. + 3.*(+ux -uz) + 4.5*(+ux -uz)*(+ux -uz) - 1.5*(ux*ux+uy*uy+uz*uz) );

f11[i]= w7*rho*( 1. + 3.*(-ux+uy ) + 4.5*(-ux+uy )*(-ux+uy ) - 1.5*(ux*ux+uy*uy+uz*uz) );

f12[i]= w7*rho*( 1. + 3.*(-ux-uy ) + 4.5*(-ux-uy )*(-ux-uy ) - 1.5*(ux*ux+uy*uy+uz*uz) );

f13[i]= w7*rho*( 1. + 3.*(-ux +uz) + 4.5*(-ux +uz)*(-ux +uz) - 1.5*(ux*ux+uy*uy+uz*uz) );

f14[i]= w7*rho*( 1. + 3.*(-ux -uz) + 4.5*(-ux -uz)*(-ux -uz) - 1.5*(ux*ux+uy*uy+uz*uz) );

f15[i]= w7*rho*( 1. + 3.*( +uy+uz) + 4.5*( +uy+uz)*( +uy+uz) - 1.5*(ux*ux+uy*uy+uz*uz) );

f16[i]= w7*rho*( 1. + 3.*( +uy-uz) + 4.5*( +uy-uz)*( +uy-uz) - 1.5*(ux*ux+uy*uy+uz*uz) );

f17[i]= w7*rho*( 1. + 3.*( -uy+uz) + 4.5*( -uy+uz)*( -uy+uz) - 1.5*(ux*ux+uy*uy+uz*uz) );

f18[i]= w7*rho*( 1. + 3.*( -uy-uz) + 4.5*( -uy-uz)*( -uy-uz) - 1.5*(ux*ux+uy*uy+uz*uz) );

u[i] = sqrtf(ux*ux+uy*uy+uz*uz);

}

// Do this for each device

for (i = 0; i < deviceCount; i++)

{

read = 0;

data[i].width = width;

data[i].height = height;

data[i].length = length;

saveData[i].u = (float *)malloc(width*height*length*sizeof(float));

saveData[i].solid = (bool *)malloc(width*height*length*sizeof(bool));

saveData[i].name = (char *)malloc(13*sizeof(char));

saveData[i].width = width;

Page 71: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

66

saveData[i].height = height;

saveData[i].length = length;

saveData[i].commRank = commRank;

// Initialize with no solids (0 state)

for (j=0; j<width*height*length; j++)

{

solid[j] = 0;

solid_bound[j] = 0;

}

// Open the input file and set at initial data point

input = fopen("input.bmp","r");

for (y=0; y<height; y++)

{

fseek(input, 0x3E + y*img_width/8 + (i+commRank*deviceCount)*width/8, SEEK_SET);

for (x=0; x < width/8; x++)

{

// Read if current point is solid and save to 'solid'

read=fscanf(input, "%c", &temp) + read;

for (k = 8; k > 0; k--)

if ( !( ( (int)temp & (1 << k-1) ) >> k-1 ) )

solid[(x + y*width/8)*8+(8-k)] = 1;

}

}

if (read != width*height/8) printf("\nerror: verify input.bmp\n\n");

// Close input file

fclose(input);

//Extend length

for (z=0 ; z<length; z++) for (y=0; y<height; y++) for (x=0; x<width; x++)

solid[x + y*width + z*height*width] = solid[x + y*width];

for (j=0; j < width*height*length; j++) saveData[i].solid[j] = solid[j];

// If all boundaries are solid, solid_bound=1;

for (z=0; z < length; z++) for (y=0; y<height; y++)

for (x=1; x<width-1; x++)

{

xn = x-1;

xp = x+1;

// If y is the most top node, the top neighbor will be the most bottom node

yn = (y > 0 ) ? (y-1) : (height-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

yp = (y < (height-1)) ? (y+1) : (0 );

// If y is the most top node, the top neighbor will be the most bottom node

zn = (z > 0 ) ? (z-1) : (length-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

zp = (z < (length-1)) ? (z+1) : (0 );

if

(

solid[ x + y *width + z *width*height ] &&

solid[ xp + y *width + z *width*height ] &&

solid[ xn + y *width + z *width*height ] &&

solid[ x + yp*width + z *width*height ] &&

solid[ x + yn*width + z *width*height ] &&

solid[ x + y *width + zp*width*height ] &&

solid[ x + y *width + zn*width*height ] &&

solid[ xp + yp*width + z *width*height ] &&

solid[ xp + yn*width + z *width*height ] &&

solid[ xp + y *width + zp*width*height ] &&

solid[ xp + y *width + zn*width*height ] &&

solid[ xn + yp*width + z *width*height ] &&

solid[ xn + yn*width + z *width*height ] &&

solid[ xn + y *width + zp*width*height ] &&

solid[ xn + y *width + zn*width*height ] &&

solid[ x + yp*width + zp*width*height ] &&

solid[ x + yp*width + zn*width*height ] &&

solid[ x + yn*width + zp*width*height ] &&

solid[ x + yn*width + zn*width*height ]

)

solid_bound[x+y*width+z*width*height] = 1;

}

// Set current device

CUDA_SAFE_CALL( cudaSetDevice( i ) );

// Allocate all memory from data structure to current device

CUDA_SAFE_CALL( cudaMalloc( &data[i].u, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].solid, sizeof(bool)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].solid_bound, sizeof(bool)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f0, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f1, sizeof(float)*width*height*length ) );

Page 72: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

67

CUDA_SAFE_CALL( cudaMalloc( &data[i].f2, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f3, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f4, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f5, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f6, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f7, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f8, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f9, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f10, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f11, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f12, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f13, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f14, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f15, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f16, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f17, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].f18, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f0_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f1_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f2_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f3_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f4_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f5_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f6_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f7_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f8_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f9_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f10_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f11_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f12_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f13_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f14_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f15_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f16_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f17_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL(cudaMalloc( &data[i].f18_new, sizeof(float)*width*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f1 , sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f7 , sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f8 , sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f9 , sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f10, sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f2 , sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f11, sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f12, sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f13, sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].border_f14, sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f1 , sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f7 , sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f8 , sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f9 , sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f10, sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f2 , sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f11, sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f12, sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f13, sizeof(float)*height*length ) );

CUDA_SAFE_CALL( cudaMalloc( &data[i].nb_border_f14, sizeof(float)*height*length ) );

// Create the stream for current device

CUDA_SAFE_CALL( cudaStreamCreate(&data[i].stream) );

// Copy initial data from host to the device

CUDA_SAFE_CALL( cudaMemcpy( data[i].u, u , sizeof(float)*width*height*length, cudaMemcpyDefault )

);

CUDA_SAFE_CALL( cudaMemcpy( data[i].solid, solid, sizeof(bool)*width*height*length,

cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].solid_bound, solid_bound, sizeof(bool)*width*height*length,

cudaMemcpyDefault ) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f0, f0, sizeof(float)*width*height*length, cudaMemcpyDefault )

);

CUDA_SAFE_CALL( cudaMemcpy( data[i].f1, f1, sizeof(float)*width*height*length, cudaMemcpyDefault )

);

CUDA_SAFE_CALL( cudaMemcpy( data[i].f2, f2, sizeof(float)*width*height*length, cudaMemcpyDefault )

);

CUDA_SAFE_CALL( cudaMemcpy( data[i].f3, f3, sizeof(float)*width*height*length, cudaMemcpyDefault )

);

CUDA_SAFE_CALL( cudaMemcpy( data[i].f4, f4, sizeof(float)*width*height*length, cudaMemcpyDefault )

);

CUDA_SAFE_CALL( cudaMemcpy( data[i].f5, f5, sizeof(float)*width*height*length, cudaMemcpyDefault )

);

CUDA_SAFE_CALL( cudaMemcpy( data[i].f6, f6, sizeof(float)*width*height*length, cudaMemcpyDefault )

);

CUDA_SAFE_CALL( cudaMemcpy( data[i].f7, f7, sizeof(float)*width*height*length, cudaMemcpyDefault )

);

Page 73: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

68

CUDA_SAFE_CALL( cudaMemcpy( data[i].f8, f8, sizeof(float)*width*height*length, cudaMemcpyDefault )

);

CUDA_SAFE_CALL( cudaMemcpy( data[i].f9, f9, sizeof(float)*width*height*length, cudaMemcpyDefault )

);

CUDA_SAFE_CALL( cudaMemcpy( data[i].f10, f10, sizeof(float)*width*height*length, cudaMemcpyDefault

) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f11, f11, sizeof(float)*width*height*length, cudaMemcpyDefault

) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f12, f12, sizeof(float)*width*height*length, cudaMemcpyDefault

) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f13, f13, sizeof(float)*width*height*length, cudaMemcpyDefault

) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f14, f14, sizeof(float)*width*height*length, cudaMemcpyDefault

) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f15, f15, sizeof(float)*width*height*length, cudaMemcpyDefault

) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f16, f16, sizeof(float)*width*height*length, cudaMemcpyDefault

) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f17, f17, sizeof(float)*width*height*length, cudaMemcpyDefault

) );

CUDA_SAFE_CALL( cudaMemcpy( data[i].f18, f18, sizeof(float)*width*height*length, cudaMemcpyDefault

) );

}

// Deallocate host memories

free( u );

free( solid );

free( solid_bound );

free( f0 );

free( f1 );

free( f2 );

free( f3 );

free( f4 );

free( f5 );

free( f6 );

free( f7 );

free( f8 );

free( f9 );

free( f10 );

free( f11 );

free( f12 );

free( f13 );

free( f14 );

free( f15 );

free( f16 );

free( f17 );

free( f18 );

}

extern "C"

void initialize_cpu( DataStructCpu *data, SaveStruct *saveData, float ux, float uy,

float rho, float w0, float w1, float w5, int width, int height,

int img_width, int commRank)

{

// Create auxiliary variables

char temp;

int read = 0;

// Counters

int i, j, k;

int xn, x, xp, yn, y, yp;

// Input file

FILE *input;

// Allocate memories

data->u = (float *)malloc(width*height*sizeof(float));

data->solid = (bool *)malloc(width*height*sizeof(bool));

data->solid_bound = (bool *)malloc(width*height*sizeof(bool));

data->f0 = (float *)malloc(width*height*sizeof(float));

data->f1 = (float *)malloc(width*height*sizeof(float));

data->f2 = (float *)malloc(width*height*sizeof(float));

data->f3 = (float *)malloc(width*height*sizeof(float));

data->f4 = (float *)malloc(width*height*sizeof(float));

data->f5 = (float *)malloc(width*height*sizeof(float));

data->f6 = (float *)malloc(width*height*sizeof(float));

data->f7 = (float *)malloc(width*height*sizeof(float));

data->f8 = (float *)malloc(width*height*sizeof(float));

data->f0_new = (float *)malloc(width*height*sizeof(float));

data->f1_new = (float *)malloc(width*height*sizeof(float));

data->f2_new = (float *)malloc(width*height*sizeof(float));

data->f3_new = (float *)malloc(width*height*sizeof(float));

data->f4_new = (float *)malloc(width*height*sizeof(float));

data->f5_new = (float *)malloc(width*height*sizeof(float));

data->f6_new = (float *)malloc(width*height*sizeof(float));

data->f7_new = (float *)malloc(width*height*sizeof(float));

Page 74: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

69

data->f8_new = (float *)malloc(width*height*sizeof(float));

data->border_f1 = (float *)malloc(height*sizeof(float));

data->border_f3 = (float *)malloc(height*sizeof(float));

data->border_f5 = (float *)malloc(height*sizeof(float));

data->border_f6 = (float *)malloc(height*sizeof(float));

data->border_f7 = (float *)malloc(height*sizeof(float));

data->border_f8 = (float *)malloc(height*sizeof(float));

saveData->u = (float *)malloc(width*height*sizeof(float));

saveData->solid = (bool *)malloc(width*height*sizeof(bool));

saveData->name = (char *)malloc(13*sizeof(char));

saveData->width = width;

saveData->height = height;

saveData->length = 1;

saveData->commRank = commRank;

// Calculate initial density functions for initial velocity condition

for (i=0; i<width*height; i++)

{

data->f0[i] = w0*rho*( 1. - 1.5*(ux*ux+uy*uy) );

data->f1[i] = w1*rho*( 1. + 3.*(+ux ) + 4.5*(+ux )*(+ux ) - 1.5*(ux*ux+uy*uy) );

data->f2[i] = w1*rho*( 1. + 3.*( +uy) + 4.5*( +uy)*( +uy) - 1.5*(ux*ux+uy*uy) );

data->f3[i] = w1*rho*( 1. + 3.*(-ux ) + 4.5*(-ux )*(-ux ) - 1.5*(ux*ux+uy*uy) );

data->f4[i] = w1*rho*( 1. + 3.*( -uy) + 4.5*( -uy)*( -uy) - 1.5*(ux*ux+uy*uy) );

data->f5[i] = w5*rho*( 1. + 3.*( ux+uy) + 4.5*( ux+uy)*( ux+uy) - 1.5*(ux*ux+uy*uy) );

data->f6[i] = w5*rho*( 1. + 3.*(-ux+uy) + 4.5*(-ux+uy)*(-ux+uy) - 1.5*(ux*ux+uy*uy) );

data->f7[i] = w5*rho*( 1. + 3.*(-ux-uy) + 4.5*(-ux-uy)*(-ux-uy) - 1.5*(ux*ux+uy*uy) );

data->f8[i] = w5*rho*( 1. + 3.*( ux-uy) + 4.5*( ux-uy)*( ux-uy) - 1.5*(ux*ux+uy*uy) );

data->u[i] = sqrtf(ux*ux+uy*uy);

}

// Initialize with no solids (0 state)

for (j=0; j<width*height; j++)

{

data->solid[j] = 0;

data->solid_bound[j] = 0;

}

// Open the input file

input = fopen("input.bmp","r");

for (y=0; y<height; y++)

{

fseek(input, 0x3E + y*img_width/8 + commRank*width/8, SEEK_SET);

for (x=0; x < width/8; x++)

{

// Read if current point is solid and save to 'solid'

read=fscanf(input, "%c", &temp) + read;

for (k = 8; k > 0; k--)

if ( !( ( (int)temp & (1 << k-1) ) >> k-1 ) )

data->solid[(x + y*width/8)*8+(8-k)] = 1;

}

}

if (read != width*height/8) printf("\nerror: verify input.bmp\n\n");

// Close input file

fclose(input);

for (j=0; j < width*height; j++) saveData->solid[j] = data->solid[j];

// If all boundaries are solid, solid_bound=1;

for (y=0; y<height; y++) for (x=1; x<width-1; x++)

{

xn = x-1;

xp = x+1;

// If y is the most top node, the top neighbor will be the most bottom node

yn = (y > 0 ) ? (y-1) : (height-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

yp = (y < (height-1)) ? (y+1) : (0 );

if (data->solid[x+y*width] && data->solid[xp+y*width] && data->solid[x+yp*width] && data-

>solid[xn+y*width] && data->solid[x+yn*width] &&

data->solid[xp+yp*width] && data->solid[xn+yp*width] && data->solid[xn+yn*width] && data-

>solid[xp+yn*width])

data->solid_bound[x+y*width] = 1;

}

data->width = width;

data->height = height;

}

extern "C"

void initialize_3d_cpu( DataStruct3dCpu *data, SaveStruct *saveData, float ux, float uy,

float uz, float rho, float w0, float w1, float w7, int width, int height,

int length, int img_width, int commRank)

{

Page 75: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

70

// Create auxiliary variables

char temp;

int read = 0;

// Counters

int i, j, k;

int xn, x, xp, yn, y, yp, zn, z, zp;

// Input file

FILE *input;

data->width = width;

data->height = height;

data->length = length;

// Allocate memories

data->u = (float *)malloc(width*height*length*sizeof(float));

data->solid = (bool *)malloc(width*height*length*sizeof(bool));

data->solid_bound = (bool *)malloc(width*height*length*sizeof(bool));

data->f0 = (float *)malloc(width*height*length*sizeof(float));

data->f1 = (float *)malloc(width*height*length*sizeof(float));

data->f2 = (float *)malloc(width*height*length*sizeof(float));

data->f3 = (float *)malloc(width*height*length*sizeof(float));

data->f4 = (float *)malloc(width*height*length*sizeof(float));

data->f5 = (float *)malloc(width*height*length*sizeof(float));

data->f6 = (float *)malloc(width*height*length*sizeof(float));

data->f7 = (float *)malloc(width*height*length*sizeof(float));

data->f8 = (float *)malloc(width*height*length*sizeof(float));

data->f9 = (float *)malloc(width*height*length*sizeof(float));

data->f10 = (float *)malloc(width*height*length*sizeof(float));

data->f11 = (float *)malloc(width*height*length*sizeof(float));

data->f12 = (float *)malloc(width*height*length*sizeof(float));

data->f13 = (float *)malloc(width*height*length*sizeof(float));

data->f14 = (float *)malloc(width*height*length*sizeof(float));

data->f15 = (float *)malloc(width*height*length*sizeof(float));

data->f16 = (float *)malloc(width*height*length*sizeof(float));

data->f17 = (float *)malloc(width*height*length*sizeof(float));

data->f18 = (float *)malloc(width*height*length*sizeof(float));

data->f0_new = (float *)malloc(width*height*length*sizeof(float));

data->f1_new = (float *)malloc(width*height*length*sizeof(float));

data->f2_new = (float *)malloc(width*height*length*sizeof(float));

data->f3_new = (float *)malloc(width*height*length*sizeof(float));

data->f4_new = (float *)malloc(width*height*length*sizeof(float));

data->f5_new = (float *)malloc(width*height*length*sizeof(float));

data->f6_new = (float *)malloc(width*height*length*sizeof(float));

data->f7_new = (float *)malloc(width*height*length*sizeof(float));

data->f8_new = (float *)malloc(width*height*length*sizeof(float));

data->f9_new = (float *)malloc(width*height*length*sizeof(float));

data->f10_new = (float *)malloc(width*height*length*sizeof(float));

data->f11_new = (float *)malloc(width*height*length*sizeof(float));

data->f12_new = (float *)malloc(width*height*length*sizeof(float));

data->f13_new = (float *)malloc(width*height*length*sizeof(float));

data->f14_new = (float *)malloc(width*height*length*sizeof(float));

data->f15_new = (float *)malloc(width*height*length*sizeof(float));

data->f16_new = (float *)malloc(width*height*length*sizeof(float));

data->f17_new = (float *)malloc(width*height*length*sizeof(float));

data->f18_new = (float *)malloc(width*height*length*sizeof(float));

data->border_f1 = (float *)malloc(height*length*sizeof(float));

data->border_f7 = (float *)malloc(height*length*sizeof(float));

data->border_f8 = (float *)malloc(height*length*sizeof(float));

data->border_f9 = (float *)malloc(height*length*sizeof(float));

data->border_f10 = (float *)malloc(height*length*sizeof(float));

data->border_f2 = (float *)malloc(height*length*sizeof(float));

data->border_f11 = (float *)malloc(height*length*sizeof(float));

data->border_f12 = (float *)malloc(height*length*sizeof(float));

data->border_f13 = (float *)malloc(height*length*sizeof(float));

data->border_f14 = (float *)malloc(height*length*sizeof(float));

saveData->u = (float *)malloc(width*height*length*sizeof(float));

saveData->solid = (bool *)malloc(width*height*length*sizeof(bool));

saveData->name = (char *)malloc(13*sizeof(char));

saveData->width = width;

saveData->height = height;

saveData->length = length;

saveData->commRank = commRank;

for (i=0; i<width*height*length; i++)

{

data->f0[i] = w0*rho*( 1. - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f1[i] = w1*rho*( 1. + 3.*(+ux ) + 4.5*(+ux )*(+ux ) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f2[i] = w1*rho*( 1. + 3.*(-ux ) + 4.5*(-ux )*(-ux ) - 1.5*(ux*ux+uy*uy+uz*uz)

);

Page 76: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

71

data->f3[i] = w1*rho*( 1. + 3.*( +uy ) + 4.5*( +uy )*( +uy ) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f4[i] = w1*rho*( 1. + 3.*( -uy ) + 4.5*( -uy )*( -uy ) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f5[i] = w1*rho*( 1. + 3.*( +uz) + 4.5*( +uz)*( +uz) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f6[i] = w1*rho*( 1. + 3.*( -uz) + 4.5*( -uz)*( -uz) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f7[i] = w7*rho*( 1. + 3.*(+ux+uy ) + 4.5*(+ux+uy )*(+ux+uy ) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f8[i] = w7*rho*( 1. + 3.*(+ux-uy ) + 4.5*(+ux-uy )*(+ux-uy ) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f9[i] = w7*rho*( 1. + 3.*(+ux +uz) + 4.5*(+ux +uz)*(+ux +uz) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f10[i]= w7*rho*( 1. + 3.*(+ux -uz) + 4.5*(+ux -uz)*(+ux -uz) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f11[i]= w7*rho*( 1. + 3.*(-ux+uy ) + 4.5*(-ux+uy )*(-ux+uy ) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f12[i]= w7*rho*( 1. + 3.*(-ux-uy ) + 4.5*(-ux-uy )*(-ux-uy ) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f13[i]= w7*rho*( 1. + 3.*(-ux +uz) + 4.5*(-ux +uz)*(-ux +uz) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f14[i]= w7*rho*( 1. + 3.*(-ux -uz) + 4.5*(-ux -uz)*(-ux -uz) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f15[i]= w7*rho*( 1. + 3.*( +uy+uz) + 4.5*( +uy+uz)*( +uy+uz) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f16[i]= w7*rho*( 1. + 3.*( +uy-uz) + 4.5*( +uy-uz)*( +uy-uz) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f17[i]= w7*rho*( 1. + 3.*( -uy+uz) + 4.5*( -uy+uz)*( -uy+uz) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->f18[i]= w7*rho*( 1. + 3.*( -uy-uz) + 4.5*( -uy-uz)*( -uy-uz) - 1.5*(ux*ux+uy*uy+uz*uz)

);

data->u[i] = sqrtf(ux*ux+uy*uy+uz*uz);

}

// Initialize with no solids (0 state)

for (j=0; j<width*height*length; j++)

{

data->solid[j] = 0;

data->solid_bound[j] = 0;

}

// Open the input file

input = fopen("input.bmp","r");

for (y=0; y<height; y++)

{

fseek(input, 0x3E + y*img_width/8 + commRank*width/8, SEEK_SET);

for (x=0; x < width/8; x++)

{

// Read if current point is solid and save to 'solid'

read=fscanf(input, "%c", &temp) + read;

for (k = 8; k > 0; k--)

if ( !( ( (int)temp & (1 << k-1) ) >> k-1 ) )

data->solid[(x + y*width/8)*8+(8-k)] = 1;

}

}

if (read != width*height/8) printf("\nerror: verify input.bmp\n\n");

// Close input file

fclose(input);

//Extend length

for (z=0 ; z<length; z++) for (y=0; y<height; y++) for (x=0; x<width; x++)

data->solid[x + y*width + z*height*width] = data->solid[x + y*width];

for (j=0; j < width*height*length; j++) saveData->solid[j] = data->solid[j];

// If all boundaries are solid, solid_bound=1;

for (z=0; z < length; z++) for (y=0; y<height; y++) for (x=1; x<width-1; x++)

{

xn = x-1;

xp = x+1;

// If y is the most top node, the top neighbor will be the most bottom node

yn = (y > 0 ) ? (y-1) : (height-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

yp = (y < (height-1)) ? (y+1) : (0 );

// If y is the most top node, the top neighbor will be the most bottom node

zn = (z > 0 ) ? (z-1) : (length-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

zp = (z < (length-1)) ? (z+1) : (0 );

if

(

data->solid[ x + y *width + z *width*height ] &&

data->solid[ xp + y *width + z *width*height ] &&

Page 77: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

72

data->solid[ xn + y *width + z *width*height ] &&

data->solid[ x + yp*width + z *width*height ] &&

data->solid[ x + yn*width + z *width*height ] &&

data->solid[ x + y *width + zp*width*height ] &&

data->solid[ x + y *width + zn*width*height ] &&

data->solid[ xp + yp*width + z *width*height ] &&

data->solid[ xp + yn*width + z *width*height ] &&

data->solid[ xp + y *width + zp*width*height ] &&

data->solid[ xp + y *width + zn*width*height ] &&

data->solid[ xn + yp*width + z *width*height ] &&

data->solid[ xn + yn*width + z *width*height ] &&

data->solid[ xn + y *width + zp*width*height ] &&

data->solid[ xn + y *width + zn*width*height ] &&

data->solid[ x + yp*width + zp*width*height ] &&

data->solid[ x + yp*width + zn*width*height ] &&

data->solid[ x + yn*width + zp*width*height ] &&

data->solid[ x + yn*width + zn*width*height ]

)

data->solid_bound[x+y*width+z*width*height] = 1;

}

}

Page 78: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

73

streaming.h:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 28.11.2012

* UNICAMP - Universidade Estadual de Campinas

*

* This header file defines the streaming function. This functions passes the actual density

* functions to the nearest lattice node in 'fx_new' according to its direction.

**************************************************************************************************/

#ifndef STREAMING_H

#define STREAMING_H

extern "C"

void streaming( DataStruct *data, dim3 grid, dim3 block );

extern "C"

void streaming_3d( DataStruct3d *data, dim3 grid, dim3 block );

extern "C"

void streaming_cpu( DataStructCpu *data );

extern "C"

void streaming_3d_cpu( DataStruct3dCpu *data );

#endif

streaming.cu:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* Streaming C and CUDA kernel functions

**************************************************************************************************/

#include <cutil.h>

#include "latibol.h"

__global__ void streaming_kernel ( DataStruct data )

{

// Create variables that defines neighbors and current coordinates

int xn, x, xp, yn, y, yp;

int offset[9];

int dim;

x = threadIdx.x + blockIdx.x * blockDim.x;

y = threadIdx.y + blockIdx.y * blockDim.y;

dim = blockDim.x * gridDim.x;

offset[0] = (x ) + (y ) * dim;

if ( !data.solid_bound[offset[0]] )

{

// Apply periodic boundary condition

// If x is the most left node, the left neighbor will be the most right node

xn = (x > 0 ) ? (x-1) : (data.width-1 );

// If x is the most right node, the right neighbor will be the most left node

xp = (x < (data.width-1) ) ? (x+1) : (0 );

// If y is the most top node, the top neighbor will be the most bottom node

yn = (y > 0 ) ? (y-1) : (data.height-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

yp = (y < (data.height-1)) ? (y+1) : (0 );

// Calculate offset for fx_new

offset[1] = (xp) + (y ) * dim;

offset[2] = (x ) + (yp) * dim;

offset[3] = (xn) + (y ) * dim;

offset[4] = (x ) + (yn) * dim;

offset[5] = (xp) + (yp) * dim;

offset[6] = (xn) + (yp) * dim;

offset[7] = (xn) + (yn) * dim;

offset[8] = (xp) + (yn) * dim;

Page 79: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

74

// Apply streaming process moving current densities functions to next position

data.f0_new[offset[0]] = data.f0[offset[0]];

data.f1_new[offset[1]] = data.f1[offset[0]];

data.f2_new[offset[2]] = data.f2[offset[0]];

data.f3_new[offset[3]] = data.f3[offset[0]];

data.f4_new[offset[4]] = data.f4[offset[0]];

data.f5_new[offset[5]] = data.f5[offset[0]];

data.f6_new[offset[6]] = data.f6[offset[0]];

data.f7_new[offset[7]] = data.f7[offset[0]];

data.f8_new[offset[8]] = data.f8[offset[0]];

}

}

// C streaming function that calls CUDA kernel

extern "C"

void streaming( DataStruct *data, dim3 grid, dim3 block )

{

streaming_kernel<<<grid, block, 0, data->stream>>> ( *data );

CUT_CHECK_ERROR("streaming failed");

}

__global__ void streaming_3d_kernel ( DataStruct3d data )

{

// Create variables that defines neighbors and current coordinates

int xn, x, xp, yn, y, yp, zn, z, zp;

int offset[19];

x = threadIdx.x + blockIdx.x * blockDim.x;

y = threadIdx.y + blockIdx.y * blockDim.y;

z = threadIdx.z + blockIdx.z * blockDim.z;

int x_dim = blockDim.x * gridDim.x;

int xy_dim = x_dim*blockDim.y * gridDim.y;

offset[0] = (x ) + (y )*x_dim + (z )*xy_dim;

if (!data.solid_bound[offset[0]] )

{

// Apply periodic boundary condition

// If x is the most left node, the left neighbor will be the most right node

xn = (x > 0 ) ? (x-1) : (data.width-1 );

// If x is the most right node, the right neighbor will be the most left node

xp = (x < (data.width-1) ) ? (x+1) : (0 );

// If y is the most top node, the top neighbor will be the most bottom node

yn = (y > 0 ) ? (y-1) : (data.height-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

yp = (y < (data.height-1)) ? (y+1) : (0 );

// If y is the most top node, the top neighbor will be the most bottom node

zn = (z > 0 ) ? (z-1) : (data.length-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

zp = (z < (data.length-1)) ? (z+1) : (0 );

// Calculate offset for fx_new

offset[1] = (xp) + (y )*x_dim + (z )*xy_dim;

offset[2] = (xn) + (y )*x_dim + (z )*xy_dim;

offset[3] = (x ) + (yp)*x_dim + (z )*xy_dim;

offset[4] = (x ) + (yn)*x_dim + (z )*xy_dim;

offset[5] = (x ) + (y )*x_dim + (zp)*xy_dim;

offset[6] = (x ) + (y )*x_dim + (zn)*xy_dim;

offset[7] = (xp) + (yp)*x_dim + (z )*xy_dim;

offset[8] = (xp) + (yn)*x_dim + (z )*xy_dim;

offset[9] = (xp) + (y )*x_dim + (zp)*xy_dim;

offset[10]= (xp) + (y )*x_dim + (zn)*xy_dim;

offset[11]= (xn) + (yp)*x_dim + (z )*xy_dim;

offset[12]= (xn) + (yn)*x_dim + (z )*xy_dim;

offset[13]= (xn) + (y )*x_dim + (zp)*xy_dim;

offset[14]= (xn) + (y )*x_dim + (zn)*xy_dim;

offset[15]= (x ) + (yp)*x_dim + (zp)*xy_dim;

offset[16]= (x ) + (yp)*x_dim + (zn)*xy_dim;

offset[17]= (x ) + (yn)*x_dim + (zp)*xy_dim;

offset[18]= (x ) + (yn)*x_dim + (zn)*xy_dim;

// Apply streaming process moving current densities functions to next position

data.f0_new [offset[0]] = data.f0 [offset[0]];

data.f1_new [offset[1]] = data.f1 [offset[0]];

data.f2_new [offset[2]] = data.f2 [offset[0]];

data.f3_new [offset[3]] = data.f3 [offset[0]];

data.f4_new [offset[4]] = data.f4 [offset[0]];

data.f5_new [offset[5]] = data.f5 [offset[0]];

data.f6_new [offset[6]] = data.f6 [offset[0]];

data.f7_new [offset[7]] = data.f7 [offset[0]];

data.f8_new [offset[8]] = data.f8 [offset[0]];

data.f9_new [offset[9]] = data.f9 [offset[0]];

data.f10_new[offset[10]] = data.f10[offset[0]];

data.f11_new[offset[11]] = data.f11[offset[0]];

data.f12_new[offset[12]] = data.f12[offset[0]];

Page 80: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

75

data.f13_new[offset[13]] = data.f13[offset[0]];

data.f14_new[offset[14]] = data.f14[offset[0]];

data.f15_new[offset[15]] = data.f15[offset[0]];

data.f16_new[offset[16]] = data.f16[offset[0]];

data.f17_new[offset[17]] = data.f17[offset[0]];

data.f18_new[offset[18]] = data.f18[offset[0]];

}

}

extern "C"

void streaming_3d( DataStruct3d *data, dim3 grid, dim3 block )

{

streaming_3d_kernel<<<grid, block, 0, data->stream>>> ( *data );

CUT_CHECK_ERROR("streaming failed");

}

extern "C"

void streaming_cpu( DataStructCpu *data )

{

// Create variables that defines neighbors and current coordinates

int xn, x, xp, yn, y, yp;

int offset[9];

int width = data->width;

for (y = 0; y < data->height; y++) for (x = 0; x < width; x++)

{

offset[0] = (x ) + (y ) * width;

if ( !data->solid_bound[offset[0]] )

{

// Apply periodic boundary condition

// If x is the most left node, the left neighbor will be the most right node

xn = (x > 0 ) ? (x-1) : (width-1 );

// If x is the most right node, the right neighbor will be the most left node

xp = (x < (width-1) ) ? (x+1) : (0 );

// If y is the most top node, the top neighbor will be the most bottom node

yn = (y > 0 ) ? (y-1) : (data->height-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

yp = (y < (data->height-1)) ? (y+1) : (0 );

// Calculate offset for fx_new

offset[1] = (xp) + (y ) * width;

offset[2] = (x ) + (yp) * width;

offset[3] = (xn) + (y ) * width;

offset[4] = (x ) + (yn) * width;

offset[5] = (xp) + (yp) * width;

offset[6] = (xn) + (yp) * width;

offset[7] = (xn) + (yn) * width;

offset[8] = (xp) + (yn) * width;

// Apply streaming process moving current densities functions to next position

data->f0_new[offset[0]] = data->f0[offset[0]];

data->f1_new[offset[1]] = data->f1[offset[0]];

data->f2_new[offset[2]] = data->f2[offset[0]];

data->f3_new[offset[3]] = data->f3[offset[0]];

data->f4_new[offset[4]] = data->f4[offset[0]];

data->f5_new[offset[5]] = data->f5[offset[0]];

data->f6_new[offset[6]] = data->f6[offset[0]];

data->f7_new[offset[7]] = data->f7[offset[0]];

data->f8_new[offset[8]] = data->f8[offset[0]];

}

}

}

extern "C"

void streaming_3d_cpu( DataStruct3dCpu *data )

{

// Create variables that defines neighbors and current coordinates

int xn, x, xp, yn, y, yp, zn, z, zp;

int offset[19];

int x_dim = data->width;

int xy_dim = x_dim*data->height;

for (z=0; z < data->length; z++) for (y = 0; y < data->height; y++) for (x = 0; x < data->width; x++)

{

offset[0] = (x ) + (y )*x_dim + (z )*xy_dim;

if ( !data->solid_bound[offset[0]] )

{

// Apply periodic boundary condition

// If x is the most left node, the left neighbor will be the most right node

xn = (x > 0 ) ? (x-1) : (data->width-1 );

// If x is the most right node, the right neighbor will be the most left node

xp = (x < (data->width-1) ) ? (x+1) : (0 );

// If y is the most top node, the top neighbor will be the most bottom node

yn = (y > 0 ) ? (y-1) : (data->height-1);

Page 81: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

76

// If y is the most bottom node, the bottom neighbor will be the most top node

yp = (y < (data->height-1)) ? (y+1) : (0 );

// If y is the most top node, the top neighbor will be the most bottom node

zn = (z > 0 ) ? (z-1) : (data->length-1);

// If y is the most bottom node, the bottom neighbor will be the most top node

zp = (z < (data->length-1)) ? (z+1) : (0 );

// Calculate offset for fx_new

offset[1] = (xp) + (y )*x_dim + (z )*xy_dim;

offset[2] = (xn) + (y )*x_dim + (z )*xy_dim;

offset[3] = (x ) + (yp)*x_dim + (z )*xy_dim;

offset[4] = (x ) + (yn)*x_dim + (z )*xy_dim;

offset[5] = (x ) + (y )*x_dim + (zp)*xy_dim;

offset[6] = (x ) + (y )*x_dim + (zn)*xy_dim;

offset[7] = (xp) + (yp)*x_dim + (z )*xy_dim;

offset[8] = (xp) + (yn)*x_dim + (z )*xy_dim;

offset[9] = (xp) + (y )*x_dim + (zp)*xy_dim;

offset[10]= (xp) + (y )*x_dim + (zn)*xy_dim;

offset[11]= (xn) + (yp)*x_dim + (z )*xy_dim;

offset[12]= (xn) + (yn)*x_dim + (z )*xy_dim;

offset[13]= (xn) + (y )*x_dim + (zp)*xy_dim;

offset[14]= (xn) + (y )*x_dim + (zn)*xy_dim;

offset[15]= (x ) + (yp)*x_dim + (zp)*xy_dim;

offset[16]= (x ) + (yp)*x_dim + (zn)*xy_dim;

offset[17]= (x ) + (yn)*x_dim + (zp)*xy_dim;

offset[18]= (x ) + (yn)*x_dim + (zn)*xy_dim;

// Apply streaming process moving current densities functions to next position

data->f0_new [offset[0]] = data->f0 [offset[0]];

data->f1_new [offset[1]] = data->f1 [offset[0]];

data->f2_new [offset[2]] = data->f2 [offset[0]];

data->f3_new [offset[3]] = data->f3 [offset[0]];

data->f4_new [offset[4]] = data->f4 [offset[0]];

data->f5_new [offset[5]] = data->f5 [offset[0]];

data->f6_new [offset[6]] = data->f6 [offset[0]];

data->f7_new [offset[7]] = data->f7 [offset[0]];

data->f8_new [offset[8]] = data->f8 [offset[0]];

data->f9_new [offset[9]] = data->f9 [offset[0]];

data->f10_new[offset[10]] = data->f10[offset[0]];

data->f11_new[offset[11]] = data->f11[offset[0]];

data->f12_new[offset[12]] = data->f12[offset[0]];

data->f13_new[offset[13]] = data->f13[offset[0]];

data->f14_new[offset[14]] = data->f14[offset[0]];

data->f15_new[offset[15]] = data->f15[offset[0]];

data->f16_new[offset[16]] = data->f16[offset[0]];

data->f17_new[offset[17]] = data->f17[offset[0]];

data->f18_new[offset[18]] = data->f18[offset[0]];

}

}

}

Page 82: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

77

get_border.h:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* This header file defines the get_border function. This functions passes the most right density

* functions f1, f5, and f8 from fx_new to border_fx and the most left density functions

* f3, f6, and f7 from fx_new to border_fx

**************************************************************************************************/

#ifndef GET_BORDER_H

#define GET_BORDER_H

extern "C"

void get_border( DataStruct *data, dim3 grid, dim3 block );

extern "C"

void get_border_3d( DataStruct3d *data, dim3 grid, dim3 block );

extern "C"

void get_border_cpu( DataStructCpu *data );

extern "C"

void get_border_3d_cpu( DataStruct3dCpu *data );

#endif

get_border.cu:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* Get_border C and CUDA kernel functions

**************************************************************************************************/

#include <cutil.h>

#include <cuda_runtime_api.h>

#include "latibol.h"

__global__ void get_border_kernel ( DataStruct data )

{

int x = threadIdx.x + blockIdx.x * blockDim.x;

// if x is the most right node copies fx_new to border_fx

if (x == (data.width-1))

{

int y = threadIdx.y + blockIdx.y * blockDim.y;

int offset = x + y * blockDim.x * gridDim.x;

data.border_f1[y] = data.f1_new[offset];

data.border_f5[y] = data.f5_new[offset];

data.border_f8[y] = data.f8_new[offset];

}

// if x is the most left node copies fx_new to border_fx

if (x == 0)

{

int y = threadIdx.y + blockIdx.y * blockDim.y;

int offset = x + y * blockDim.x * gridDim.x;

data.border_f3[y] = data.f3_new[offset];

data.border_f6[y] = data.f6_new[offset];

data.border_f7[y] = data.f7_new[offset];

}

}

// C get_border function that calls CUDA kernel

extern "C"

void get_border( DataStruct *data, dim3 grid, dim3 block )

{

get_border_kernel<<<grid, block, 0, data->stream>>> ( *data );

}

__global__ void get_border_3d_kernel ( DataStruct3d data )

{

Page 83: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

78

int x = threadIdx.x + blockIdx.x * blockDim.x;

// if x is the most right node copies fx_new to border_fx

if (x == (data.width-1))

{

int y = threadIdx.y + blockIdx.y*blockDim.y;

int z = threadIdx.z + blockIdx.z*blockDim.z;

int offset = x + y * blockDim.x*gridDim.x + z * blockDim.x*gridDim.x*blockDim.y*gridDim.y ;

data.border_f1 [y+data.height*z] = data.f1_new [offset];

data.border_f7 [y+data.height*z] = data.f7_new [offset];

data.border_f8 [y+data.height*z] = data.f8_new [offset];

data.border_f9 [y+data.height*z] = data.f9_new [offset];

data.border_f10[y+data.height*z] = data.f10_new[offset];

}

// if x is the most left node copies fx_new to border_fx

if (x == 0)

{

int y = threadIdx.y + blockIdx.y*blockDim.y;

int z = threadIdx.z + blockIdx.z*blockDim.z;

int offset = x + y * blockDim.x*gridDim.x + z * blockDim.x*gridDim.x*blockDim.y*gridDim.y ;

data.border_f2 [y+data.height*z] = data.f2_new [offset];

data.border_f11[y+data.height*z] = data.f11_new[offset];

data.border_f12[y+data.height*z] = data.f12_new[offset];

data.border_f13[y+data.height*z] = data.f13_new[offset];

data.border_f14[y+data.height*z] = data.f14_new[offset];

}

}

extern "C"

void get_border_3d( DataStruct3d *data, dim3 grid, dim3 block )

{

get_border_3d_kernel<<<grid, block, 0, data->stream>>> ( *data );

}

extern "C"

void get_border_cpu( DataStructCpu *data )

{

int x, y, offset;

for (x = 0; x < data->width; x++)

{

// if x is the most right node copies fx_new to border_fx

if (x == (data->width-1)) for (y = 0; y < data->height; y++)

{

offset = x + y*data->width;

data->border_f1[y] = data->f1_new[offset];

data->border_f5[y] = data->f5_new[offset];

data->border_f8[y] = data->f8_new[offset];

}

// if x is the most left node copies fx_new to border_fx

if (x == 0) for (y = 0; y < data->height; y++)

{

offset = x + y*data->width;

data->border_f3[y] = data->f3_new[offset];

data->border_f6[y] = data->f6_new[offset];

data->border_f7[y] = data->f7_new[offset];

}

}

}

extern "C"

void get_border_3d_cpu( DataStruct3dCpu *data )

{

int x, y, z, offset, border_offset;

int width = data->width;

int height = data->height;

int length = data-> length;

for (x = 0; x < width; x++)

{

// if x is the most right node copies fx_new to border_fx

if (x == (width-1)) for (z=0; z < length; z++) for (y = 0; y < height; y++)

{

offset = x + y*width + z*width*height;

border_offset = y + z*height;

data->border_f1 [border_offset] = data->f1_new [offset];

data->border_f7 [border_offset] = data->f7_new [offset];

data->border_f8 [border_offset] = data->f8_new [offset];

data->border_f9 [border_offset] = data->f9_new [offset];

data->border_f10[border_offset] = data->f10_new[offset];

}

// if x is the most left node copies fx_new to border_fx

Page 84: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

79

if (x == 0) for (z=0; z < length; z++) for (y = 0; y < height; y++)

{

offset = x + y*width + z*width*height;

border_offset = y + z*height;

data->border_f2 [border_offset] = data->f2_new [offset];

data->border_f11[border_offset] = data->f11_new[offset];

data->border_f12[border_offset] = data->f12_new[offset];

data->border_f13[border_offset] = data->f13_new[offset];

data->border_f14[border_offset] = data->f14_new[offset];

}

}

}

Page 85: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

80

copy_border.h:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* This header file defines the copy_border function. This functions copies nb_border_fx to

* border_fx from any device to any device using UVA, so its necessary compute capability 2.0+.

* To use this function its necessary to pass the current device nb_border_fx, 'left' device

* border_fa and 'right' device border_fa.

**************************************************************************************************/

#ifndef COPY_BORDER_H

#define COPY_BORDER_H

extern "C"

void copy_border_devices( float *nb_border_f1, float *nb_border_f5, float *nb_border_f8,

float *nb_border_f3, float *nb_border_f6, float *nb_border_f7,

float *border_f1, float *border_f5, float *border_f8,

float *border_f3, float *border_f6, float *border_f7,

int height, cudaStream_t &s );

void copy_border_3d_devices( float *nb_border_f1, float *nb_border_f7, float *nb_border_f8,

float *nb_border_f9, float *nb_border_f10,

float *nb_border_f2, float *nb_border_f11, float *nb_border_f12,

float *nb_border_f13, float *nb_border_f14,

float *border_f1, float *border_f7, float *border_f8,

float *border_f9, float *border_f10,

float *border_f2, float *border_f11, float *border_f12,

float *border_f13, float *border_f14,

int height, int length, cudaStream_t &s );

void copy_border_nodes ( float *border_ctr_buf, float *border_top_buf, float *border_btm_buf,

float *nb_border_ctr, float *nb_border_top, float *nb_border_btm,

int height, int dest, int source, MPI_Status Stat, cudaStream_t &s );

void copy_border_3d_nodes ( float *border_ctr_buf, float *border_lft_buf, float *border_rgt_buf,

float *border_top_buf, float *border_btm_buf,

float *nb_border_ctr, float *nb_border_lft, float *nb_border_rgt,

float *nb_border_top, float *nb_border_btm,

int height, int length, int dest, int source, MPI_Status Stat, cudaStream_t &s

);

void copy_border_nodes_cpu ( float *border_ctr_buf, float *border_top_buf, float *border_btm_buf,

int height, int dest, int source, MPI_Status Stat );

void copy_border_3d_nodes_cpu ( float *border_ctr_buf, float *border_lft_buf, float *border_rgt_buf,

float *border_top_buf, float *border_btm_buf,

int height, int length, int dest, int source, MPI_Status Stat );

#endif

copy_border.cu:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* Copy_border function

**************************************************************************************************/

#include <cutil.h>

#include <cuda_runtime_api.h>

#include <mpi.h>

extern "C"

void copy_border_devices( float *nb_border_f1, float *nb_border_f5, float *nb_border_f8,

float *nb_border_f3, float *nb_border_f6, float *nb_border_f7,

float *border_f1, float *border_f5, float *border_f8,

float *border_f3, float *border_f6, float *border_f7,

int height, cudaStream_t &s )

{

// Copies border_fx to nb_border_fx using UVA

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f1, border_f1, sizeof(float)*height,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f5, border_f5, sizeof(float)*height,

cudaMemcpyDefault, s));

Page 86: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

81

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f8, border_f8, sizeof(float)*height,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f3, border_f3, sizeof(float)*height,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f6, border_f6, sizeof(float)*height,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f7, border_f7, sizeof(float)*height,

cudaMemcpyDefault, s));

}

void copy_border_3d_devices( float *nb_border_f1, float *nb_border_f7, float *nb_border_f8,

float *nb_border_f9, float *nb_border_f10,

float *nb_border_f2, float *nb_border_f11, float *nb_border_f12,

float *nb_border_f13, float *nb_border_f14,

float *border_f1, float *border_f7, float *border_f8,

float *border_f9, float *border_f10,

float *border_f2, float *border_f11, float *border_f12,

float *border_f13, float *border_f14,

int height, int length, cudaStream_t &s )

{

// Copies border_fx to nb_border_fx using UVA

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f1, border_f1, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f7, border_f7, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f8, border_f8, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f9, border_f9, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f10, border_f10, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f2, border_f2, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f11, border_f11, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f12, border_f12, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f13, border_f13, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_f14, border_f14, sizeof(float)*height*length,

cudaMemcpyDefault, s));

}

void copy_border_nodes ( float *border_ctr_buf, float *border_top_buf, float *border_btm_buf,

float *nb_border_ctr, float *nb_border_top, float *nb_border_btm,

int height, int dest, int source, MPI_Status Stat, cudaStream_t &s )

{

CUDA_SAFE_CALL(cudaMemcpyAsync(border_ctr_buf, nb_border_ctr, sizeof(float)*height,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(border_top_buf, nb_border_top, sizeof(float)*height,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(border_btm_buf, nb_border_btm, sizeof(float)*height,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL( cudaDeviceSynchronize() );

MPI_Sendrecv_replace(border_ctr_buf, height, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD, &Stat);

MPI_Sendrecv_replace(border_top_buf, height, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD, &Stat);

MPI_Sendrecv_replace(border_btm_buf, height, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD, &Stat);

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_ctr, border_ctr_buf, sizeof(float)*height,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_top, border_top_buf, sizeof(float)*height,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_btm, border_btm_buf, sizeof(float)*height,

cudaMemcpyDefault, s));

}

void copy_border_3d_nodes ( float *border_ctr_buf, float *border_lft_buf, float *border_rgt_buf,

float *border_top_buf, float *border_btm_buf,

float *nb_border_ctr, float *nb_border_lft, float *nb_border_rgt,

float *nb_border_top, float *nb_border_btm,

int height, int length, int dest, int source, MPI_Status Stat, cudaStream_t &s

)

{

CUDA_SAFE_CALL(cudaMemcpyAsync(border_ctr_buf, nb_border_ctr, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(border_lft_buf, nb_border_lft, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(border_rgt_buf, nb_border_rgt, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(border_top_buf, nb_border_top, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(border_btm_buf, nb_border_btm, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL( cudaDeviceSynchronize() );

Page 87: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

82

MPI_Sendrecv_replace(border_ctr_buf, height*length, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD,

&Stat);

MPI_Sendrecv_replace(border_lft_buf, height*length, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD,

&Stat);

MPI_Sendrecv_replace(border_rgt_buf, height*length, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD,

&Stat);

MPI_Sendrecv_replace(border_top_buf, height*length, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD,

&Stat);

MPI_Sendrecv_replace(border_btm_buf, height*length, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD,

&Stat);

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_ctr, border_ctr_buf, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_lft, border_lft_buf, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_rgt, border_rgt_buf, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_top, border_top_buf, sizeof(float)*height*length,

cudaMemcpyDefault, s));

CUDA_SAFE_CALL(cudaMemcpyAsync(nb_border_btm, border_btm_buf, sizeof(float)*height*length,

cudaMemcpyDefault, s));

}

void copy_border_nodes_cpu ( float *border_ctr_buf, float *border_top_buf, float *border_btm_buf,

int height, int dest, int source, MPI_Status Stat )

{

MPI_Sendrecv_replace(border_ctr_buf, height, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD, &Stat);

MPI_Sendrecv_replace(border_top_buf, height, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD, &Stat);

MPI_Sendrecv_replace(border_btm_buf, height, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD, &Stat);

}

void copy_border_3d_nodes_cpu ( float *border_ctr_buf, float *border_lft_buf, float *border_rgt_buf,

float *border_top_buf, float *border_btm_buf,

int height, int length, int dest, int source, MPI_Status Stat )

{

MPI_Sendrecv_replace(border_ctr_buf, height*length, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD,

&Stat);

MPI_Sendrecv_replace(border_lft_buf, height*length, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD,

&Stat);

MPI_Sendrecv_replace(border_rgt_buf, height*length, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD,

&Stat);

MPI_Sendrecv_replace(border_top_buf, height*length, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD,

&Stat);

MPI_Sendrecv_replace(border_btm_buf, height*length, MPI_FLOAT, dest, 1, source, 1, MPI_COMM_WORLD,

&Stat);

}

Page 88: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

83

apply_border.h:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* This header file defines the apply_border function. This functions apply neighbor borders to

* the fx_new. If the lattice node is the most right it copies f1, f5 and f8, if it is the most

* left node it copies f3, f6, f7.

**************************************************************************************************/

#ifndef APPLY_BORDER_H

#define APPLY_BORDER_H

extern "C"

void apply_border ( DataStruct *data, dim3 grid, dim3 block );

extern "C"

void apply_border_3d ( DataStruct3d *data, dim3 grid, dim3 block );

extern "C"

void apply_border_cpu ( DataStructCpu *data );

extern "C"

void apply_border_3d_cpu ( DataStruct3dCpu *data );

#endif

apply_border.cu:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* Apply_border C and CUDA kernel functions

**************************************************************************************************/

#include <cutil.h>

#include "latibol.h"

__global__ void apply_border_kernel ( DataStruct data )

{

int x = threadIdx.x + blockIdx.x * blockDim.x;

// If the lattice node is the most right it copies f1, f5 and f8 neighbor borders

if (x == 0)

{

int y = threadIdx.y + blockIdx.y * blockDim.y;

int offset = x + y * blockDim.x * gridDim.x;

data.f1_new[offset] = data.nb_border_f1[y];

data.f5_new[offset] = data.nb_border_f5[y];

data.f8_new[offset] = data.nb_border_f8[y];

}

// If the lattice node is the most left it copies f3, f6 and f7 neighbor borders

if (x == (data.width-1))

{

int y = threadIdx.y + blockIdx.y * blockDim.y;

int offset = x + y * blockDim.x * gridDim.x;

data.f3_new[offset] = data.nb_border_f3[y];

data.f6_new[offset] = data.nb_border_f6[y];

data.f7_new[offset] = data.nb_border_f7[y];

}

}

// C apply_border function that calls CUDA kernel

extern "C"

void apply_border ( DataStruct *data, dim3 grid, dim3 block )

{

apply_border_kernel<<<grid, block, 0, data->stream>>> ( *data );

CUT_CHECK_ERROR("apply border failed");

}

__global__ void apply_border_3d_kernel ( DataStruct3d data )

{

int x = threadIdx.x + blockIdx.x * blockDim.x;

Page 89: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

84

// If the lattice node is the most right it copies f1, f5 and f8 neighbor borders

if (x == 0)

{

int y = threadIdx.y + blockIdx.y*blockDim.y;

int z = threadIdx.z + blockIdx.z*blockDim.z;

int offset = x + y * blockDim.x*gridDim.x + z * blockDim.x*gridDim.x*blockDim.y*gridDim.y;

data.f1_new [offset] = data.nb_border_f1 [y+data.height*z];

data.f7_new [offset] = data.nb_border_f7 [y+data.height*z];

data.f8_new [offset] = data.nb_border_f8 [y+data.height*z];

data.f9_new [offset] = data.nb_border_f9 [y+data.height*z];

data.f10_new[offset] = data.nb_border_f10[y+data.height*z];

}

// If the lattice node is the most left it copies f3, f6 and f7 neighbor borders

if (x == (data.width-1))

{

int y = threadIdx.y + blockIdx.y*blockDim.y;

int z = threadIdx.z + blockIdx.z*blockDim.z;

int offset = x + y * blockDim.x*gridDim.x + z * blockDim.x*gridDim.x*blockDim.y*gridDim.y;

data.f2_new [offset] = data.nb_border_f2 [y+data.height*z];

data.f11_new[offset] = data.nb_border_f11[y+data.height*z];

data.f12_new[offset] = data.nb_border_f12[y+data.height*z];

data.f13_new[offset] = data.nb_border_f13[y+data.height*z];

data.f14_new[offset] = data.nb_border_f14[y+data.height*z];

}

}

extern "C"

void apply_border_3d ( DataStruct3d *data, dim3 grid, dim3 block )

{

apply_border_3d_kernel<<<grid, block, 0, data->stream>>> ( *data );

CUT_CHECK_ERROR("apply border failed");

}

extern "C"

void apply_border_cpu ( DataStructCpu *data )

{

int x, y, offset;

for (x = 0; x < data->width; x++)

{

// If the lattice node is the most right it copies f1, f5 and f8 neighbor borders

if (x == 0) for (y = 0; y < data->height; y++)

{

offset = x + y*data->width;

data->f1_new[offset] = data->border_f1[y];

data->f5_new[offset] = data->border_f5[y];

data->f8_new[offset] = data->border_f8[y];

}

// If the lattice node is the most left it copies f3, f6 and f7 neighbor borders

if (x == (data->width-1)) for (y = 0; y < data->height; y++)

{

offset = x + y*data->width;

data->f3_new[offset] = data->border_f3[y];

data->f6_new[offset] = data->border_f6[y];

data->f7_new[offset] = data->border_f7[y];

}

}

}

extern "C"

void apply_border_3d_cpu ( DataStruct3dCpu *data )

{

int x, y, z, offset, border_offset;

int width = data->width;

int height = data->height;

int length = data->length;

for (x = 0; x < width; x++)

{

// if x is the most right node copies (f1, f5 and f8) fx_new to border_fx

if (x == 0) for (z=0; z < length; z++) for (y = 0; y < height; y++)

{

offset = x + y*width + z*width*height;

border_offset = y + z*height;

data->f1_new [offset] = data->border_f1 [border_offset];

data->f7_new [offset] = data->border_f7 [border_offset];

data->f8_new [offset] = data->border_f8 [border_offset];

data->f9_new [offset] = data->border_f9 [border_offset];

data->f10_new[offset] = data->border_f10[border_offset];

}

// if x is the most left node copies (f3, f6 and f7) fx_new to border_fx

if (x == (width-1)) for (z=0; z < length; z++) for (y = 0; y < height; y++)

Page 90: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

85

{

offset = x + y*width + z*width*height;

border_offset = y + z*height;

data->f2_new [offset] = data->border_f2 [border_offset];

data->f11_new[offset] = data->border_f11[border_offset];

data->f12_new[offset] = data->border_f12[border_offset];

data->f13_new[offset] = data->border_f13[border_offset];

data->f14_new[offset] = data->border_f14[border_offset];

}

}

}

Page 91: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

86

collision.h:

/**************************************************************************************************

* Lattice Boltzmann Multi-GPU CUDA Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* This header file defines the collision function. This functions calculate the collison term

* and pass the new distribution function to fa

**************************************************************************************************/

#ifndef COLL_H

#define COLL_H

extern "C"

void collision(DataStruct *data, float w0, float w1, float w5, float tau_inv, dim3 grid, dim3 block );

extern "C"

void collision_3d( DataStruct3d *data, float w0, float w1, float w5, float tau_inv, dim3 grid, dim3 block

);

extern "C"

void collision_cpu( DataStructCpu *data, float w0, float w1, float w5, float tau_inv );

extern "C"

void collision_3d_cpu( DataStruct3dCpu *data, float w0, float w1, float w5, float tau_inv );

#endif

collision.cu:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* Collision C and CUDA kernel functions

**************************************************************************************************/

#include <cutil.h>

#include "latibol.h"

__global__ void collision_kernel ( DataStruct data, float w0, float w1, float w5, float tau_inv )

{

int x = threadIdx.x + blockIdx.x * blockDim.x;

int y = threadIdx.y + blockIdx.y * blockDim.y;

int offset = x + y * blockDim.x * gridDim.x;

if (!data.solid_bound[offset])

{

// Create registers

float rho, ux, uy, usq, uxsq, uysq;

float uxuy5, uxuy6;

//Apply Bounce-Back boundary condition

if (data.solid[offset])

{

data.f1[offset] = data.f3_new[offset];

data.f2[offset] = data.f4_new[offset];

data.f3[offset] = data.f1_new[offset];

data.f4[offset] = data.f2_new[offset];

data.f5[offset] = data.f7_new[offset];

data.f6[offset] = data.f8_new[offset];

data.f7[offset] = data.f5_new[offset];

data.f8[offset] = data.f6_new[offset];

}

else

{

// Calculate macroscopic density

rho = data.f0_new[offset] + data.f1_new[offset] + data.f2_new[offset] +

data.f3_new[offset] + data.f4_new[offset] + data.f5_new[offset] +

data.f6_new[offset] + data.f7_new[offset] + data.f8_new[offset];

// Calculate macroscopic velocity in x

ux = ( data.f1_new[offset] - data.f3_new[offset] + data.f5_new[offset] -

data.f6_new[offset] - data.f7_new[offset] + data.f8_new[offset] ) / rho;

// Calculate macroscopic velocity in y

Page 92: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

87

uy = ( data.f2_new[offset] - data.f4_new[offset] + data.f5_new[offset] +

data.f6_new[offset] - data.f7_new[offset] - data.f8_new[offset] ) / rho;

// Calculate square velocity and macroscopic velocity

uxsq = ux*ux;

uysq = uy*uy;

usq = uxsq + uysq;

data.u[offset] = sqrtf(usq);

// Pre-calculate some values

w0 = w0*rho;

w1 = w1*rho;

w5 = w5*rho;

uxuy5 = +ux+uy;

uxuy6 = -ux+uy;

// Inlet BC - very crude

// Calculate the collision term and pass the new equilibrium distribution function

data.f0[offset] = data.f0_new[offset] - tau_inv*( data.f0_new[offset] -

w0*(1.f - 1.5f*usq) );

data.f1[offset] = data.f1_new[offset] - tau_inv*( data.f1_new[offset] -

w1*(1.f + 3.f*ux + 4.5f*uxsq - 1.5f*usq) );

data.f2[offset] = data.f2_new[offset] - tau_inv*( data.f2_new[offset] -

w1*(1.f + 3.f*uy + 4.5f*uysq - 1.5f*usq) );

data.f3[offset] = data.f3_new[offset] - tau_inv*( data.f3_new[offset] -

w1*(1.f - 3.f*ux + 4.5f*uxsq - 1.5f*usq) );

data.f4[offset] = data.f4_new[offset] - tau_inv*( data.f4_new[offset] -

w1*(1.f - 3.f*uy + 4.5f*uysq - 1.5f*usq) );

data.f5[offset] = data.f5_new[offset] - tau_inv*( data.f5_new[offset] -

w5*(1.f + 3.f*uxuy5 + 4.5f*uxuy5*uxuy5 - 1.5f*usq) );

data.f6[offset] = data.f6_new[offset] - tau_inv*( data.f6_new[offset] -

w5*(1.f + 3.f*uxuy6 + 4.5f*uxuy6*uxuy6 - 1.5f*usq) );

data.f7[offset] = data.f7_new[offset] - tau_inv*( data.f7_new[offset] -

w5*(1.f - 3.f*uxuy5 + 4.5f*uxuy5*uxuy5 - 1.5f*usq) );

data.f8[offset] = data.f8_new[offset] - tau_inv*( data.f8_new[offset] -

w5*(1.f - 3.f*uxuy6 + 4.5f*uxuy6*uxuy6 - 1.5f*usq) );

}

}

}

// C collision function that calls CUDA kernel

extern "C"

void collision(DataStruct *data, float w0, float w1, float w5, float tau_inv, dim3 grid, dim3 block )

{

collision_kernel<<<grid, block, 0, data->stream>>> ( *data, w0, w1, w5, tau_inv );

CUT_CHECK_ERROR("collision failed");

}

__global__ void collision_3d_kernel ( DataStruct3d data, float w0, float w1, float w7, float tau_inv )

{

int x = threadIdx.x + blockIdx.x * blockDim.x;

int y = threadIdx.y + blockIdx.y * blockDim.y;

int z = threadIdx.z + blockIdx.z * blockDim.z;

int offset = x + y*blockDim.x*gridDim.x + z*blockDim.x*gridDim.x*blockDim.y*gridDim.y;

if (!data.solid_bound[offset])

{

// Create registers

float rho, ux, uy, uz, usq, uxsq, uysq, uzsq;

float uxuy7, uxuy8, uxuz9, uxuz10, uyuz15, uyuz16;

//Apply Bounce-Back boundary condition

if (data.solid[offset])

{

data.f1 [offset] = data.f2_new [offset];

data.f2 [offset] = data.f1_new [offset];

data.f3 [offset] = data.f4_new [offset];

data.f4 [offset] = data.f3_new [offset];

data.f5 [offset] = data.f6_new [offset];

data.f6 [offset] = data.f5_new [offset];

data.f7 [offset] = data.f12_new[offset];

data.f8 [offset] = data.f11_new[offset];

data.f9 [offset] = data.f14_new[offset];

data.f10[offset] = data.f13_new[offset];

data.f11[offset] = data.f8_new [offset];

data.f12[offset] = data.f7_new [offset];

data.f13[offset] = data.f10_new[offset];

data.f14[offset] = data.f9_new [offset];

data.f15[offset] = data.f18_new[offset];

data.f16[offset] = data.f17_new[offset];

data.f17[offset] = data.f16_new[offset];

data.f18[offset] = data.f15_new[offset];

}

else

{

// Calculate macroscopic density

Page 93: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

88

rho = data.f0_new[offset] + data.f1_new[offset] + data.f2_new[offset] +

data.f3_new[offset] + data.f4_new[offset] + data.f5_new[offset] +

data.f6_new[offset] + data.f7_new[offset] + data.f8_new[offset] +

data.f9_new[offset] + data.f10_new[offset] + data.f11_new[offset] +

data.f12_new[offset] + data.f13_new[offset] + data.f14_new[offset] +

data.f15_new[offset] + data.f16_new[offset] + data.f17_new[offset] +

data.f18_new[offset];

// Calculate macroscopic velocity in x

ux = ( +data.f1_new [offset] +data.f7_new [offset] +data.f8_new [offset]

+data.f9_new [offset] +data.f10_new[offset] -data.f2_new [offset]

-data.f11_new[offset] -data.f12_new[offset] -data.f13_new[offset]

-data.f14_new[offset] ) / rho;

// Calculate macroscopic velocity in y

uy = ( +data.f3_new [offset] +data.f7_new [offset] +data.f11_new[offset]

+data.f15_new[offset] +data.f16_new[offset] -data.f4_new [offset]

-data.f8_new [offset] -data.f12_new[offset] -data.f17_new[offset]

-data.f18_new[offset] ) / rho;

// Calculate macroscopic velocity in z

uz = ( +data.f5_new [offset] +data.f9_new [offset] +data.f13_new[offset]

+data.f15_new[offset] +data.f17_new[offset] -data.f6_new [offset]

-data.f10_new[offset] -data.f14_new[offset] -data.f16_new[offset]

-data.f18_new[offset] ) / rho;

// Calculate square velocity and macroscopic velocity

uxsq = ux*ux;

uysq = uy*uy;

uzsq = uz*uz;

usq = uxsq + uysq + uzsq;

data.u[offset] = sqrtf(usq);

// Pre-calculate some values

w0 = w0*rho;

w1 = w1*rho;

w7 = w7*rho;

uxuy7 = +ux +uy;

uxuy8 = +ux -uy;

uxuz9 = +ux +uz;

uxuz10 = +ux -uz;

uyuz15 = +uy +uz;

uyuz16 = +uy -uz;

data.f0 [offset] = data.f0_new [offset] - tau_inv*( data.f0_new [offset] -

w0*(1.f - 1.5f*usq) );

data.f1 [offset] = data.f1_new [offset] - tau_inv*( data.f1_new [offset] -

w1*(1.f + 3.f*ux + 4.5f*uxsq - 1.5f*usq) );

data.f2 [offset] = data.f2_new [offset] - tau_inv*( data.f2_new [offset] -

w1*(1.f - 3.f*ux + 4.5f*uxsq - 1.5f*usq) );

data.f3 [offset] = data.f3_new [offset] - tau_inv*( data.f3_new [offset] -

w1*(1.f + 3.f*uy + 4.5f*uysq - 1.5f*usq));

data.f4 [offset] = data.f4_new [offset] - tau_inv*( data.f4_new [offset] -

w1*(1.f - 3.f*uy + 4.5f*uysq - 1.5f*usq) );

data.f5 [offset] = data.f5_new [offset] - tau_inv*( data.f5_new [offset] -

w1*(1.f + 3.f*uz + 4.5f*uzsq - 1.5f*usq) );

data.f6 [offset] = data.f6_new [offset] - tau_inv*( data.f6_new [offset] -

w1*(1.f - 3.f*uz + 4.5f*uzsq - 1.5f*usq) );

data.f7 [offset] = data.f7_new [offset] - tau_inv*( data.f7_new [offset] -

w7*(1.f + 3.f*uxuy7 + 4.5f*uxuy7 *uxuy7 - 1.5f*usq) );

data.f8 [offset] = data.f8_new [offset] - tau_inv*( data.f8_new [offset] -

w7*(1.f + 3.f*uxuy8 + 4.5f*uxuy8 *uxuy8 - 1.5f*usq) );

data.f9 [offset] = data.f9_new [offset] - tau_inv*( data.f9_new [offset] -

w7*(1.f + 3.f*uxuz9 + 4.5f*uxuz9 *uxuz9 - 1.5f*usq) );

data.f10[offset] = data.f10_new[offset] - tau_inv*( data.f10_new[offset] -

w7*(1.f + 3.f*uxuz10 + 4.5f*uxuz10*uxuz10 - 1.5f*usq) );

data.f11[offset] = data.f11_new[offset] - tau_inv*( data.f11_new[offset] -

w7*(1.f - 3.f*uxuy8 + 4.5f*uxuy8 *uxuy8 - 1.5f*usq) );

data.f12[offset] = data.f12_new[offset] - tau_inv*( data.f12_new[offset] -

w7*(1.f - 3.f*uxuy7 + 4.5f*uxuy7 *uxuy7 - 1.5f*usq) );

data.f13[offset] = data.f13_new[offset] - tau_inv*( data.f13_new[offset] -

w7*(1.f - 3.f*uxuz10 + 4.5f*uxuz10*uxuz10 - 1.5f*usq) );

data.f14[offset] = data.f14_new[offset] - tau_inv*( data.f14_new[offset] -

w7*(1.f - 3.f*uxuz9 + 4.5f*uxuz9 *uxuz9 - 1.5f*usq) );

data.f15[offset] = data.f15_new[offset] - tau_inv*( data.f15_new[offset] -

w7*(1.f + 3.f*uyuz15 + 4.5f*uyuz15*uyuz15 - 1.5f*usq) );

data.f16[offset] = data.f16_new[offset] - tau_inv*( data.f16_new[offset] -

w7*(1.f + 3.f*uyuz16 + 4.5f*uyuz16*uyuz16 - 1.5f*usq) );

data.f17[offset] = data.f17_new[offset] - tau_inv*( data.f17_new[offset] -

w7*(1.f - 3.f*uyuz16 + 4.5f*uyuz16*uyuz16 - 1.5f*usq) );

data.f18[offset] = data.f18_new[offset] - tau_inv*( data.f18_new[offset] -

w7*(1.f - 3.f*uyuz15 + 4.5f*uyuz15*uyuz15 - 1.5f*usq) );

}

}

}

Page 94: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

89

// C collision function that calls CUDA kernel

extern "C"

void collision_3d( DataStruct3d *data, float w0, float w1, float w7, float tau_inv, dim3 grid, dim3 block )

{

collision_3d_kernel<<<grid, block, 0, data->stream>>> ( *data, w0, w1, w7, tau_inv );

CUT_CHECK_ERROR("collision failed");

}

extern "C"

void collision_cpu( DataStructCpu *data, float w0, float w1, float w5, float tau_inv )

{

int offset;

float rho, ux, uy, usq, uxsq, uysq;

float uxuy5, uxuy6;

float w0_r, w1_r, w5_r;

for (offset = 0; offset < data->width*data->height; offset++) if (!data->solid_bound[offset])

{

//Apply Bounce-Back boundary condition

if (data->solid[offset])

{

data->f1[offset] = data->f3_new[offset];

data->f2[offset] = data->f4_new[offset];

data->f3[offset] = data->f1_new[offset];

data->f4[offset] = data->f2_new[offset];

data->f5[offset] = data->f7_new[offset];

data->f6[offset] = data->f8_new[offset];

data->f7[offset] = data->f5_new[offset];

data->f8[offset] = data->f6_new[offset];

}

else

{

// Calculate macroscopic density

rho = data->f0_new[offset] + data->f1_new[offset] + data->f2_new[offset] +

data->f3_new[offset] + data->f4_new[offset] + data->f5_new[offset] +

data->f6_new[offset] + data->f7_new[offset] + data->f8_new[offset];

// Calculate macroscopic velocity in x

ux = ( data->f1_new[offset] - data->f3_new[offset] + data->f5_new[offset] -

data->f6_new[offset] - data->f7_new[offset] + data->f8_new[offset] ) / rho;

// Calculate macroscopic velocity in y

uy = ( data->f2_new[offset] - data->f4_new[offset] + data->f5_new[offset] +

data->f6_new[offset] - data->f7_new[offset] - data->f8_new[offset] ) / rho;

// Calculate square velocity and macroscopic velocity

uxsq = ux*ux;

uysq = uy*uy;

usq = uxsq + uysq;

data->u[offset] = sqrtf(usq);

// Pre-calculate some values

w0_r = w0*rho;

w1_r = w1*rho;

w5_r = w5*rho;

uxuy5 = +ux+uy;

uxuy6 = -ux+uy;

// If the node is not solid calculate the collison term and pass the new distribution

// function to fx

data->f0[offset] = data->f0_new[offset] - tau_inv*( data->f0_new[offset] -

w0_r*(1.f - 1.5f*usq) );

data->f1[offset] = data->f1_new[offset] - tau_inv*( data->f1_new[offset] -

w1_r*(1.f + 3.f*ux + 4.5f*uxsq - 1.5f*usq) );

data->f2[offset] = data->f2_new[offset] - tau_inv*( data->f2_new[offset] -

w1_r*(1.f + 3.f*uy + 4.5f*uysq - 1.5f*usq) );

data->f3[offset] = data->f3_new[offset] - tau_inv*( data->f3_new[offset] -

w1_r*(1.f - 3.f*ux + 4.5f*uxsq - 1.5f*usq) );

data->f4[offset] = data->f4_new[offset] - tau_inv*( data->f4_new[offset] -

w1_r*(1.f - 3.f*uy + 4.5f*uysq - 1.5f*usq) );

data->f5[offset] = data->f5_new[offset] - tau_inv*( data->f5_new[offset] -

w5_r*(1.f + 3.f*uxuy5 + 4.5f*uxuy5*uxuy5 - 1.5f*usq) );

data->f6[offset] = data->f6_new[offset] - tau_inv*( data->f6_new[offset] -

w5_r*(1.f + 3.f*uxuy6 + 4.5f*uxuy6*uxuy6 - 1.5f*usq) );

data->f7[offset] = data->f7_new[offset] - tau_inv*( data->f7_new[offset] -

w5_r*(1.f - 3.f*uxuy5 + 4.5f*uxuy5*uxuy5 - 1.5f*usq) );

data->f8[offset] = data->f8_new[offset] - tau_inv*( data->f8_new[offset] -

w5_r*(1.f - 3.f*uxuy6 + 4.5f*uxuy6*uxuy6 - 1.5f*usq) );

}

}

}

extern "C"

void collision_3d_cpu( DataStruct3dCpu *data, float w0, float w1, float w7, float tau_inv )

{

int offset;

float w0_r, w1_r, w7_r;

Page 95: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

90

float rho, ux, uy, uz, usq, uxsq, uysq, uzsq;

float uxuy7, uxuy8, uxuz9, uxuz10, uyuz15, uyuz16;

for (offset = 0; offset < data->width*data->height*data->length; offset++) if (!data-

>solid_bound[offset])

{

//Apply Bounce-Back boundary condition

if (data->solid[offset])

{

data->f1 [offset] = data->f2_new [offset];

data->f2 [offset] = data->f1_new [offset];

data->f3 [offset] = data->f4_new [offset];

data->f4 [offset] = data->f3_new [offset];

data->f5 [offset] = data->f6_new [offset];

data->f6 [offset] = data->f5_new [offset];

data->f7 [offset] = data->f12_new[offset];

data->f8 [offset] = data->f11_new[offset];

data->f9 [offset] = data->f14_new[offset];

data->f10[offset] = data->f13_new[offset];

data->f11[offset] = data->f8_new [offset];

data->f12[offset] = data->f7_new [offset];

data->f13[offset] = data->f10_new[offset];

data->f14[offset] = data->f9_new [offset];

data->f15[offset] = data->f18_new[offset];

data->f16[offset] = data->f17_new[offset];

data->f17[offset] = data->f16_new[offset];

data->f18[offset] = data->f15_new[offset];

}

else

{

// Calculate macroscopic density

rho = data->f0_new[offset] + data->f1_new[offset] + data->f2_new[offset] +

data->f3_new[offset] + data->f4_new[offset] + data->f5_new[offset] +

data->f6_new[offset] + data->f7_new[offset] + data->f8_new[offset] +

data->f9_new[offset] + data->f10_new[offset] + data->f11_new[offset] +

data->f12_new[offset] + data->f13_new[offset] + data->f14_new[offset] +

data->f15_new[offset] + data->f16_new[offset] + data->f17_new[offset] +

data->f18_new[offset];

// Calculate macroscopic velocity in x

ux = ( +data->f1_new [offset] +data->f7_new [offset] +data->f8_new [offset]

+data->f9_new [offset] +data->f10_new[offset] -data->f2_new [offset]

-data->f11_new[offset] -data->f12_new[offset] -data->f13_new[offset]

-data->f14_new[offset] ) / rho;

// Calculate macroscopic velocity in y

uy = ( +data->f3_new [offset] +data->f7_new [offset] +data->f11_new[offset]

+data->f15_new[offset] +data->f16_new[offset] -data->f4_new [offset]

-data->f8_new [offset] -data->f12_new[offset] -data->f17_new[offset]

-data->f18_new[offset] ) / rho;

// Calculate macroscopic velocity in z

uz = ( +data->f5_new [offset] +data->f9_new [offset] +data->f13_new[offset]

+data->f15_new[offset] +data->f17_new[offset] -data->f6_new [offset]

-data->f10_new[offset] -data->f14_new[offset] -data->f16_new[offset]

-data->f18_new[offset] ) / rho;

// Calculate square velocity and macroscopic velocity

uxsq = ux*ux;

uysq = uy*uy;

uzsq = uz*uz;

usq = uxsq + uysq + uzsq;

data->u[offset] = sqrtf(usq);

// Pre-calculate some values

w0_r = w0*rho;

w1_r = w1*rho;

w7_r = w7*rho;

uxuy7 = +ux +uy;

uxuy8 = +ux -uy;

uxuz9 = +ux +uz;

uxuz10 = +ux -uz;

uyuz15 = +uy +uz;

uyuz16 = +uy -uz;

// If the node is not solid calculate the collison term and pass the new distribution

// function to fx

data->f0 [offset] = data->f0_new [offset] - tau_inv*( data->f0_new [offset] -

w0_r*(1.f - 1.5f*usq) );

data->f1 [offset] = data->f1_new [offset] - tau_inv*( data->f1_new [offset] -

w1_r*(1.f + 3.f*ux + 4.5f*uxsq - 1.5f*usq) );

data->f2 [offset] = data->f2_new [offset] - tau_inv*( data->f2_new [offset] -

w1_r*(1.f - 3.f*ux + 4.5f*uxsq - 1.5f*usq) );

data->f3 [offset] = data->f3_new [offset] - tau_inv*( data->f3_new [offset] -

w1_r*(1.f + 3.f*uy + 4.5f*uysq - 1.5f*usq));

data->f4 [offset] = data->f4_new [offset] - tau_inv*( data->f4_new [offset] -

Page 96: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

91

w1_r*(1.f - 3.f*uy + 4.5f*uysq - 1.5f*usq) );

data->f5 [offset] = data->f5_new [offset] - tau_inv*( data->f5_new [offset] -

w1_r*(1.f + 3.f*uz + 4.5f*uzsq - 1.5f*usq) );

data->f6 [offset] = data->f6_new [offset] - tau_inv*( data->f6_new [offset] -

w1_r*(1.f - 3.f*uz + 4.5f*uzsq - 1.5f*usq) );

data->f7 [offset] = data->f7_new [offset] - tau_inv*( data->f7_new [offset] -

w7_r*(1.f + 3.f*uxuy7 + 4.5f*uxuy7 *uxuy7 - 1.5f*usq) );

data->f8 [offset] = data->f8_new [offset] - tau_inv*( data->f8_new [offset] -

w7_r*(1.f + 3.f*uxuy8 + 4.5f*uxuy8 *uxuy8 - 1.5f*usq) );

data->f9 [offset] = data->f9_new [offset] - tau_inv*( data->f9_new [offset] -

w7_r*(1.f + 3.f*uxuz9 + 4.5f*uxuz9 *uxuz9 - 1.5f*usq) );

data->f10[offset] = data->f10_new[offset] - tau_inv*( data->f10_new[offset] -

w7_r*(1.f + 3.f*uxuz10 + 4.5f*uxuz10*uxuz10 - 1.5f*usq) );

data->f11[offset] = data->f11_new[offset] - tau_inv*( data->f11_new[offset] -

w7_r*(1.f - 3.f*uxuy8 + 4.5f*uxuy8 *uxuy8 - 1.5f*usq) );

data->f12[offset] = data->f12_new[offset] - tau_inv*( data->f12_new[offset] -

w7_r*(1.f - 3.f*uxuy7 + 4.5f*uxuy7 *uxuy7 - 1.5f*usq) );

data->f13[offset] = data->f13_new[offset] - tau_inv*( data->f13_new[offset] -

w7_r*(1.f - 3.f*uxuz10 + 4.5f*uxuz10*uxuz10 - 1.5f*usq) );

data->f14[offset] = data->f14_new[offset] - tau_inv*( data->f14_new[offset] -

w7_r*(1.f - 3.f*uxuz9 + 4.5f*uxuz9 *uxuz9 - 1.5f*usq) );

data->f15[offset] = data->f15_new[offset] - tau_inv*( data->f15_new[offset] -

w7_r*(1.f + 3.f*uyuz15 + 4.5f*uyuz15*uyuz15 - 1.5f*usq) );

data->f16[offset] = data->f16_new[offset] - tau_inv*( data->f16_new[offset] -

w7_r*(1.f + 3.f*uyuz16 + 4.5f*uyuz16*uyuz16 - 1.5f*usq) );

data->f17[offset] = data->f17_new[offset] - tau_inv*( data->f17_new[offset] -

w7_r*(1.f - 3.f*uyuz16 + 4.5f*uyuz16*uyuz16 - 1.5f*usq) );

data->f18[offset] = data->f18_new[offset] - tau_inv*( data->f18_new[offset] -

w7_r*(1.f - 3.f*uyuz15 + 4.5f*uyuz15*uyuz15 - 1.5f*usq) );

}

}

}

Page 97: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

92

save.h:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 28.11.2012

* UNICAMP - Universidade Estadual de Campinas

*

* This header file defines the psave and save functions. The psave function saves the parallel

* file, that is used to unite all the datasets from each device save file.* Each device saves

* its dataset file when it's called the save function.

**************************************************************************************************/

#ifndef SAVE_H

#define SAVE_H

extern "C"

void psave(int width, int height, int length, int step, int deviceCount, int commSize);

extern "C"

void *save(void *w);

#endif

save.cu:

/**************************************************************************************************

* D2Q9 Lattice Boltzmann CUDA Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* Save functions

**************************************************************************************************/

#include <cutil.h>

#include <cuda_runtime_api.h>

#include <pthread.h>

#include <error.h>

#include "latibol.h"

extern "C"

void psave(int width, int height, int length, int step, int deviceCount, int commSize)

{

// Create variables

int i;

FILE *psave_file;

char *name;

// Allocate name string

name = (char *)malloc(13*sizeof(char));

// Get the name 'rxxxx.pvti' of save file according to the number of the step

name[0] = 'r';

name[1] = (char)(step / 1000) + '0';

name[2] = (char)(step % 1000 / 100) + '0';

name[3] = (char)(step % 100 / 10) + '0';

name[4] = (char)(step % 10) + '0';

name[5] = '.';

name[6] = 'p';

name[7] = 'v';

name[8] = 't';

name[9] = 'i';

name[10] = '\0';

// Create and open 'rxxxx.pvti' file

printf("%s ", name);

psave_file=fopen(name,"w");

// Change name string to name of the dataset file

name[5] = 'p';

name[8] = '.';

name[9] = 'v';

name[10] = 't';

name[11] = 'i';

name[12] = '\0';

// Save the parallel file according to VTK file format

fprintf(psave_file, "<VTKFile type=\"PImageData\" version=\"0.1\" byte_order=\"LittleEndian\">\n");

fprintf(psave_file, " <PImageData WholeExtent=\"0 %d 0 %d 0 %d\" GhostLevel=\"#\" Origin=\"0 0 0\""

" Spacing=\"1 1 1\">\n", (width - 1)*commSize*deviceCount, height-1, length-1);

Page 98: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

93

fprintf(psave_file, " <PPointData Scalars=\"Velocity\">\n");

fprintf(psave_file, " <DataArray type=\"Float32\" Name=\"Velocity\" format=\"ascii\"/>\n");

fprintf(psave_file, " </PPointData>\n <PCellData>\n </PCellData>\n");

for (i=0; i < commSize*deviceCount; i++) {

name[6] = (char)(i / 10) + '0';

name[7] = (char)(i % 10) + '0';

fprintf(psave_file, " <Piece Extent=\"%d %d 0 %d 0 %d\" Source=\"%s\"/>\n",

i*(width-1), (width-1) + i*(width-1), height-1, length-1, name);

}

fprintf(psave_file, " </PImageData>\n</VTKFile>\n");

// Close file and deallocate name string

fclose (psave_file);

free( name );

}

extern "C"

void *save(void *w)

{

SaveStruct *saveData = (SaveStruct *) w;

int i;

int width = saveData->width;

int height = saveData->height;

int length = saveData->length;

int step = saveData->step;

int commRank = saveData->commRank;

int deviceID = saveData->deviceID;

int deviceCount = saveData->deviceCount;

FILE *save_file;

// Get the name 'rxxxxpxx.vti' of save file according to the number of the step and device

saveData->name[0] = 'r';

saveData->name[1] = (char)(step / 1000) + '0';

saveData->name[2] = (char)(step % 1000 / 100) + '0';

saveData->name[3] = (char)(step % 100 / 10) + '0';

saveData->name[4] = (char)(step % 10) + '0';

saveData->name[5] = 'p';

saveData->name[6] = (char)( (commRank*deviceCount+deviceID) / 10 ) + '0';

saveData->name[7] = (char)( (commRank*deviceCount+deviceID) % 10 ) + '0';

saveData->name[8] = '.';

saveData->name[9] = 'v';

saveData->name[10] = 't';

saveData->name[11] = 'i';

saveData->name[12] = '\0';

// Create and open 'rxxxxpxx.vti' file

save_file = fopen(saveData->name,"w");

// Save dataset file according to VTK file format

fprintf(save_file, "<VTKFile type=\"ImageData\" version=\"0.1\" byte_order=\"LittleEndian\">\n");

fprintf(save_file, " <ImageData WholeExtent=\"%d %d 0 %d 0 %d\" Origin=\"0 0 0\" Spacing=\"1 1

1\">\n",

(commRank*deviceCount+deviceID)*(width-1), width-1 + (commRank*deviceCount+deviceID)*(width-1),

height-1, length-1);

fprintf(save_file, " <Piece Extent=\"%d %d 0 %d 0 %d\">\n",

(commRank*deviceCount+deviceID)*(width-1), width-1 + (commRank*deviceCount+deviceID)*(width-1),

height-1, length-1);

fprintf(save_file, " <PointData Scalars=\"Velocity\">\n");

fprintf(save_file, " <DataArray type=\"Float32\" Name=\"Velocity\" format=\"ascii\" >\n");

fprintf(save_file, " ");

// Save dataset to file.

// If the lattice node is solid, the save data is 0.0, else, save data is the velocity

for(i=0; i < width*height*length; i++)

{

if (!saveData->solid[i]) fprintf(save_file, "%f ",saveData->u[i]);

else fprintf(save_file, "%f ", 0.0);

if ((i+1) % width == 0) fprintf(save_file, "\n ");

}

fprintf(save_file, "</DataArray>\n </PointData>\n <CellData>\n </CellData>\n </Piece>\n "

"</ImageData>\n</VTKFile>\n");

// Close file and deallocate host variables

fclose( save_file );

return 0;

}

Page 99: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

94

cudaFree_data.h:

/**************************************************************************************************

* Lattice Boltzmann Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* This header file defines the cudaFree_data function. This functions desallocate all memory

* at the device.

**************************************************************************************************/

#ifndef CUDAFREE_H

#define CUDAFREE_H

extern "C"

void cudaFree_data( DataStruct *data, int deviceCount );

extern "C"

void cudaFree_data_3d( DataStruct3d *data, int deviceCount );

#endif

cudaFree_data.cu:

/**************************************************************************************************

* D2Q9 Lattice Boltzmann Multi-GPU CUDA Simulator

* Lucas Volpe - 11.28.2012

* UNICAMP - Universidade Estadual de Campinas

*

* CudaFree_data function

**************************************************************************************************/

#include <cutil.h>

#include <cuda_runtime_api.h>

#include "latibol.h"

extern "C"

void cudaFree_data( DataStruct *data, int deviceCount )

{

int i;

for (i=0; i < deviceCount ; i++)

{

// Set current device and desallocate memories

CUDA_SAFE_CALL( cudaSetDevice( i ) );

CUDA_SAFE_CALL( cudaFree( data[i].u ) );

CUDA_SAFE_CALL( cudaFree( data[i].solid ) );

CUDA_SAFE_CALL( cudaFree( data[i].solid_bound ) );

CUDA_SAFE_CALL( cudaFree( data[i].f0 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f1 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f2 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f3 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f4 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f5 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f6 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f7 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f8 ) );

CUDA_SAFE_CALL(cudaFree( data[i].f0_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f1_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f2_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f3_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f4_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f5_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f6_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f7_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f8_new ) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f1 ) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f5 ) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f8 ) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f3 ) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f6 ) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f7 ) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f1 ) );

Page 100: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

95

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f5 ) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f8 ) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f3 ) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f6 ) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f7 ) );

CUDA_SAFE_CALL( cudaStreamDestroy( data[i].stream ) );

}

}

extern "C"

void cudaFree_data_3d( DataStruct3d *data, int deviceCount )

{

int i;

for (i=0; i < deviceCount ; i++)

{

// Set current device and desallocate memories

CUDA_SAFE_CALL( cudaSetDevice( i ) );

CUDA_SAFE_CALL( cudaFree( data[i].u ) );

CUDA_SAFE_CALL( cudaFree( data[i].solid ) );

CUDA_SAFE_CALL( cudaFree( data[i].solid_bound ) );

CUDA_SAFE_CALL( cudaFree( data[i].f0 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f1 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f2 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f3 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f4 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f5 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f6 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f7 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f8 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f9 ) );

CUDA_SAFE_CALL( cudaFree( data[i].f10) );

CUDA_SAFE_CALL( cudaFree( data[i].f11) );

CUDA_SAFE_CALL( cudaFree( data[i].f12) );

CUDA_SAFE_CALL( cudaFree( data[i].f13) );

CUDA_SAFE_CALL( cudaFree( data[i].f14) );

CUDA_SAFE_CALL( cudaFree( data[i].f15) );

CUDA_SAFE_CALL( cudaFree( data[i].f16) );

CUDA_SAFE_CALL( cudaFree( data[i].f17) );

CUDA_SAFE_CALL( cudaFree( data[i].f18) );

CUDA_SAFE_CALL(cudaFree( data[i].f0_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f1_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f2_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f3_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f4_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f5_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f6_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f7_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f8_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f9_new ) );

CUDA_SAFE_CALL(cudaFree( data[i].f10_new) );

CUDA_SAFE_CALL(cudaFree( data[i].f11_new) );

CUDA_SAFE_CALL(cudaFree( data[i].f12_new) );

CUDA_SAFE_CALL(cudaFree( data[i].f13_new) );

CUDA_SAFE_CALL(cudaFree( data[i].f14_new) );

CUDA_SAFE_CALL(cudaFree( data[i].f15_new) );

CUDA_SAFE_CALL(cudaFree( data[i].f16_new) );

CUDA_SAFE_CALL(cudaFree( data[i].f17_new) );

CUDA_SAFE_CALL(cudaFree( data[i].f18_new) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f1 ) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f7 ) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f8 ) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f9 ) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f10) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f2 ) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f11) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f12) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f13) );

CUDA_SAFE_CALL( cudaFree( data[i].border_f14) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f1 ) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f7 ) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f8 ) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f9 ) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f10) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f2 ) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f11) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f12) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f13) );

CUDA_SAFE_CALL( cudaFree( data[i].nb_border_f14) );

CUDA_SAFE_CALL( cudaStreamDestroy( data[i].stream ) );

}

}

Page 101: UNIVERSIDADE ESTADUAL DE CAMPINAS …lotavio/tgs/2012_Partição de Domínios para... · Resultado do arquivo de saída ‘r0001.pvti’ cortado ao meio em x.....28 Figura 4.8. Perfil

96