risoluzione delle equazioni di navierstokes su scheda graficaforma/didattica/progettipacs/... ·...

66

Upload: truonghuong

Post on 18-Feb-2019

213 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

POLITECNICO DI MILANO

FACOLTA DI INGEGNERIA DEI SISTEMI

Corso di Laurea in

Ingegneria Matematica

Risoluzione delle equazioni di NavierStokes su scheda grafica

Progetto del Corso di Programmazione Avanzata per ilCalcolo Scientico

Alberto CRIVELLARO Nicoletta PAPUCCI751750 750733

Anno Accademico 2010/2011

Page 2: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali
Page 3: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Introduzione

Questo documento descrive la realizzazione di un software per simulazioni inuidodinamica sviluppato nel quadro del corso di Programmazione Avanzataper il Calcolo Scientico, AA 2010/2011. Scopo di questo lavoro è l'imple-mentazione di un algoritmo risolutivo per le equazioni di Navier-Stokes peruidi incomprimibili basato su tecniche di calcolo parallelo su scheda graca.

Una scheda graca (GPU) possiede una struttura intrinsecamente paral-lela e ottimizzata per il calcolo di semplici operazioni su larghe quantitàdi dati; è possibile sfruttare tali caratteristiche per eseguire in parallelooperazioni che esulano dalla semplice gestione delle visualizzazione su uncomputer.

Questo approccio, chiamato GPGPU (General Purpose computation onGPU), di recente introduzione, è attualmente in piena espansione in numerosisettori. in particolare sta rivoluzionando il mondo del calcolo scientico mod-erno, orendo prestazioni di calcolo molto maggiori rispetto alle tradizionaliimplementazioni su CPU e permettendo simulazioni real time e interattive.

L'impiego del calcolo parallelo su GPU permette di aumentare notevol-mente la velocità di calcolo, ma è necessario un ripensamento degli algoritmirisolutivi tradizionali per renderli parallelizzabili e tenere in conto i vincolilegati alla struttura delle GPU, meno essibili delle CPU tradizionali. Inquesto lavoro si fa uso di CUDA, un'interfaccia di programmazione per ilGPGPU su schede grache NVIDIA; si tratta di una tecnologia di recen-tissima introduzione (2007) e in piena evoluzione. In particolare, si imple-mentano due algoritmi risolutivi per le equazioni di Navier-Stokes, uno percondizioni al bordo periodiche e uno per condizioni di Dirichlet. Sono im-plementate anche tutte le routines necessarie per la gestione dei dati e perla visualizzazione in tempo reale della simulazione.

Nel capitolo 1 si introducono alcuni elementi di base su CUDA e suOpenGL; nel capitolo 2 si descrivono in dettaglio gli algoritmi di risoluzioneper le equazioni di Navier-Stokes (in particolare si fa uso del metodo diproiezione di Chorin-Temam accoppiato con un metodo spettrale di Fourier-Galerkin). Nel capitolo 3 sono descritti la struttura del codice e gli aspettiimplementativi più importanti. Inne nel capitolo 4 si eettua un'analisi delsimulatore basata sui risultati di prove numeriche.

iii

Page 4: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali
Page 5: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Indice

Introduzione iii

1 Elementi di CUDA e OpenGL 1

1.1 Struttura di una GPU . . . . . . . . . . . . . . . . . . . . . . 11.2 CUDA per il calcolo scientico . . . . . . . . . . . . . . . . . 21.3 Installazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.4 Elementi di base: kernels . . . . . . . . . . . . . . . . . . . . . 31.5 Memoria della GPU . . . . . . . . . . . . . . . . . . . . . . . 41.6 Coalescenza . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61.7 Compilazione . . . . . . . . . . . . . . . . . . . . . . . . . . . 71.8 OpenGL - compatibilità con CUDA . . . . . . . . . . . . . . . 7

2 Equazioni di riferimento e algoritmo di risoluzione 11

2.1 Le equazioni di Navier-Stokes per uidi incomprimibili . . . . 112.2 Il metodo di proiezione di Chorin-Temam . . . . . . . . . . . 122.3 Il metodo di Fourier Galerkin . . . . . . . . . . . . . . . . . . 13

3 Aspetti implementativi 17

3.1 Installazione di Offf . . . . . . . . . . . . . . . . . . . . . . . 173.1.1 Compilazione automatica su sistemi Linux-based . . . 18

3.2 Strutture dati e organizzazione del codice . . . . . . . . . . . 183.2.1 Overview del programma . . . . . . . . . . . . . . . . 18

3.3 Struttura a oggetti: classi . . . . . . . . . . . . . . . . . . . . 193.3.1 Alcune scelte stilistiche . . . . . . . . . . . . . . . . . 26

3.4 Kernels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.5 Funzioni e routines per la gestione della visualizzazione . . . . 293.6 Lettura di parametri a runtime: il le config.txt . . . . . . 313.7 Librerie e plug-in esterni . . . . . . . . . . . . . . . . . . . . . 32

v

Page 6: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

4 Risultati numerici 35

4.1 Settaggio parametri . . . . . . . . . . . . . . . . . . . . . . . . 354.2 Test eettuati . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

4.2.1 Test di convergenza . . . . . . . . . . . . . . . . . . . 364.2.2 Confronto CUDA vs Matlab . . . . . . . . . . . . . . . 374.2.3 Confronto su GPU diverse . . . . . . . . . . . . . . . . 38

4.3 Il proler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394.4 Conclusioni. Prospettive . . . . . . . . . . . . . . . . . . . . . 41

A FFT, DCT e DST 45

B Codice Matlab 55

Bibliograa 57

vi

Page 7: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Elenco delle gure

1.1 Operazioni oating point al secondo: paragone tra CPU e GPU. 21.2 Struttura di una griglia . . . . . . . . . . . . . . . . . . . . . 31.3 Organizzazione gerarchica della memoria di una GPU (da [1]). 5

2.1 Schema di risoluzione per condizioni al bordo periodiche. . . . 142.2 Schema di risoluzione per condizioni al bordo di Dirichlet. . . 16

3.1 Diagramma UML delle classi di Offf. . . . . . . . . . . . . . 193.2 I due tipi di visualizzazione . . . . . . . . . . . . . . . . . . . 30

4.1 Norma L2 della velocità orizzontale dopo 100 iterazioni(sin.)e dopo 500 iterazioni (dx.). . . . . . . . . . . . . . . . . . . . 37

4.2 Screenshot dell'output del CUDA proler. . . . . . . . . . . . 394.3 OFFF periodico: non coalescente vs coalescente . . . . . . . . 404.4 GPU-time per inizializzare BC_periodic . . . . . . . . . . . . 404.5 BC_dirichlet : utilizzazione della GPU . . . . . . . . . . . . . 41

A.1 Schema della DST ottimizzata. . . . . . . . . . . . . . . . . . 54

vii

Page 8: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali
Page 9: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 1Elementi di CUDA e OpenGL

CUDA è un'architettura hardware per l'elaborazione parallela creata daNVIDIA nel novembre del 2006. Tramite il suo ambiente di sviluppo, èpossibile scrivere codici general pourpose che sfruttano le capacità di calcoloparallelo delle GPU (Graphics Processing Units) Nvidia. CUDA è statosviluppato in modo da supportare vari linguaggi di programmazione, comeFortran, Matlab o Java. Quello maggiormente utilizzato è il CUDA C.

CUDA fornisce due dierenti API (Application Programming Interface),una di basso livello e una di livello superiore, le cui caratteristiche sonodettagliate nella sezione 1.7. Il primo CUDA SDK (Software DevelopmentKit) è stato reso pubblico nel febbraio del 2007 per Microsoft Windows eLinux. Da allora ogni anno ne esce una nuova versione, ed è stato aggiuntoil supporto per Mac OS X.

Le moderne GPU sono dei sistemi multi-core altamente parallelizzati, edè possibile sfruttare la loro struttura intrinsecamente parallela per eettuarecalcoli estremamente esigenti per i quali le tradizionali architetture CPUnon hanno una capacità di elaborazione suciente. Per questo motivo si èsviluppato negli ultimi anni un settore della ricerca informatica, laGPGPU(General-Purpose computing on Graphics Processing Units) che ha comeobiettivo l'utilizzo del processore della scheda graca per scopi diversi dallatradizionale gestione di immagini sullo schermo.

1.1 Struttura di una GPU

In questa sede non entriamo nei dettagli dell'architettura di una GPU. Aini della trattazione è comunque utile sapere che :

• una GPU possiede un'architettura specializzata per eettuare calcoliintensivi e altamente paralleli;• rispetto ad una CPU, molti più transistori sono occupati dal processingdei dati, e molti meno dalla gestione della memoria;

1

Page 10: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 1 Elementi di CUDA e OpenGL

Figura 1.1: Operazioni oating point al secondo: paragone tra CPU e GPU.

• una GPU esegue un programma in parallelo su ogni dato, quindi nonnecessita alcun controllo accurato di usso;• in una GPU l'intensità aritmetica, cioè il rapporto tra le operazioniaritmetiche e quelle di memoria, è molto alta; per questo la latenza diaccesso alla memoria può essere notevolmente ridotta.

Queste caratteristiche intrinseche delle schede grache spiegano il lorocrescente utilizzo nell'analisi di grandi quantità di dati con la program-mazione parallela.

1.2 CUDA per il calcolo scientico

La programmazione su GPU spalanca orizzonti allettanti per il calcolo sci-entico. L'esecuzione in parallelo degli algoritmi di risoluzione di equazionialle derivate parziali permette un notevole incremento della velocità di cal-colo tanto da permettere simulazioni non stazionarie in real time, con lapossibilità di modicare in ogni momento forzanti, condizioni al contorno,etc.. D'altro canto, proprio in virtù della peculiare struttura della GPU, èspesso necessario un ripensamento degli algoritmi di calcolo per beneciaredi tutte le sue potenzialità. Questo sforzo di ri-concepimento degli algoritmiè eseguito da anni nell'ambito dei videogiochi e dell'intrattenimento, doveperò lo scopo è quello di ottenere nel modo più rapido possibile simulazioniverosimili piuttosto che accurate. Il lavoro di adattamento dei tradizionalistrumenti dell'analisi numerica per il GPGPU è ancora un processo in pienosvolgimento, di cui questo progetto vuole essere un modestissimo contributo.

1.3 Installazione

Per poter sfruttare le capacità di CUDA è necessario avere una scheda gracaNvidia compatibile, il cui elenco è presente all'indirizzo http://developer.

2

Page 11: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 1 Elementi di CUDA e OpenGL

nvidia.com/cuda-gpus. Questo lavoro è stato sviluppato su una NvidiaGeForce 8400M GS, con compute capability 1.1.

Il software di installazione è freeware ed è presente nella sezione developerdel sito internet di Nvidia sotto forma di un Toolkit. Nello sviluppo di questoprogetto è stato utilizzato il Cuda Toolkit 3.2. 1.

L'installazione richiede innanzitutto l'aggiornamento del driver della sche-da graca Nvidia e in seguito del software CUDA (prima il Toolkit, poi laSDK); sul computer ci deve essere un compilatore gcc funzionante. Nella gui-da Getting Started presente nel Toolkit sono riportate tutte le informazioninecessarie passo passo per la corretta installazione e per la verica delle fun-zionalità del sistema. Nella GPU Computing SDK è presente una collezionedi esempi da compilare, molto utili sia come punto di partenza per avvicinarsia CUDA sia per avere una prima visione delle sue potenzialità.

1.4 Elementi di base: kernels

La base di un programma CUDA sono delle funzioni scritte in linguaggio C(con qualche estensione) chiamate kernels, che vengono eseguite in paralleloda più thread. Esse vengono chiamate dal programma che gira sulla CPU(host) e sono eseguite sulla GPU (device).

Figura 1.2: Struttura di una griglia

I threads sono organizzati inblocchi e i diversi blocchi formanouna griglia (Fig. 1.2). I threadappartenenti allo stesso blocco pos-sono sincronizzarsi, e ogni bloccopossiede uno spazio di memoria con-diviso dai propri threads, la cosid-detta shared memory. La dimen-sione della griglia e dei blocchi - chepossono essere monodimensionali,bidimensionali o tridimensionali-viene scelta dall'utente al momen-to della chiamata ad un kernel. Cisono alcune limitazioni sul numerodi threads per blocco che dipen-dono dalla compute capability dellascheda graca, e cambiare le dimen-sioni può inuire sulla performancedel calcolo. La documentazione di CUDA suggerisce di eettuare diversitentativi per trovare la congurazione ottimale.

1Da maggio 2011 è uscita una nuova versione, disponibile all'indirizzo http://

developer.nvidia.com/cuda-toolkit-40

3

Page 12: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 1 Elementi di CUDA e OpenGL

Un kernel è denito usando la dichiarazione __global__, e il numero dithreads e blocchi che lo eseguono sono specicati al momento della chiamatadel kernel con la sintassi <<<dimGrid,dimBlock>>>. Ogni thread dellagriglia è identicato in maniera univoca all'interno della griglia grazie allevariabili built-in ThreadIdx,blockIdix, blockDim.

A titolo di esempio, riportiamo un esempio di kernel tratto dal nostrosoftware, in cui si trasforma un array di oat in un array di numeri complessi.Si noti come questi ultimi sono salvati in un tipo specico di CUDA, float2,una struttura con due oat a cui si accede con i campi x e y (analogamentesono implementati i tipi float3 e float4). La chiamata del kernel avvienedall'host.

//Chiamata a l k e rne l d a l l ' hos treal2complex_k <<<dimGrid , dimBlock>>> (r_d , r_complex_d ) ;

//Kernel : trasforma una v a r i a b i l e f l o a t in una v a r i a b i l e complessa__global__ void real2complex_k ( f loat ∗a , f loat2 ∗c )

int idx = blockIdx . x∗blockDim . x+threadIdx . x ;int idy = blockIdx . y∗blockDim . y+threadIdx . y ;i f ( idx < Nx && idy <Ny)

int index = idx + idy ∗Nx;c [ index ] . x = a [ index ] ;c [ index ] . y = 0 . f ;

Esempio di Kernel

I kernel quindi sono fondamentalmente funzioni C con dichiarazione e chia-mata particolari. Inoltre, essi hanno alcune limitazioni rispetto alle classichefunzioni C. Essi non possono essere ricorsivi, devono ritornare void, nonpossono avere variabili statiche né un numero di argomenti variabile. Inoltrepossono accedere unicamente alla memoria della scheda graca, separata daquella della CPU.L'ottimizzazione del codice eseguito da un kernel comprende molti aspetticomplessi, che possono cambiare radicalmente le performances di un pro-gramma. La problematica principale da tenere presente è quella di garantireaccessi coalescenti alla memoria globale della GPU (cf. le sezioni seguenti),ma esistono altri accorgimenti che possono incrementare notevolmente la ra-pidità di esecuzione di un kernel. Per due esempi istruttivi si consultino, adesempio, gli articoli [2] e [3].

1.5 Memoria della GPU

La gestione della memoria e degli scambi di dati fra GPU e CPU è un aspettocruciale dell'implementazione del codice, tanto più che spesso il trasferimentodi dati da uno spazio di memoria all'altro costituisce il bottle neck di un

4

Page 13: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 1 Elementi di CUDA e OpenGL

programma. Come esemplicato in gura 1.3, in una GPU sono presentidiversi spazi di memoria:

• una memoria privata per ogni thread (registri);• una memoria condivisa da tutti i thread dello stesso blocco (sharedmemory);• una memoria globale accessibile da tutti i threads (global memory).

Sono inlotre presenti due spazi supplementari di memoria per sola letturaaccessibili da tutti i threads (texture memory e constant memory).

Figura 1.3: Organizzazione gerarchica della memoria di una GPU (da [1]).

La copia di dati dalla memoria dell'host alla global memory viene gestitadal codice eseguito sull'host, con i seguenti comandi://Al loca su l dev i ce l ' area d i memoria puntata da p , d i grandezza in

by t e s nby tescudaMalloc (void ∗∗p , s i ze_t nbytes ) ;//Copia i da t i pun ta t i da src n e l l ' area ind i ca t a da ds t .cudaMemcpy(void ∗dst , void ∗ src , s i z e_t nbytes ,

enum cudaMemcpyKind d i r e c t i o n ) ;

5

Page 14: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 1 Elementi di CUDA e OpenGL

//Libera l ' area d i memoria puntata da pcudaFree (void ∗p) ;

CUDA:Gestione della memoria

dove la direzione può essere cudaMemcpyHostToDevice,cudaMemcpyDeviceToHost, cudaMemcpyDeviceToDevice.

Si è già accennato al fatto che i threads dello stesso blocco possono col-laborare attraverso la shared memory, una memoria molto rapida che vieneliberata all'uscita dal kernel. Lo sfruttamento della shared memory e deiregistri è cruciale per diminuire gli overhead e ridurre operazioni di accessoalla global memory (si veda la sezione 1.6 sulla coalescenza).

__shared__ f loat sT i l e [ b l o ckS i z e ] ; // a l l o c a shared memorysT i l e [ idx ]= in [ index ] ; // copia g l i a t t r i b u t i in ing re s so n e l l a shared

memory__syncthreads ( ) ; // bar r i e ra : s i n c ron i z za i threads d i un b locco

Shared memory

La NVIDIA Cuda Programming Guide [1] presente nel Cuda Toolkitcontiene (in abbondanza) tutte le informazioni necessarie per arontare lascrittura dei primi kernel e per ottimizzare l'utilizzo delle risorse parallele.Se ne consiglia vivamente la lettura.

1.6 Coalescenza

Fino ad ora abbiamo visto come sfruttare la memoria della scheda gracaper immagazzinare e gestire informazioni più rapidamente. Ci sono peròalcuni aspetti riguardanti le modalità di accesso alla memoria globale chepermettono di ottimizzare ulteriormente le performance.

Rendere possibili degli accessi coalescenti alla global memory signicariuscire ad accorpare più letture o scritture in un'unica transazione. Gliaccessi alla global memory dei chip graci compatibili con CUDA devonoseguire delle regole ben precise per minimizzare il numero di transazioni.L'unità a cui fare riferimento è quella dell'half-warp, cioè di 16 threadfacenti parti dello stesso gruppo di 32 thread (warp) tenuti in esecuzioneper ciascun ciclo di clock su un multiprocessore. Le linee guida da seguirenell'organizzazione degli accessi di un half warp cambiano a seconda dellacompute capability supportata dal dispositivo.

Per i chip graci con compute capability 1.0 o 1.1 (come quello usatonello sviluppo di questo lavoro), gli accessi di un half-warp sono coalescentise leggono un'area contigua di memoria di:

• 64 byte ogni thread può leggere 16 bit (int, float, ...)• 128 byte ogni thread può leggere un int2,float2, ...• 256 byte ogni thread può leggere un int4, float4, ...

6

Page 15: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 1 Elementi di CUDA e OpenGL

Inoltre l'indirizzo iniziale di una regione deve essere multiplo della grandezzadella regione e gli accessi devono essere perfettamente allineati tra i thread.

Il modo più semplice per ridurre gli accessi non coalescenti è sfruttandola shared memory. Bisogna però assicurarsi che le operazioni di copia dellamemoria globale in quella shared siano fatte seguendo le regole appena citate.In seguito l'elaborazione dei dati viene eettuata sulla memoria condivisa,per poi essere ricopiati in quella globale solo prima dell'uscita dal kernel.

Fortunatamente nelle schede grache più recenti le condizioni per averecoalescenza degli accessi sono meno severe. Per maggiori informazioni ariguardo si veda il capitolo 5 di [1].

1.7 Compilazione

Nell'introduzione si è già accennato all'esistenza di due interfacce di pro-grammazione. La driver API è una lower-level API che fornisce funzioniper il caricamento e la chiamata dei kernel come moduli di codice binarioo assembly del CUDA, permettendo quindi un perfetto controllo del codicee garantendo l'indipendenza da altri linguaggi. Invece il CUDA C ha unaruntime API costruita sopra l'interfaccia driver: è quindi più coincisa e piùsemplice da programmare.

In entrambi i casi i kernel devono essere compilati in codice binario connvcc per essere eseguiti sul device. I le sorgente compilati con nvcc pos-sono contenere un mix di codice eseguito sull'host e sul device. Il workowdi base del compilatore consiste nel separare il codice device da quello host,e compilare il primo in una forma assembly (codice PTX) oppure binaria(oggetto cubin). Il codice host può sia essere integrato direttamente comecodice oggetto, sia ricompilato con un dierente compilatore, per esempiogcc. Dato che nvcc non supporta il linguaggio C++, l'utilizzo in contempo-ranea di nvcc e gcc permette, fra le altre cose, di integrare il codice CUDAin un programma strutturato secondo la programmazione a oggetti.

1.8 OpenGL - compatibilità con CUDA

OpenGL (Open Graphics Library) è una specica che denisce una APIcomune a più linguaggi per scrivere codici che producono graca in 2D e3D. É stata sviluppata dalla Silicon Graphics Inc. nel 1992, e al momento ègestita dal consorzio no-prot Khronos Group.

Si tratta di una specica con funzioni di graca avanzate, infatti è ampia-mente usata per il rendering di videogiochi, realtà virtuali, CAD etc.; essa haquindi moltissime funzioni evolute che esulano dall'ambito di questo lavoro.Nel seguito la spiegazione sarà incentrata sulla possibilità di interagire conCUDA per rappresentare in real time i risultati calcolati dalla scheda graca.Manca nella seppur ampia bibliograa presente su internet un buon tutorial

7

Page 16: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 1 Elementi di CUDA e OpenGL

o un manuale di riferimento che spieghi come integrare OpenGL a CUDA.Una buona fonte di ispirazione sono i già citati esempi presenti nella SDK.

Riassumiamo nel seguito gli step da seguire per riuscire a visualizzare deidati in real time e i comandi principali di OpenGL:

1. Inizializzazione ambiente graco2. Creazione VBO3. Aggiornamento VBO ad ogni iterazione4. Visualizzazione

Innanzitutto è necessario inizializzare l'ambiente graco creando unanestra e associandole alcune funzioni satellite che permettono di gestirela visualizzazione, come i comandi da tastiera, il movimento del mouse oaltre funzioni. Per queste prime nozioni si richiama il primo capitolo dellaOpenGL Programming Guide [4].

Le mattonelle della rappresentazione con OpenGL sono i VBO (VertexBuer Objects) che salvano i vettori di dati da visualizzare su memoriagraca e facilitano il trasferimento eciente di memoria. Gli step per creareun VBO e renderlo accessibile a CUDA sono i seguenti:

glGenBuffersARB ( s i ze_t n , int∗ i d s ) // genera un bu f f e r o b j e c t ;glBindBufferARB (enum target , int i d s ) // as soc ia i l b u f f e r ;glBufferDataARB (enum target , s i ze_t s i z e , void∗ data , enum usage ) //

copia i da t i ne l b u f f e r ;cudaGraphicsGLRegisterBuffer ( struct cudaGraphicsResource ∗∗ re source ,

int bu f f e r , unsigned int f l a g s ) // rende l ' o g ge t t o b u f f e ra c c e s s i b i l e da CUDA.

Creazione VBO

A questo punto la GPU conterrà un puntatore ad una struttura di tipocudaGraphicsResource, che viene aggiornata non appena il processore dellascheda graca ha calcolato i nuovi risultati. Questo procedimento vienequindi eettuato ad ogni ciclo temporale con la sintassi:

cudaGraphicsMapResources ( int count , struct cudaGraphicsResource ∗∗r e source s , cudaStream_t stream ) // rende l a r i s o r s a a c c e s s i b i l eda CUDA

cudaGraphicsResourceGetMappedPointer (void ∗∗devPtr , s i ze_t ∗ s i z e ,struct cudaGraphicsResource ∗ r e s ou r c e ) // a t t r a v e r s o devPtrr i t o rna un puntatore a t t r a v e r s o i l qua le s i possa accedere a l l ar i s o r s a g r a f i c a

Aggiornamento buer object

Una volta che si modica il valore dell'oggetto puntato da devPtr, ilVBO nella scheda graca si aggiorna automaticamente, insieme alla visual-izzazione. Ora non resta che dissociare le risorse grache concudaGraphicsUnmapResource e passare all'iterazione successiva.

La visualizzazione viene eettuata automaticamente da una delle funzioniche sono state inizializzate allo step 1. Si tratta della funzione display(),che non può prendere nessun argomento e che, dopo aver aggiornato i dati,imposta le caratteristiche della visualizzazione. In particolare attraverso la

8

Page 17: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 1 Elementi di CUDA e OpenGL

funzione glVertexPointer specica la location di un array di coordinateda disegnare, imposta il tipo di oggetto che si vuole disegnare (punti, linee,poligoni) e il loro colore.

9

Page 18: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali
Page 19: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 2Equazioni di riferimento e algoritmo

di risoluzione

2.1 Le equazioni di Navier-Stokes per uidi incom-

primibili

Il presente lavoro ha come scopo la risoluzione delle equazioni di Navier-Stokes per uidi incomprimibili:

Trovare u : Ω ⊂ R2 × [0, T ]→ R2 tale che:∂u∂t − ν4u + u · ∇u +4p = fdivu = 0 in Ωu(x, t = 0) = u0

+ Boundary conditions,

(2.1)

dove Ω è un dominio rettangolare di R2. Le equazioni (2.1) servono a descri-vere il campo di velocità u(x, t) e la pressione p(x, t) di un uido di viscositàν sottoposto a una forzante f(x, t). Per semplicità nel seguito supporremoche Ω = [0, 1]× [0, 1] e che f(x, t) = f(x) (forzante stazionaria). 1

Esiste una vasta bibliograa sulla discretizzazione delle equazioni di NavierStokes e oggi sono moltissimi i metodi numerici a disposizione per risolverle[5, 6].

L'obiettivo del nostro lavoro è di implementare un solutore performantesu GPU per ottenere simulazioni realistiche in tempo reale, ossia di sfruttarele capacità di calcolo della GPU per permettere il calcolo della soluzione ela sua contemporanea visualizzazione a ogni passo temporale. L'algoritmorisolutivo sviluppato ha il pregio da un lato di prestarsi a essere completa-

1Il nostro codice permette simulazioni su domini rettangolari generici, con forzantestazionaria.

11

Page 20: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 2 Equazioni di riferimento e algoritmo di risoluzione

mente parallelizzato e dall'altro di sfruttare al massimo le capacità di calcolodella GPU. Esso si basa su:

• il metodo di proiezione di Chorin-Temam per l'avanzamento temporale;• il metodo spettrale di Fourier-Galerkin per la risoluzione dei problemialle derivate parziali a ogni passo temporale.

In particolare la scelta del metodo spettrale di Fourier-Galerkin perme-tte di risolvere i sistemi lineari associati ai problemi alle derivate parzialiattraverso la FFT (Fast Fourier Transform), il cui calcolo può essere resonotevolmente più rapido se eseguito su GPU.

2.2 Il metodo di proiezione di Chorin-Temam

La risoluzione delle equazioni di Navier-Stokes attraverso i metodi di proiezioneprevede la scomposizione dell'avanzamento temporale in due passi in cuivengono risolti separatamente gli operatori di diusione e trasporto e il vin-colo di incomprimibilità. L'algoritmo proposto separatamente da Chorin eTemam alla ne degli anni '60 [7, 8] si basa su questa osservazione e risolvele equazioni (2.1) nel seguente modo :

per ogni passo k ≥ 0 risolvere:

STEP 1 :

u−uk

dt − ν4u + uk · ∇uk = fk+1 in ΩBoundary Conditions

STEP 2 :

4pk+1 = 1

dt div u in Ω∂p∂n = 0 su ∂Ω

STEP 3 : uk+1 = u− dt∇ pk+1

(2.2)

Allo STEP 1 si risolve un problema elittico per trovare il valore dellavelocità intermedia u che non soddisfa il vincolo di incomprimibilità. LoSTEP 2 viene chiamato passo di proiezione infatti i valori aggiornati dipressione e velocità si trovano proiettando u su uno spazio a divergenzanulla Hdiv = v ∈ L2(Ω), div v = 0.

La scelta delle condizioni al bordo in (2.2) non è banale. Infatti in Hdivnon si può denire la traccia al bordo, ma soltanto la traccia della compo-nente normale u · n|∂Ω. Questa limitazione teorica può portare ad errori displitting quando si risolve un problema con condizioni al bordo di Dirichletomogenee. Il passo 2 risulta ben denito unicamente se si impone u · n = 0su ∂Ω: la velocità nale non ha le stesse condizioni al contorno di quella in-iziale! Questo comportamento si può riettere nella presenza di strati limitesulla pressione.

Sono stati dimostrati risultati di stabilità e di convergenza del primoordine per la velocità e per la pressione.

12

Page 21: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 2 Equazioni di riferimento e algoritmo di risoluzione

2.3 Il metodo di Fourier Galerkin

Si è già accennato nella parte introduttiva che l'obiettivo del lavoro è quellodi sfruttare le capacità di calcolo parallele della GPU e in particolare dellasua FFT. Per farlo i sistemi (2.2) vengono risolti con un metodo spettrale ditipo Fourier-Galerkin [9]. Si tratta di una classe di metodi spettrali in cuil'edp viene proiettata sullo spazio nito dimensionale dei polinomi trigono-metrici. La soluzione u è così approssimata da una combinazione lineare difunzioni continue PN (u), e gli operatori dierenziali vengono diagonalizzatinello spazio di Fourier.

In questo modo un'equazione a derivate parziali si riduce ad un sistemalineare in cui le incognite sono i coecienti di Fourier della soluzione. Èpossibile dimostrare che il metodo di Fourier Galerkin è stabile e convergeesponenzialmente alla soluzione esatta.

Vediamo come si applica questo metodo al nostro problema modello. Perlo STEP 1 si risolve il problema :

Trovare uh ∈ Vh tale che:(uh,wh) + dt(ν4uh,wh) =dt(ukh · ∇ukh,wh) + dt(fk+1,wh) + (ukh,wh) ∀wh ∈ Vh,

(2.3)

dove ukh è la soluzione calcolata al passo precedente, Vh è uno spazio nitodimensionale di dimensione (Nx ×Ny)

2 e (·, ·) denota il prodotto scalare in[L2(Ω)]2.

Per lo STEP 2 risolviamo il problema :Trovare pk+1

h ∈ Zh tale che:(∇ pk+1

h ,∇ zh) = 1dt(div uh, zh) ∀zh ∈ Zh,

(2.4)

dove Zh è uno spazio Nx×Ny dimensionale e (·, ·) denota il prodotto scalarein L2(Ω).

Le condizioni al contorno per la velocità e per la pressione sono deniteimplicitamente dalla scelta degli spazi Vh e Zh.

Condizioni al contorno periodiche

L'imposizione delle condizioni al bordo periodiche per la velocità e per lapressione è la più naturale nell'ambito dei metodi di Fourier Galerkin, infattile funzioni di base sono già periodiche. Le soluzioni a ogni passo sono cercatenegli spazi :

Zh = spaneikπxeilπy, k = −Nx

2, . . . ,

Nx

2− 1, l = −Ny

2, . . . ,

Ny

2− 1,

Vh = Zh ×Zh.(2.5)

13

Page 22: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 2 Equazioni di riferimento e algoritmo di risoluzione

Figura 2.1: Schema di risoluzione per condizioni al bordo periodiche.

La scelta di questi spazi ha il vantaggio di rendere immediata la risoluzionedi (2.3) e (2.4) dato che i sistemi lineari associati sono diagonali. Qui l'-operazione computazionalmente più costosa è la valutazione dei coecientidi tali sistemi lineari che possono essere valutati rapidamente grazie all'usodella FFT.

Come mostrato nella gura 2.1, una prima fase di inizializzazione consistenel trasformare i dati iniziali (forzante e velocità) per trovare le rispettiveproiezioni nello spazio Vh (spazio delle frequenze). A ogni passo tempo-rale, si procede prima alla valutazione con uno schema a dierenze nitedel termine non lineare uk · ∇uk e alla sua trasformazione nello spazio dellefrequenze, quindi al calcolo degli STEP 1 e 2. I due passi di Chorin-Temamnecessitano della risoluzione di un problema con laplaciano, per la velocitàintermedia allo STEP 1 e per la pressione allo STEP 2. Entrambi i prob-lemi sono risolti nello spazio delle frequenze attraverso l'inversione di unamatrice diagonale. Anché gli spazi funzionali siano compatibili, si imponeuna condizione supplementare di media nulla per la pressione.

Osserviamo che a ogni passo temporale sono necessarie due trasformate diFourier: una diretta per il termine non lineare, e una inversa per permetterela visualizzazione della velocità nale e il calcolo del termine non lineare alpasso successivo.

14

Page 23: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 2 Equazioni di riferimento e algoritmo di risoluzione

Condizioni al contorno di Dirichlet

Il metodo sopra descritto riesce a coniugare in modo sorprendente lo sfrut-tamento dello speed-up che può fornire l'impego della GPU con un calcoloaccurato della soluzione a ogni passo tremporale. Tuttavia, il suo princi-pale svantaggio è costituito dall'impossibilità di imporre condizioni al bordodiverse da quelle periodiche denite implicitamente dalla scelta degli spazifunzionali.

Desiderando risolvere il problema della cavità, ci siamo confrontati conla sda di implementare un metodo numerico che conciliasse l'imposizione dicondizioni di Dirichlet omogenee per la velocità, con un costo computazionaleragionevole. Una dicoltà ulteriore deriva dalla necessità di imporre con-dizioni di Neumann per la pressione:

∀k = 0, 1, . . .

u(x, tk) = 0 su ∂Ω∂p(x,tk)∂n = 0 su ∂Ω,

(2.6)

dove n(x) è il versore normale uscente da ∂Ω.E' doveroso osservare che l'algoritmo per condizioni al bordo periodiche

presentato sopra è performante anche se implementato su CPU; al contrario,in presenza di equazioni con condizioni al bordo di tipo (2.6), se il calcoloviene eettuato su CPU l'approccio presentato qui risulta svantaggioso siain termini di tempo computazionale che in termini di accuratezza rispettoad altri algoritmi.

La letteratura su questo particolare approccio alla risoluzione delle equazionidi Navier-Stokes è molto scarsa. Nella fase di sviluppo dell'algoritmo ci siamoin parte ispirati a [10], in cui le tre direzioni della velocità (x e y e z) ven-gono proiettate in spazi diversi al ne di rispettare le condizioni di Dirichlet.Questo approccio è risultato però incompatibile con la necessità impostadall'algoritmo di proiezione di avere condizioni di Neumann per la pressione.

Il nostro algoritmo è pensato per conservare le caratterisiche di mag-gior interesse del metodo precedente, basate sulla parallelizzabilità dellaFFT, adattandolo per imporre le condizioni al contorno (2.6). Lo schemadi risoluzione è sostanzialmente identico a quello dell'algoritmo precedente(proiezione di Chorin Temam e metodo di Fourier Galerkin), ma si scelgonoin modo dierente gli spazi funzionali in cui proiettare pressione e velocità:

Zh = spancos(πkx)cos(πly) k = 0, . . . , Nx − 1 l = 0, . . . , Ny − 1

Vh =Wh ×Wh, con

Wh = spansin(πkx)sin(πly) k = 1, . . . , Nx l = 1, . . . , Ny

(2.7)Come evidenziato nella gura 2.2, lo schema iterativo è più complesso

che nel caso di condizioni al bordo periodiche. A ogni passo temporale sono

15

Page 24: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 2 Equazioni di riferimento e algoritmo di risoluzione

Figura 2.2: Schema di risoluzione per condizioni al bordo di Dirichlet.

necessarie una trasformazione diretta per il termine non lineare e a una in-versa per la velocità aggiornata, come nel caso precedente. Inoltre, dopo averrisolto lo STEP 1 in Vh (spazio dei seni) è necessario trovare l'espressionedi div uh in Zh (spazio dei coseni). Questo passaggio non era necessarionel primo algoritmo descritto.In questo caso il passaggio avviene atraverso la IDST (trasformata di seni in-versa) per le componenti di utilde, la valutazione di div(uh) nello spazio realee la sua trasformata nello spazio dei coseni. Si potrebbe anche passare diret-tamente dallo spazio dei seni a quello dei coseni con un semplice cambiamen-to di base (complessità temporale O((NxNy)

2), ma l'uso di due trasformatepermette di beneciare delle performances della FFT (complessità temporaleO(NxNylog(NxNy))).

16

Page 25: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3Aspetti implementativi

Il nostro software permette di calcolare una soluzione approssimata delleequazioni di Navier-Stokes per uidi incomprimibili in un dominio rettango-lare. L'algoritmo risolutivo è eseguito su GPU grazie all'interfaccia CUDAper GPGPU, e il risultato della simulazione è visualizzato in tempo reale.

Fra le caratteristiche principali del simulatore ricordiamo:

• supporto di due diverse modalità di visualizzazione.

• simulazioni possibili con due tipi diversi di condizioni al contorno,periodiche o di Dirichlet omogenee;

• gestione semplicata di tutti i parametri della simulazione da partedell'utente attraverso il le config.txt;

• parametri di visualizzazione modicabili a run-time (colore, dimensionedella nestra);

• interfaccia per il salvataggio dei dati e la visualizzazione con GNUPlotalla ne della simulazione;

• possibilità di caricare da le un campo di velocità iniziale e/o un campoforzante stazionario;

• compilazione automatizzata su sistemi Linux.

3.1 Installazione di Offf

Nella documentazione allegata al codice si trovano le istruzioni per l'instal-lazione, la compilazione e l'utilizzo del software. In particolare sono richiesti,prima della compilazione, l'installazione di CUDA e del CUDA Toolkit, en-trambi scaricabili all'indirizzo http://developer.nvidia.com. Si veda permaggiori informazioni la sezione 1.3.

17

Page 26: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

3.1.1 Compilazione automatica su sistemi Linux-based

Il comando make permette nei sistemi Linux di eettuare automaticamentela compilazione e il linking del codice seguendo le istruzioni contenute in unoscript, il Makefile. Nel nostro caso sono presenti due le di congurazione,Makefile e common.mk. Nel primo sono specicati i nomi dei les da com-pilare con nvcc e con gcc e le principali librerie da includere. Il secondo,ispirato all'analogo le presente nella SDK, esegue le istruzioni necessarieall'inclusione delle librerie e al settaggio corretto di una lunga serie di agsper i compilatori in base alle speciche del sistema operativo e della schedagraca.

3.2 Strutture dati e organizzazione del codice

Oltre alla messa a punto degli algoritmi risolutivi il progetto ha richiestoun'attenta riessione sull'impostazione del codice. La sua concezione ha cos-tituito il punto di incontro fra un approccio di tipo programmazione a oggetti(C++) per la gestione dei dati e degli algoritmi implementativi, e una se-rie di vincoli aggiuntivi imposti dall'utilizzo di CUDA e dall'uso di routinespre-denite di OpenGL.Allo stato attuale, tutte le funzioni CUDA (fra cui i kernels) non sono com-patibili con C++; ciò signica, ad esempio, che esse non possono accettarecome argomenti puntatori a istanze di classi, ma solo puntatori a una gam-ma ristretta di tipi di dati C (float*, int*, etc.) e alcuni tipi derivatiintrodotti da CUDA (int2*, float2*, float3*, etc.). 1 Fra gli altri vincoli,ricordiamo che i kernels non possono essere invocati direttamente dai meto-di di una classe per problemi di compatibilità di compilazione, ma solo dafunzioni C denite nel loro stesso le.

Inoltre l'utilizzo di routine pre-denite di OpenGL ha imposto un utilizzoattento di alcune variabili globali, di cui si è cercato di ridurre al massimol'uso.

3.2.1 Overview del programma

Il codice può essere ricondotto a tre parti principali:

• una parte strutturata secondo la programmazione a oggetti, che com-prende le classi BC e derivate, Vector_field, Scalar_field e derivata;

• una serie di routines e funzioni per la gestione della visualizzazione ela compatibilità fra CUDA e openGL;

1 Questa limitazione verrà probabilmente eliminata a partire dalle prossime versionidel CUDA-C

18

Page 27: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

Figura 3.1: Diagramma UML delle classi di Offf.

• una serie di funzioni (kernels CUDA) e routines con il compito di svol-gere calcoli numerici, trattamento di dati e altre operazioni che esulanodalla semplice gestione della visualizazione su GPU.

Ognuna di queste parti è oggetto di un'analisi più dettagliata nel seguito.

3.3 Struttura a oggetti: classi

La parte del codice strutturata secondo la programmazione a oggetti com-prende 2 tipi di classi. Un primo tipo è costituito dalla classe Scalar_field,dalla classe Complete_Scalar_field e dalla classe Vector_field. Esse con-sentono l'implementazione e la gestione di campi, scalari o vettoriali, denitisu una griglia rettangolare di punti di dimensioni Nx ×Ny. Il secondo tipoè costituito dalla classe BC e dalle sue classi glie. In esse vengono imple-mentati gli algoritmi risolutivi corrispondenti a ogni scelta di condizioni alcontorno. Non riportiamo nel seguito una descrizione dettagliata di tutti imetodi delle classi, presente nella documentazione allegata al sotfware, masolo una descrizione generale per chiarire gli aspetti più importanti o piùdelicati.

Classi Scalar_field e Copmlete_Scalar_field

L'implementazione di un solutore per le equazioni di Navier-Stokes ha resonecessario creare una classe per rappresentare un campo scalare denito su

19

Page 28: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

un dominio rettangolare (discretizzato) in R2. Le classi Scalar_field eComplete_Scalar_field svolgono questo compito.

Partiamo dalla classe Scalar_field. Il suo attributo principale è unarray dinamico di numeri cufftComplex, allocato sulla GPU, in cui sonostoccati i valori del campo scalare in tutti i punti della griglia bidimension-ale di Nx×Ny punti. Il cufftComplex è un tipo di dati della libreria CUFFT

per rappresentare numeri complessi; si può accedere a parte reale e immag-inaria con i campi x e y. Osserviamo che la scelta di un array dinamicoC è dettata dall'esigenza di manipolare i dati con kernels (cf. capitolo 1).Il costruttore non alloca l'array dinamico, ma lo setta a NULL. Per l'allo-cazione della memoria sulla scheda graca attraverso i comandi dettagliatinella sezione 1.5 è necessario invocare il metodo alloc_memory.Tra i metodi pubblici della classe, sono di particolare importanza quelliche implementano sul campo scalare le operazioni richieste dall'algoritmodi risoluzione: t, trasformata discreta di seni (dst), trasformata discreta dicoseni (dct) e loro inverse. Questi metodi eettuano delle trasformate in-

place e modicano unicamente l'attributo array_complex, sostituendo al suocontenuto il risultato della trasformata (per questo il nome è caratterizzatodall'estensione _C2C, che sta per ComplexToComplex).

Nel caso dell'algoritmo risolutivo per condizioni al bordo periodiche,si richiede di calcolare la trasformata inversa di Fourier ma di conservareallo stesso tempo l'array trasformato per utilizzarlo al passo successivo.Fra le molte scelte implementative possibili, abbiamo scelto di creare laclasse Complete_Scalar_field, che eredita da Scalar_field e che possiedeun attributo in più, l'array dinamico di float array_real. Il metodoifftscaled_C2R calcola la trasformata inversa di Fourier di array_complexsenza modicarne il contenuto, e la memorizza in array_real. I metodireal2complex e complex2real permettono di copiare il contenuto di un ar-ray sull'altro (moltiplicandolo per un eventuale fattore di scala).Osserviamo che nelle simulazioni della cavità l'algoritmo include operazionicome la dst e la dct, che si eettuano su campi scalari reali e ritornanocampi reali. Nonostante ciò, la scelta di utilizzare un array di cufftComplexè più indicata. Infatti queste operazioni sono implementate sfruttando lat della libreria Cufft che manipola array complessi, e l'uso di un array dicufftComplex permette di minimizzare i trasferimenti di memoria.

Si noti che la maggior parte delle operazioni sui dati di entrambe le classiavviene attraverso la chiamata a kernels e ad altre routines che girano suGPU (come la t della libreria Cufft), sfruttando al massimo le potenzialitàdi parallelizzazione sulla scheda graca.Un'ulteriore funzionalità è stata introdotta a ni dimostrativi. Per metterein evidenza le peculiarità della programmazione CUDA rispetto a quellatradizionale in C/C++, nel le config.txt è possibile scegliere di eseguireil codice in versione non ottimizzata. Si tratta di una funzionalità che nonha nessuno scopo pratico ai ni della simulazione, semplicemente scegliendo

20

Page 29: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

la versione non ottimizzata vengono impiegate:

• le versioni non coalescenti dei kernels utilizzati dal metodo transpose

di Scalar_field (ereditato da Complete_Scalar_field);

• una versione non ottimizzata della dst.

Questa funzionalità è implementata grazie all'uso di puntatori a funzioni,che vengono inizializzati a runtime a funzioni e metodi dierenti a secondadell'opzione specicata nel le di congurazione.2

(L'uso di funtori sembrava, in questa occasione, inutilmente sosticato).La denizione di costruttori di copia e dell'operatore di assegnamento ren-dono le classi Scalar_field e Complete_Scalar_field più versatili per unutilizzo generico.

class Sca l a r_ f i e l d protected :

int Nxx ;int Nyy ;cufftComplex∗ array_complex ;

private :

//Member func t i on po in t e r to a member methodvoid ( S c a l a r_ f i e l d : : ∗ dst_pointer ) ( ) ;//Member func t i on po in t e r to an ex tern func t i onvoid (∗ t ranspose_pointer ) (cufftComplex∗ ,cufftComplex∗ , int , int ) ;

void dct1D_C2C( ) ;void idct1D_C2C ( ) ;void dctwe ights ( int i ) ;

void dst1D_C2C( ) ;void dst2D_opt ( ) ;void dst2D_nonopt ( ) ;

public :S c a l a r_ f i e l d ( ) ;S c a l a r_ f i e l d ( int Nx, int Ny, bool _opt ) ;S c a l a r_ f i e l d ( const Sca l a r_ f i e l d& z ) ;~S ca l a r_ f i e l d ( ) ;

void alloc_memory ( ) ;void s av e t oF i l e (char const ∗ f i l ename , const f loat Lx , const f loat

Ly) ;

//Fourier forward and inve r s e transformvoid fft_C2C ( cu f f tHand le plan ) ;void i f f t sca led_C2C ( cu f f tHand le plan ) ;

//Fourier s ine forward and inve r s e transformvoid dst2D_C2C( ) ;void idst2D_C2C ( ) ;

//Fourier cos ine forward and inve r s e transform

2La questione della coalescenza è descritta più dettagliatamente nel capitolo 1 e inquesto capitolo nella sezione 3.4; per quanto riguarda le dierenze fra le due versioni delladst, si veda l'appendice A.

21

Page 30: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

void dct2D_C2C( ) ;void idct2D_C2C ( ) ;

void t ranspose ( ) ;

S c a l a r_ f i e l d& operator=(const Sca l a r_ f i e l d& z ) ;

friend class BC_dir ichlet ;friend class BC_periodic ;friend class BC;

;

class Complete_Scalar_fie ld : public Sca l a r_ f i e l d protected :

f loat ∗ array_rea l ;

public :Complete_Scalar_fie ld ( ) ;Complete_Scalar_fie ld ( int Nx, int Ny, bool _opt ) ;Complete_Scalar_fie ld ( const Complete_Scalar_fie ld& z ) ;

~Complete_Scalar_fie ld ( ) ;

void alloc_memory ( ) ;void set_val ( f loat ∗u) ;void s av e t oF i l e (char const ∗ f i l ename , const f loat Lx , const f loat Ly

) ;

void realTOcomplex ( f loat s c a l e ) ;void complexTOreal ( f loat s c a l e ) ;void i f f t sca led_C2R ( cu f f tHand le pland ) ;

f loat ∗ getReal ( ) ;Complete_Scalar_fie ld& operator=(const Complete_Scalar_fie ld& z ) ;

friend class BC_periodic ;friend class BC_dir ichlet ;friend class BC;

;

Dichiarazione delle classi Scalar_eld e Complete_Scalar_eld

Classe Vector_field

La classe Vector_field rappresenta un campo vettoriale denito su un do-minio rettangolare discretizzato di R2; essa in sostanza è costituita da duepuntatori ad istanze della classe Complete_Scalar_field. I suoi metodirichiamano i rispettivi metodi della classe Complete_Scalar_field per ognicomponente.

class Vector_f i e ld boost : : scoped_ptr<Complete_Scalar_fie ld>comp1 ;boost : : scoped_ptr<Complete_Scalar_fie ld>comp2 ;

void t ranspose ( ) ;

public :Vector_f i e ld ( Complete_Scalar_fie ld ∗ s1 , Complete_Scalar_fie ld ∗ s2 ) ;

22

Page 31: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

Vector_f i e ld ( ) ;Vector_f i e ld ( const Vector_f i e ld& z ) ;~Vector_f i e ld ( ) ;

Vector_f i e ld& operator=(const Vector_f i e ld& z ) ;

// ba s i c methods ( in genera l , they r e c a l l the correspondings c a l a r_ f i e l d methods

void set_val ( f loat ∗u , f loat ∗v ) ;void alloc_memory ( ) ;void fft_C2C ( cu f f tHand le plan ) ;void i f f t sca led_C2R ( cu f f tHand le plan ) ;void i f f t sca led_C2C ( cu f f tHand le plan ) ;void dst2D_C2C( ) ;void idst2D_C2C ( ) ;void dct2D_C2C( ) ;void idct2D_C2C ( ) ;void realTOcomplex ( f loat a ) ;void complexTOreal ( f loat a ) ;f loat ∗ getComp1 ( ) ;f loat ∗ getComp2 ( ) ;

void s a v e t oF i l eV f i e l d (char const ∗ f i l ename , const int Nxx , constint Nyy , const f loat Lx , const f loat Ly) ;

friend class BC;friend class BC_periodic ;friend class BC_dir ichlet ;

;

Dichiarazione della classe Vector_eld

Classe BC e derivate

Come abbiamo osservato nel capitolo 2 a scelte diverse delle condizioni alcontorno corrispondono algoritmi di risoluzione diversi. Essi sono stati im-plementati nelle due classi BC_dirichlet e BC_periodic, che ereditano dal-la classe astratta BC. L'interfaccia pubblica delle tre classi è praticamenteidentica; i metodi pubblici possono essere suddivisi tre categorie.

Metodi di inizializzazione: ne fanno parte i costruttori, il metodo BC::

initialize e i metodi startloop delle classi glie (che eseguono eventualioperazioni di inizializzazione aggiuntive non eettuate in BC::initialize).Essi inizializzano gli attributi delle classi. Il metodo BC::initParticles

inizializza la posizione delle particlelle per la visualizzazione.

Metodi di iterazione: BC::loop (virtuale, sovrascritto in ogni classeglia) è il metodo che viene chiamato dalla routine esterna di visualizzazionedisplay. Esso eettua l'aggiornamento dei dati calcolando un passo tem-porale dell'algoritmo di proiezione di Chorin-Temam. Il metodo virtualeBC::only_loop, sovrascritto anch'esso dalle classi glie, può essere usato ani di test impostando la ag test nel le di congurazione. Esso calcola la

23

Page 32: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

soluzione senza chiamare routine di visualizzazione e stampa a schermo lanorma L2 della velocità nale.I metodi loop delle classi BC_dirichlet e BC_periodic eseguono gli algo-ritmi descritti nel capitolo 2 e dettagliati nelle gure 2.1 e 2.2. Osserviamoche per ogni STEP dell'algoritmo di Chorin Temam le operazioni algebriche(somme, divisioni, etc.) vengono eseguite attraverso la chiamata a kernels,mentre le trasformate vengono calcolate sfruttando le routines della libreriaCufft, che non possono essere invocate da kernels.Nel caso di condizioni al contorno periodiche si eettuano unicamente unatrasformata all'inizio e una alla ne di ogni iterazione, per cui tutte le altreoperazioni sono eseguite da un unico kernel, invocato attraverso il metodoprivato BC_periodic::chorintemam.Nell'algoritmo sviluppato per risolvere il problema della cavità si eettuanodelle trasformate tra ogni STEP di Chorin-Temam, risulta quindi neces-sario invocare diversi kernels per ogni iterazione, ognuno dei quali esegue leoperazioni algebriche fra una trasformata e l'altra. Essi sono chiamati at-traverso i metodi privati chorintemamD1, chorintemamD2 e chorintemamD3

di BC_dirichlet.

Metodi di visualizzazione e altri: alcuni metodi della classe BC servonoa gestire le due modalità di visualizzazione supportate. Per una descrizionepiù dettagliata si veda la sezione 3.5. Osserviamo che a seconda della modal-ità di visualizzazione selezionata (gestita attraverso la variabile globale vis)a ogni iterazione viene invocato uno fra i metodi BC::advectparticles eBC::createpartvector. Per evitare di vericare il valore della variabileglobale vis a ogni iterazione, si sfrutta l'attributo BC::vis_function, che èun puntatore a un metodo di BC, e viene inizializzato al metodo corretto nelcostruttore di BC.Metodi accessori permettono di ritornare l'istante attuale della simulazione,di vericare se si è raggiunto il numero di iterazioni previsto e di salvare idati su un le alla ne della simulazione.Inne, il metodo BC::vortic permette di calcolare un campo di velocità apartire dall'espressione di un campo di vorticità assegnato. Il campo di ve-locità può essere impostato come velocità iniziale nel le config.txt, pereettuare dei test di funzionamento.

class BC protected :

const int Nxx ; // Number o f Fourier modes in x d i r e c t i on .const int Nyy ; // Number o f Fourier modes in y d i r e c t i on .const f loat Lx ; // Domain dimension in x d i r e c t i on .const f loat Ly ; // Domain dimension in y d i r e c t i on .const f loat nu ; // Vi s co s i t y .const f loat dt ; // Time s t ep .const f loat maxiter ; // Maximum number o f i t e r a t i o n s .boost : : scoped_ptr<Vector_f ie ld> f o r c e ; // Force vec to r f i e l d .boost : : scoped_ptr<Vector_f ie ld> ve l ; // Ve loc i t y vec to r f i e l d .

24

Page 33: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

boost : : scoped_ptr<Vector_f ie ld> nl ; // Non l i n e a r term u . grad (u) .boost : : scoped_ptr<Sca l a r_f i e l d> pre s s ; // Pressure s ca l a r f i e l d .f loat2 ∗ f i e l d ; // Handle to update p a r t i c l e s p o s i t i o n s .f loat s tep ; // Current time s t ep .cu f f tHand le plan ; // Handle f o r FFT in 2D.s i ze_t tPi tch ; // Optimizes 2D−arrays a l l o c a t i o n on GPU.bool plotend ; // I f t rue c r ea t e s GNU−p l o t graphs at .

//Member func t i on po in t e r to a member func t i on .void (BC: : ∗ v i s_funct ion ) ( f loat2 ∗) ;

void updateve l o c i ty ( ) ;void adv e c t p a r t i c l e s ( f loat2 ∗ p) ;void c r e a t epa r t v e c t o r ( f loat2 ∗ p) ;void non l inea r ( ) ;void vo r t i c ( f loat ∗ ux , f loat ∗uy ) ;void i n i t P a r t i c l e s ( f loat2 ∗p) ;f loat myrand (void ) ;void read_from_fi le ( std : : s t r i n g f i l ename , f loat ∗ f ) ;void L2norm ( ) ;

public :

BC( ) ;BC( int argc , char∗∗ argv , bool g_bOpenGLQA, int Nx, int Ny, f loat _lx ,

f loat _ly , f loat _nu, f loat _dt , f loat _maxiter , bool _plotend ,bool _opt ) ;

virtual ~BC( ) ;void i n i t i a l i z e (bool vort , s td : : s t r i n g f i lename_fx , std : : s t r i n g

f i lename_fy , std : : s t r i n g filename_vx , std : : s t r i n g fi lename_vy ) ;virtual void s t a r t l o op ( ) =0;virtual void loop ( struct cudaGraphicsResource ∗∗ vbo_resource ) =0;virtual void only_loop ( ) =0;virtual void pr intend ( ) =0;bool f i n i s h e d ( ) ;f loat get_stepnum ( ) ;

;

Dichiarazione della classe BC

class BC_periodic : public BC

void chorintemam ( ) ;

public :BC_periodic ( ) ;BC_periodic ( int argc , char∗∗ argv , bool g_bOpenGLQA, int Nx, int Ny,

f loat l l x , f loat l l y , f loat _nu, f loat _dt , f loat _tmax , bool_plotend , bool _opt ) ;

~BC_periodic ( ) ;void s t a r t l o op ( ) ;void loop ( struct cudaGraphicsResource ∗∗ vbo_resource ) ;void only_loop ( ) ;void pr intend ( ) ;

;

Dichiarazione della classe BC_periodic

class BC_dir ichlet : public BC

// in termed ia te v e l o c i t y f i e l d .

25

Page 34: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

boost : : scoped_ptr<Vector_f ie ld> u t i l d e ;// d ivergence o f the in termed ia te v e l o c i t y f i e l d .

boost : : scoped_ptr<Sca l a r_f i e l d> divut ;

void d i vu t i l d e ( ) ;void chorintemamD1 ( ) ;void chorintemamD2 ( ) ;void chorintemamD3 ( ) ;

public :BC_dir ichlet ( ) ;BC_dir ichlet ( int argc , char∗∗ argv , bool g_bOpenGLQA, int Nx, int Ny,

f loat l l x , f loat l l y , f loat _nu, f loat _dt , f loat _tmax , bool_plotend , bool _opt ) ;

~BC_dir ichlet ( ) ;void s t a r t l o op ( ) ;void loop ( struct cudaGraphicsResource ∗∗ vbo_resource ) ;void only_loop ( ) ;void pr intend ( ) ;

;

Dichiarazione della classe BC_dirichlet

Attributi: fra gli attributi più importanti di BC vi sono:

• istanze delle classi Vector_field, Scalar_field e Complete_Scalar_field (gestiti dinamicamente attraverso scoped pointers della libre-ria BOOST), corrispondenti alle grandezze siche in gioco nella simu-lazione;

• parametri sici (numero di gradi di libertà, viscosità, dimensioni deldominio, etc.)

• attributi per il controllo del numero di iterazioni (passo temporale,numero massimo di iterazioni, iterazione corrente);

• handlers per l'esecuzione delle trasformate con la libreria Cufft;

• l'array float2* field, allocato sul device come matrice 2D, che a ogniiterazione viene aggiornato con una chiamata a BC::updatevelocity()e contiene il valore della componente orizzontale della velocità nelcampo .x e quella verticale nel campo .y, e che viene utilizzato peraggiornare la visualizzazione.

La classe BC e le sue derivate hanno fra i loro attributi delle istanzedelle classi Vector_field, Scalar_field e Complete_Scalar_field. Persemplicare la chiamata dei kernels BC BC_dirichlet e BC_periodic sonodichiarate classi friend di Vector_field, Scalar_field e Complete_Scalar_field, in modo da avere completo accesso ai loro membri privati.

3.3.1 Alcune scelte stilistiche

L'organizzazione del codice cerca di rispondere a due esigenze contrapposte.Da un lato è necessario che il programma sia robusto e minimizzi possibili

26

Page 35: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

errori, regolamentando l'accesso ai dati. Si tratta di un'esigenza ancor piùstringente dal momento che per interagire con CUDA la struttura dati allabase del software è costituita da array dinamici C, un tipo di dati delicato dagestire e privo delle protezioni di strutture più evolute. La programmazionea oggetti rappresenta una risposta a tale esigenza. Dall'altro, l'interfacciaCUDA richiede un'impostazione procedurale del codice. La soluzione è stataconservare una parte del programma organizzato in classi e una costituitada funzioni C tradizionali.

Uno sforzo importante è stato dedicato al tentativo di salvaguardare ilpiù possibile l'integrità del codice rispetto a possibili interventi futuri. Allostesso tempo, si sono adottate soluzioni per facilitare eventuali aggiunte,come l'implementazione di nuovi algoritmi risolutivi. Da questo punto divista, si osservi che volendo implementare un nuovo algoritmo di risoluzione(ad esempio con delle condizioni al contorno di Neumann per la velocità)basterebbe:

• implementare una nuova classe BC_nuova che eredita dalla classe BC ene sovrascive i metodi virtuali;

• renderla friend delle classi di tipi di dati che utilizza (per es. Vector_field e Complete_Scalar_field);

• creare nel main un'istanza di tale classe.

Inoltre, per implementare tutti i rapporti di aggregazione fra una classee l'altra sono impiegati gli scoped_pointers della libreria BOOST (http://www.boost.org/). Questa scelta permette di coniugare i vantaggi di unattributo dinamico (come il puntatore a un oggetto) con una gestione sicurae automatizzata della memoria, permettendo allo stesso tempo alla classe dimantenere la proprietà esclusiva del proprio attributo.

3.4 Kernels

I kernels sono il cuore della programmazione CUDA. Essi possono essere invo-cati dall'host e eseguiti sulla GPU eseguendo in parallelo operazioni sui dati.Abbiamo già descritto nel capitolo 1 le principali caratteristiche dei kernels,come ogni kernel venga eseguito simultaneamente da numerosi thread orga-nizzati in blocchi e le problematiche legate all'accesso della memoria. In tuttii nostri kernels abbiamo fatto uso, ove possibile, della shared memory pergarantire accessi coalescenti alla memoria globale e velocizzare l'esecuzionedel codice. Come mostrato in [2, 3] il problema dell'ottimizzazione del codiceeseguito da un kernel è complesso.

Mostriamo nel seguito due versioni del kernel per il calcolo della traspostadi una matrice di cufftComplex: quella naive e quella ottimizzata propostain [3]. Nel programma la versione ottimizzata viene eseguita di default,

27

Page 36: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

mentre l'altra può essere eseguita scegliendo di disattivare l'ottimizzazionenel le config.txt.

__global__ void transp_NO_k(cufftComplex ∗ idata , cufftComplex ∗odata ,int width , int he ight )

int xIndex = blockIdx . x ∗ blockDim . x + threadIdx . x ;int yIndex = blockIdx . y ∗ blockDim . y + threadIdx . y ;

int index_in = xIndex + width ∗ yIndex ;int index_out = yIndex + he ight ∗ xIndex ;

odata [ index_out ] . x = idata [ index_in ] . x ;odata [ index_out ] . y = idata [ index_in ] . y ;

Transpose kernel: versione naive

__global__ void transp_k (cufftComplex ∗odata , cufftComplex ∗ idata ,int width , int he ight )

int TILE_DIM=blockDim . x ;int BLOCK_ROWS=blockDim . y ;

__shared__ cufftComplex t i l e [ block_size_x ] [ block_size_x +1] ;int blockIdx_x ;int blockIdx_y ;// d iagona l reorder ingi f ( width == he ight ) blockIdx_y = blockIdx . x ;blockIdx_x = ( blockIdx . x+blockIdx . y )%gridDim . x ;

else int bid = blockIdx . x + gridDim . x∗blockIdx . y ;blockIdx_y = bid%gridDim . y ;blockIdx_x = ( ( bid /gridDim . y )+blockIdx_y )%gridDim . x ;

int xIndex = blockIdx_x∗TILE_DIM + threadIdx . x ;int yIndex = blockIdx_y∗TILE_DIM + threadIdx . y ;int index_in = xIndex + ( yIndex ) ∗width ;xIndex = blockIdx_y∗TILE_DIM + threadIdx . x ;yIndex = blockIdx_x∗TILE_DIM + threadIdx . y ;int index_out = xIndex + ( yIndex ) ∗ he ight ;i f ( xIndex<Nx && yIndex < Ny) for ( int i =0; i<TILE_DIM; i+=BLOCK_ROWS)

t i l e [ threadIdx . y+i ] [ threadIdx . x ] =idata [ index_in+i ∗width ] ;

__syncthreads ( ) ;i f ( xIndex<Nx && yIndex < Ny)

for ( int i =0; i<TILE_DIM; i+=BLOCK_ROWS) odata [ index_out+i ∗ he ight ] . x =t i l e [ threadIdx . x ] [ threadIdx . y+i ] . x ;odata [ index_out+i ∗ he ight ] . y =t i l e [ threadIdx . x ] [ threadIdx . y+i ] . y ;

Transpose kernel: versione ottimizzata

28

Page 37: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

La versione ottimizzata del kernel tiene conto di problematiche di carat-tere generale, come l'uso della shared memory per la coalescenza e la risoluzionedei bank conicts, ma introduce anche aspetti migliorativi legati al proble-ma specico come la scomposizione della trasposizione o il diagonal block

reordening. Per maggiori dettagli si consulti [3].

Invocazione dei kernels. La chiamata a kernels avviene dall'host. Lachiamata a ogni kernel non viene eettuata direttamente ma attraverso unafunzione C denita appositamente. Questa soluzione è necessaria a causadella presenza di frammenti di codice C++ che richiedono l'utilizzo di duecompilatori, ed è suragata da numerosi esempi nel CUDA Toolkit. Per unadescrizione dettagliata di ogni kernel si veda la documentazione allegata alcodice. Per una descrizione dei kernels coinvolti nel calcolo delle trasformatediscrete si veda l'appendice A.

3.5 Funzioni e routines per la gestione della visual-

izzazione

Il software supporta due modalità di visualizzazione, mostrate in gura 3.2.La prima visualizza nella nestra una serie di particelle sospese nel uidoche si muovono seguendo il campo di velocità del uido. La seconda mostrasullo schermo delle frecce che rappresentano in ogni punto i vettori del campodi velocità. Altri graci possono essere creati alla ne della simulazionegrazie a GNUplot, ove richiesto dall'utente. La visualizzazione viene gestitaattraverso una serie di routines predenite di OpenGL descritte nella sezione1.8. I passi fondamentali per la visualizzazione sono:

1. inizializzazione dell'ambiente graco: avviene grazie alla funzioneinitGL, invocata all'inizio della simulazione dal costruttore della classeBC, e alla creazione del VBO in BC::initialize.

2. lancio del loop di visualizzazione: avviene nel main attraverso lachiamata a glutMainLoop, una funzione della libreria OpenGL;

3. aggiornamento della visualizzazione a ogni iterazione: è gestitadalla funzione display1 per la prima modalità di visualizzazione e dadisplay2 per la seconda. In queste funzioni:

• si verica se siamo giunti alla ne della simulazione invocando ilmetodo BC::finished;

• si aggiornano i dati sulla velocità invocando il metodo BC::loop,che eettua un'iterazione del metodo di Chorin Temam e aggiornail VBO;

• vengono invocate le routines OpenGL necessarie alla gestione del-la nestra;

29

Page 38: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

(a) Particelle

(b) Vettori

Figura 3.2: I due tipi di visualizzazione

• vengono calcolati i ops medi invocando la funzione computeFPS.

Per creare un Vertex Buer Object, è necessario associargli un array difloat2, i cui elementi rappresentano le coordinate dei vertici rappresentatida openGL attraverso delle primitive. Questo array viene inizializzato nelmetodo BC::initialize invocando il metodo privato BC::initParticles.

Vediamo nel dettaglio le due modalità di visualizzazione:

• modalità particles: qui ogni elemento dell'array di float2 rappre-senta le coordinate di una particella, che vengono aggiornate ad og-ni iterazione grazie al metodo BC::advectparticles. La funzionedisplay1 è invocata a ogni iterazione per la gestione della nestra,e per trasformare le coordinate dei vertici in punti con la primitivaGL_POINTS.

• modalità arrays: in questo caso il vettore di float2 deve averelunghezza doppia rispetto al numero di nodi della mesh. Infatti pertracciare una linea nel piano è necessario specicare le coordinate deidue punti iniziali e nali, che vengono uniti tramite la primitiva diopenGL GL_LINES. A ogni iterazione le coordinate dei punti vengonoaggiornate grazie al metodo BC::createpartvector.

30

Page 39: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

Passaggio di parametri alla routine display(): Le funzioni display1()e display2() non possono prendere argomenti; al loro interno è necessariochiamare metodi di un'istanza della classe BC_dirichlet o BC_periodic.A questo scopo è impiegato un puntatore globale a un oggetto di tipo BC,che al momento della creazione viene indirizzato all'istanza della classe gliaopportuna grazie al polimorsmo. Quindi in display1() e display2() perinvocare, ad esempio, il metodo loop(), si usa il comando:resolutor->loop();.

3.6 Lettura di parametri a runtime: il le config.txt

Nel le config.txt l'utente può settare una serie di parametri relativi allasimulazione che vengono letti durante l'esecuzione del programma, senzabisogno di ricompilare il codice ogni volta che si modicano dei parametri.Il contenuto del le viene poi letto nella funzione init_from_file graziealle routines messe a disposizione dalla libreria GetPot. GetPot permettedi gestire facilmente la lettura di dati da le o passati come parametri almomento dell'esecuzione del programma. Per maggiori informazioni si puòconsultare: http://getpot.sourceforge.net/.

−∗− f i l e c on f i g . txt −∗−

#s e t 1 i f you want to s t a r t the program in t e s t modet e s t = 1

[ parameters ][ . / phy s i c a l ]#v i s c o s i t ynu = 1e−3#time i n t e r v a ldt = 0.01#domain s i z eLx = 1 . fLy = 1 . f[ . . / ]

[ . / other ]#number o f t imes tepst imes teps = 1000#number o f Four i e r modesN = 128[ . . / ]

[ . . / ]

[ problem ]# 1 for pe r i od i c , 2 for d i r i c h l e tbc = 1# se t 1 to add a v o r t i c i t y f i e l dv o r t i c i t y = 0# OPTIMIZE CODE (Yes or No)opt = Yes

[ . . / ]

[ v i s u a l i z a t i o n ]# s e t 1 for b a l l s and 2 for vec t o r svis_type = 1

31

Page 40: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

# se t 1 to p l o t p r e s s i on and v e l o c i t y f i e l d using gnuplot at the endo f code

gnuplot = 1[ window ]width = 512he ight = 256[ . . / ]

[ . . / ]

[ d a t a_ f i l e s ][ . / f o r c e ]x =y =[ . . / ][ . / v e l o c i t y ]x =y =[ . . / ]

[ . . / ]

Contenuto del le config.txt

3.7 Librerie e plug-in esterni

Durante la fase di sviluppo del progetto ci siamo avvalsi dell'aiuto di alcunelibrerie esterne, tra cui alcune proprie di CUDA. In particolare abbiamosfruttato le potenzialità della libreria CUFFT per il calcolo delle FFT e dellalibreria GLUT di OpenGL per la visualizzazione realtime. Oltre a GetPot

per fare il parsing dei parametri di input, abbiamo utilizzato il generatoreautomatico di documentazione doxygen e l'utility di visualizzazione funzioniGnuplot. Nel seguito di analizzano in dettaglio questi supporti, mostrandoin che modo hanno contribuito alla realizzazione del nostro lavoro.

CUFFT

CUFFT è la libreria di NVIDIA che implementa la t (App. A), sfruttandoal massimo le capacità parallele della GPU.

L'ultima versione di questa libreria permette di calcolare trasformatedirette e inverse di valori reali e complessi in 1, 2 o 3D. Le trasformate 1Dsono limitate a vettori di 8 milioni di elementi, mentre quelle 2D e 3D possonoavere dino a 16384 elementi in ogni dimensione.

Si basa sul modello della FFTW (http://www.fftw.org), una delle li-brerie più ecienti e popolari su CPU, che contiene un semplice meccanismodi congurazione per l'esecuzione di una trasformata di una data dimen-sione e tipo. Questo approccio ha il vantaggio di denire un'unica volta lacongurazione ideale, chiamata plan, e sfruttarla per tutti i calcoli analoghi.

Questa libreria implementa diversi algoritmi di calcolo della t, aventidiversi livelli di accuratezza. Le migliori performance si ottengono trasfor-mando elementi le cui dimensioni siano:

32

Page 41: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

1. inferiori a quelle della memoria condivisa.

2. potenza di un singolo fattore (per esempio di 2).

Anche l'accuratezza dell'algoritmo di calcolo dipende da queste proprietà.Infatti per le trasformate che soddisfano il primo criterio ma non il secon-do, CUFFT usa un algoritmo generalizzato mixed-radix, più lento e menoaccurato. Per quelle che non soddisfano nessuno dei due criteri CUFFTè obbligato a salvare tutti i risultati intermedi nella memoria globale dellaGPU, riducendo le performance a causa della ridotta bandwidth di questamemoria.

Una nota importante rispetto al nostro lavoro riguarda la possibilità,accennata all'inizio del paragrafo, di calcolare trasformate di dati sia realisia complessi. In realtà CUFFT non implementa alcun algoritmo special-izzato per i valori reali, quindi non si ha nessun guadagno ad eettuaretrasformate di tipo complex-to-real. Per questo motivo, e per uniformarele notazioni, nel nostro codice preferiamo sempre le trasformate complex-to-complex, anche quando esse non sarebbero necessarie. Ciò permette,oltretutto, di inizializzare un solo plan.

Per ulteriori indicazioni sulle funzioni e i tipi deniti in questa libreria siveda la guida uciale [11].

GLUT

L'OpenGL Utility Toolkit è una libreria di utilities per scrivere programmidi computer graphics. É stata sviluppata da Mark J. Kilgard che ne detieneancora il copyright; al momento l'ultima versione disponibile è la 3.7.

Questa libreria caratterizzata da una completa portabilità tra sistemi op-erativi, motivo per cui, insieme alla sua semplicità, è molto utilizzato per lacreazione di programmi OpenGL di piccole e medie dimensioni. Infatti nonsi tratta di un toolkit completo e non può essere sfruttato per applicazionipiù avanzate. La sua funzione principale è quella di semplicare l'I/O con ilsistema operativo, gestendo la creazione e il controllo delle nestre, e moni-torando l'input da mouse e tastiera. Include anche alcune semplici routinesper disegnare semplici forme geometriche, come cubi e sfere.

Tutte le funzioni di GLUT iniziano con il presso glut, rendendole cosìfacilmente riconoscibili. La funzione principale chiamata da questa libre-ria è void glutMainLoop, con la quale si entra nel ciclo di eventi OpenGL,attraverso il quale si inizializza la nestra graca e si inizia il processo di visu-alizzazione in real time. Al momento dell'inizializzazione si denisce una fun-zione display, associata al mainloop tramite il comando glutDisplayFunc

(display), insieme ad alcune funzioni accessorie che gestiscono gli input datastiera o mouse glutKeyboardFunc, glutReshapeFunc. Il ciclo di GLUTchiama in maniera iterativa (cioè, come un vero e proprio ciclo ma senza

33

Page 42: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 3 Aspetti implementativi

bisogno di un comando for) la funzione di display, nché l'utente non escedal programma.

É necessario specicare che glutMainLoop nella sua versione originalepuò essere chiamato un'unica volta in un programma, visto che non ritornamai. L'estensione freeglut, che abbiamo incluso nel nostro lavoro, per-mette di risolvere questo problema e tornare nel main prima di uscire dalprogramma. Come CUFFT, anche la libreria GLUT è presente nel ToolkitNvidia.

Un'altra libreria inclusa nel codice è la GLEW (OpenGL ExtensionWran-gler Library), che serve da supporto per scrivere piccole estensioni a OpenGL(Come per esempio rendercheck_GL).

Doxygen

Doxygen è un generatore automatico di documentazione per diversi linguag-gi di programmazione, fra cui C, C++, Fortran, Java, etc. Esso permettedi generare documentazione per un software in diversi formati (html, rtf,man3, LaTeX, etc.) a partire da commenti presenti nel codice, opportu-namente formattati attraverso opportune tag. Questi tag ci permettono difar risaltare nella documentazione diverse informazioni, tipo il nome di unacerta funzione, l'autore, la data di creazione o modica, i parametri, il lesorgente di appartenenza o più semplicemente un breve commento o nota.Inoltre è possibile creare automaticamente dei riferimenti dal commento suuna certa parte di codice al codice stesso, facilitandone la presa in mano. Ab-biamo utilizzato Doxygen per creare un reference manual con le istruzioniper l'installazione e la descrizione dettagliata di metodi, kernels, funzioni,etc. L'utente dovrebbe essere in grado di far funzionare autonomamente ilsoftware con le informazioni in esso contenute, con a disposizione il presentedocumento per approfondimenti.

Gnuplot

Gnuplot è un programma che genera graci di funzioni in 2 e 3 dimen-sioni, graci di dati, etc., eseguibile sulla maggior parte dei sistemi operativi(GNU/Linux, Unix, Microsoft Windows, Mac OS X e al.). Gnuplot puògenerare un output direttamente sullo schermo oppure creare e salvare lesin formati graci, fra cui PNG, EPS, SVG, JPEG e molti altri. Esso può es-sere usato sia in modo interattivo, sia (come nel quadro di questo progetto) inbatcch mode, osando degli scrript. Attivando un'opzione nel le config.txt,il nostro software salva automaticamente i dati relativi al campo di velocitàa quello di pressione all'istante nale e ne crea i graci con Gnuplot.

34

Page 43: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 4Risultati numerici

Il nostro software è stato testato su due macchine, entrambe con sistemaoperativo Linux:

• un laptop HP con 2 processori CPU Intel5 R© CoreTM2 Duo T8100 a2.10 GHz, 3 GB di RAM e GPU Nvidia Nvidia GeForce 8400M GS,con compute capability 1.1; OS Linux Ubuntu 10.10 a 32 bit.

• il server VEIO del laboratiorio di calcolo del Mox, al Politecnico diMilano, equipaggiato con due processori Nvidia Tesla C1060 (computecapability 1.3), 4 processori Intel ia64 a 64bit Xeon 5520 quad core a2.27 GHz, e un totale di 12 GB di RAM; OS Scientic Linux SL release5.3 (Boron) a 64 bit.

In entrambe le macchine è stato installata la versione 3.2 di CUDA. Neitest eettuati con VEIO è stata impiegata una sola GPU.

4.1 Settaggio parametri

Nel le config.txt è possibile modicare i parametri sici della simulazione.Le prove eettuate hanno messo in evidenza la presenza di alcune condizionirestrittive per i parametri sici, in particolare:

• la simulazione con condizioni al contorno periodiche è convergente pertutti i valori testati dei parametri sici;

• il settaggio dei parametri per condizioni al bordo periodiche sembradover rispettare una condizione di tipo CFL: la simulazione divergeper tempi niti se il passo temporale dt è troppo grande rispetto alpasso di griglia max(Lx/N,Ly/N).

Vi è poi una questione legata all'implementazione su GPU: se si selezionaun numero di gradi di libertà troppo grande, l'esecuzione del codice restitu-isce un Segmentation fault, oppure si blocca. Su Laptop questo problema

35

Page 44: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 4 Risultati numerici

TEST 1 TEST 2 TEST 3 TEST 4 TEST 5

CB Dirichlet Dirichlet Periodiche Periodiche Periodiche

ν 10−3 10−3 10−3 10−3 10−3

dt 10−2 10−3 10−2 10−2 10−2

timesteps 103 103 103 103 103

N 64 32 32 64 128Lx 1 0.5 1 1 1Ly 1 0.5 1 1 1

Tabella 4.1: Parametri per i test eettuati.

si presenta con N > 512 gradi di libertà (per ciascuna dimensione) per con-dizioni al contorno periodiche, N > 128 per condizioni di Dirichlet. Pensi-amo che questo fenomeno sia legato a problemi di overow della global mem-ory della GPU, e che si tratti quindi di un limite intrinseco dell'hardware.1

4.2 Test eettuati

Un primo test è stato eettuato per vericare rigorosamente la stabilitàdel metodo confrontando la norma L2 della velocità calcolata dopo un datonumero di iterazioni al variare diN . In seguito, sono state eettuate due seriedi test comparativi. Una prima serie di test comparativi è stata eettuataper confrontare qualitativamente le performances del nostro solutore rispettoa quelle di un solutore con lo stesso algoritmo implementato in Matlab.Una seconda serie di test comparativi è stata eettuata per confrontare leperformances dello stesso solutore su due macchine diverse. In entrambi icasi sono state eettuate diverse prove variando il numero di gradi di libertàN2.

Per tutti i test, sono state imposte condizioni iniziali nulle per la ve-locità. Per i test con condizioni al bordo di Dirichlet, si è imposta unaforzante Fx = 1 ∀(x, y) : 0.9375 ≤ y ≤ 1;Fx = 0 altrimenti; Fy ≡ 0.Per i test con condizioni al bordo periodiche, si è imposta una forzanteFx = 0.05 ∀(x, y) : 0.9375 ≤ y ≤ 1;Fx = 0 altrimenti; Fy ≡ 0. Gli altriparametri sono dettagliati in Tabella 4.1.

4.2.1 Test di convergenza

Questo test è stato eettuato con condizioni al contorno periodiche, chepermettono confronti fra un maggior numero di valori di N . Abbiamo con-frontato la norma L2 della velocità calcolata dopo un numero sso di iter-

1Questa ipotesi è suragata dai test eettuati con VEIO, su cui è possibile eettuaresenza problemi simulazioni con condizioni di Dirichlet anche con N ≥ 256

36

Page 45: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 4 Risultati numerici

TEST norma L2

CB Periodiche

ν 10−3

dt 10−2

Lx 1Ly 1

Tabella 4.2: Parametri per i test eettuati.

azioni. I parametri per questo test sono riassunti in Tabella 4.2. La veloc-ità iniziale è stata imposta nulla, e si è introdotta una forzante stazionariaF : Fy ≡ 0;Fx = 0.5∀ (x, y)t.c.0.875 ≤ y ≤ 1.

In Figura 4.1 è mostrata la convergenza della norma L2 della componenteorizzontale della velocità calcolata dopo 100 e dopo 500 iterazioni. Si osservachiaramente la convergenza della norma al ranamento del passo di griglia.

Figura 4.1: Norma L2 della velocità orizzontale dopo 100 iterazioni(sin.) edopo 500 iterazioni (dx.).

4.2.2 Confronto CUDA vs Matlab

In questa prima serie di test abbiamo confrontato i tempi di esecuzione delnostro software sul Laptop con quelli dello stesso algoritmo di risoluzioneimplementato in Matlab. Abbiamo eettuato i test sia attivando sia disat-tivando la visualizzazione. In Matlab viene visualizzata solo il modulo dellavelocità, in Offf è stata selezionata la modalità Particles.

I risultati sono riportati nella Tabella 4.3. Dall'esame di tali risultatiemergono alcune osservazioni:

• il tempo di esecuzione di Offf utilizzando la visualizzazione è notevol-mente inferiore per tutti i test;

• il tempo impiegato a eseguire il calcolo senza visualizzazione è minoreper Offf nei test con condizioni al contorno periodiche, mentre risultapiù lento con condizioni al bordo di Dirichlet.

37

Page 46: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 4 Risultati numerici

TEST 1 TEST 2 TEST 3 TEST 4 TEST 5

Matlab - no vis. 11.54 4.05 1.42 4.64 24.69CUDA - no vis. 15.24 7.72 0.83 1.44 3.62Matlab - vis. 95.59 71.71 85.84 116.28 208.11CUDA - vis. 29.40 21.24 8.06 12.69 16.21

Tabella 4.3: Matlab vs Offf: Confronto tempi di esecuzione (in secondi).

Se da un lato l'algoritmo con condizioni periodiche benecia notevolmentedella parallelizzazione, l'algoritmo risolutivo con condizioni al bordo di Dirich-let implementato con CUDA sembra essere meno performante dello stessoalgoritmo fatto girare su CPU. Questo risultato sperimentale è dovuto es-senzialmente alla perdita di prestazioni della FFT di Cufft in presenza diarray con lunghezza diversa da potenze intere di 2, necessarie per il calcolodella dst. Approfondiamo questa questione nella sezione 4.3. (Per ulterioridettagli sul calcolo della dst si veda la sezione A.)

4.2.3 Confronto su GPU diverse

Dei test comparativi sono stati eettuati anche facendo girare Offf su duemacchine dierenti. Osserviamo che il modo più rigoroso di eettuare questotipo di test sarebbe confrontare le prestazioni della stessa macchina montan-do due GPU diverse, ma non è stato possibile eettuare tale tipo di test perquestioni pratiche. Nella Tabella 4.4 riportiamo i risultati delle simulazionieseguite sul Laptop e su VEIO. (Per questa serie di test le prestazioni sonostimate misurando gli FPS, Floating point operations Per Second.) Osservi-amo che le GPU delle due macchine hanno diverse compute capability : inparticolare questo implica che la GPU montata su VEIO ottimizzi in modoautomatico la gestione degli accessi alla global memory della GPU.

I risultati delle misurazioni mostrano una evidente dierenza di prestazionia favore di VEIO, ma mostrano anche un altro interessante fenomeno. Siosservi ad esempio la dierenza di prestazioni fra il TEST 1 (griglia conN2 = 4096 gradi di libertà) e il TEST 2 (griglia con N2 = 1024 gradi dilibertà). La scalabilità sembra seguire due comportamenti radicalmente dif-ferenti nelle due GPU; in particolare per un numero non abbastanza elevatodi gradi di libertà la GPU di VEIO sembra gestire poco ecacemente loscheduling delle attività da eseguire. La GPU del Laptop non sembra es-

TEST 1 TEST 2 TEST 3 TEST 4 TEST 5

Laptop 51 100 466 380 194VEIO 317 343 2319 2509 1162

Tabella 4.4: Laptop vs VEIO: Confronto prestazioni (in FPS).

38

Page 47: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 4 Risultati numerici

Figura 4.2: Screenshot dell'output del CUDA proler.

sere aetta dallo stesso comportamento. Un fenomeno analogo si riscontraconfrontando i risultati del TEST 3 e del TEST 4.

4.3 Il proler

Il proler messo a disposizione da CUDA all'interno del Toolkit permettedi raccogliere informazioni riguardanti il tempo di esecuzione di ogni kernele la gestione della memoria della GPU. In particolare può essere usato peridenticare dei bottlenecks o per quanticare il numero di accessi non coales-centi in ogni kernel. Per aprire il proler basta scrivere le seguenti linee dicomando su un terminale:export LD_LIBRARY_PATH=LD_LIBRARY_PATH:/usr/local/cuda/computeprof/bin

/usr/local/cuda/computeprof/bin/computeprof &

In gura 4.2 si può avere un'idea di come si presenti il proler all'utente.Si possono ritrovare informazioni dettagliate sull'esecuzione di ogni copia dimemoria, kernel o altra funzione propria di CUDA (come le t). In parti-colare è indicato il tempo-GPU impiegato per ogni chiamata, la dimensionedi block e grid, e il numero di accessi (sia in lettura, sia in scrittura) noncoalescenti.

Quest'ultima informazione è stata particolarmente utile nello sviluppodi Offf, in quanto ci ha permesso di ottimizzare le performance di alcunikernel in cui l'accesso alla memoria non era completamente coalescente. Peresempio nell'istogramma in gura 4.3 si confronta il numero di accessi noncoalescenti in alcune iterazioni della versione diOfff con condizioni al bordoperiodiche. Le barre di colore verde corrispondono alla versione non ottimiz-

39

Page 48: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 4 Risultati numerici

Figura 4.3: OFFF periodico: non coalescente vs coalescente

zata: sia il kernel che calcola il termine non lineare non_linear_k, sia quelloche trasforma array complessi in reali complex2real_k hanno un numerodi accessi non coalescenti molto importante. Attraverso un uso coscienziosodella shared memory, abbiamo ridotto del 100% le letture non coalescenti. 2

Una volta ottimizzato il codice, si può sfruttare un'altra funzionalità delproler per analizzare il tempo-GPU impiegato da ogni funzione. Per es-empio in gura 4.4 è riportato il tempo-GPU necessario ad inizializzare unoggetto della classe BC_periodic e ad eettuare il primo timestep. Il gracosegue l'evoluzione temporale, ed ad ogni barra corrisponde una chiamata aduna funzione del device. Nella parte sinistra si può riconoscere l'inizializ-zazione dei parametri dell'oggetto, con alcune copie hostTodevice e il calcolodelle trasformate di Fourier (che si possono riconoscere dalle sigle fft o sp).In seguito si riconoscono, nell'ordine, i kernel invocati dal metodo loop.

Come anticipato nella sezione 4.2, l'algoritmo risolutivo con condizioni albordo di Dirichlet non è altrettanto performante quanto quello con condizionial bordo periodiche. La spiegazione è da ritrovare proprio nella chiamata allaFFT implementata nella libreria cut, che non è ottimizzata per vettori conun numero di elementi non multiplo di un intero. Questo fatto è esempli-

2Si noti che il kernel non ottimizzato, non_linear_NO_k è stato mantenuto nel codicea titolo di esempio, sebbene non venga mai chiamato.

Figura 4.4: GPU-time per inizializzare BC_periodic

40

Page 49: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 4 Risultati numerici

cato molto chiaramente nella gura 4.5, in cui si vede che il 70% del tempoGPU viene utilizzato per calcolare trasformate di Fourier. Auspicabilmente,con schede grache più moderne con una compute capability superiore, nonsi dovrebbero più avere problemi di questo tipo derivanti da accessi noncoalescenti.

Figura 4.5: BC_dirichlet : utilizzazione della GPU

4.4 Conclusioni. Prospettive

I test eettuati hanno permesso di mettere in luce pregi e limiti del simu-latore. Da un lato, l'algoritmo risolutivo con condizioni al bordo periodichesi è dimostrato robusto e performante; inoltre, esso benecia di un elevatospeed-up grazie alla parallelizzazione.Dall'altro lato, l'algoritmo con condizioni al bordo di Dirichlet sembra pre-sentare buone potenzialità ma ha anche alcuni limiti. Innanzi tutto vi sonocondizioni restrittive sui parametri sici per garantire la convergenza del-l'algoritmo di proiezione. Inoltre, la mancanza di librerie specializzate pereseguire in CUDA trasformate discrete di seni e coseni ci ha indotto a im-plementarle sfruttando la FFT della libreria Cufft. Oltre ai difetti legatia un'implementazione fai da te in mancanza di una libreria specializzata,anche l'approccio in sè sembra presentare alcuni limiti, come evidenziatonelle sezioni precedenti. Un passo avanti importante sarebbe sicuramenteintrodurre implementazioni ottimizzate della dct e della dst. Un altro limitedell'algoritmo risolutivo è costituito dal numero elevato di cambiamenti dispazi funzionali (cf la gura 2.2) che impongono di eettuare molte trasfor-mate a ogni iterazione. Questo è forse il motivo per cui questo tipo dialgoritmo risolutivo non è molto diuso in letteratura. Metodi più spessoutilizzati (per esempio in [12]) si basano su algoritmi a passi frazionari, con

41

Page 50: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Capitolo 4 Risultati numerici

risoluzione dei sistemi lineari associati ai problemi di laplaciano con metodicome Jacobi. Una soluzione molto elegante per imporre condizioni di Neu-mann sulla velocità e sfruttare la FFT è proposta in [10].Osserviamo che altre interessanti prospettive sono oerte dalla possibilità cheore la simulazione real time di interagire col sistema sico mentre la simu-lazione è in corso (un simpatico esempio è costituito dal programma FluidGLdistribuito fra i samples del CUDA SDK). Integrare al nostro simulatoreroutines per aumentare l'interattività sarebbe un passo avanti naturale.

42

Page 51: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Conclusione

In questo documento abbiamo descritto l'implementazione di un software perla risoluzione di equazioni di Navier-Stokes basato sulla tecnologia CUDA peril calcolo parallelo su GPU. Sono stati implementati due algoritmi risolutivi,corrispondenti a tipi diversi di condizioni al bordo.

La realizzazione di questo software è stata possibile grazie a un percorsocomplesso, diverso da quello della tradizionale programmazione C++. Frale tappe principali di tale percorso vi sono: la familiarizzazione con unatecnologia nuova e in costante evoluzione; un'attenta rilettura degli algoritmirisolutivi per adattarli alla parallelizzazione; un'implementazione per rendereil codice essibile nonostante le limitazioni imposte da CUDA; un attentoprocesso di ottimizzazione del codice grazie a test numerici e proling.

I test numerici hanno mostrato che l'uso di questa tecnologia sembraessere una pista promettente per il calcolo numerico. Alcuni limiti emer-si nel corso della sperimentazione sembrano essere superabili con ulterioriapprofondimenti.

Speriamo che questo lavoro risulti utile a tutti quelli che intendono avven-turarsi nel mondo del GPGPU, e auguriamo loro buona fortuna.

43

Page 52: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali
Page 53: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Appendice AFFT, DCT e DST

Il lettore potrebbe non essere familiare con le denizioni delle trasformate discretein coseni e in seni. L'esistenza di 8 varianti di ciascuna di queste trasformate,ognuna con denizioni leggermente diverse, ci spinge a dettagliare per chiarezzaquale abbiamo implementato.

La mattonella principale per calcolare DCT e DST è la trasformata discreta diFourier, il cui calcolo eciente (FFT) è implementato in CUDA nella libreria Cut(si veda la sezione corrispondente). A tutt'oggi non ci è giunta notizia di librerie cheimplementino in CUDA trasformate discrete di seni e coseni. All'implementazionedi queste trasformate mediante moltiplicazione matriciale si è preferito calcolarlesfruttando le routine presenti nella libreria Cut. 1

FFT

La FFT (Fast Fourier Transform) è un algoritmo sviluppato da Cooley e Tuckernel 1965 che permette di ridurre la complessità computazionale della trasformatadiscreta di Fourier (DFT) Y = [y0, . . . , yN−1] di un vettore X = [x0, . . . , xN−1],data da:

yn =1

N

N−1∑k=0

xke−2πikn/N , n = 0, . . . , N − 1. (A.1)

La DFT ha infatti una complessità di O(N2), in quanto necessita N − 1 moltipli-cazioni e N − 1 somme. Al contrario la FFT richiede solo O(N log(N)) operazioni,riducendo notevolmente il tempo di calcolo per N grande. Per ottenere questo risul-tato l'algoritmo di Cooley-Tukey fattorizza N con un approccio divide-et-impera esfrutta la simmetria tra numeri complessi coniugati. Esistono molti altri algoritmiche implementano le FFT, specializzati per vari tipi di dati in input; si veda, peresempio, [13].

1Osserviamo che lo stesso approccio, ossia calcolare trasformate in seni e coseni tramiteFFT, è seguito anche nell'implementazione di dct e dst eettuata in Matlab.

45

Page 54: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Appendice A FFT, DCT e DST

DCT e IDCT

Fra le dierenti varianti della dct, abbiamo scelto quella di tipo II, grazie al fattoche essa può essere facilmente calcolata mediante la FFT. Dato un vettore realeX = [x0, x1, . . . , xn−1] , la sua dct è data dal vettore Y = [y0, y1, . . . , yn−1] tale che:

yk = wk

N−1∑n=0

xn cos

N

(n+

1

2

)k

]k = 0, . . . , N − 1. (A.2)

w0 =1√N, wk =

√2

N, per k = 1, 2, . . . , N − 1. (A.3)

(I coecienti wk servono per rendere la matrice corrispondente ortogonale.)In pratica i componenti di (A.2) possono essere calcolati usando la FFT, come

spiegato da Jain in [14]. Sia X = [x0, x1, . . . , xN−1] il vettore N-dimensionale (conN pari) di cui si vuole calcolare la trasformata, disponiamo le sue componenti nelseguente modo:

X = [x0, x2, x4, . . . , xN−2, xN−1, xN−3, . . . , x3, x1]

Assegnato il vettore W dei pesi, con

wk =

2e−ikπ2N

√N

, k 6= 0

1√N, k = 0

(A.4)

si ottiene:Y = DCT(X) = W · FFT(X) (A.5)

Osserviamo che la dct di un vettore reale è reale. Inoltre, se supponiamo che lecomponenti di X siano il campionamento di una funzione reale continua f su unagriglia equispaziata di N nodi, allora le componenti di Y sono (a meno di unacostante) i coecienti del polinomio trigonometrico di soli coseni

f(x) =

N−1∑k=0

wkykcos

(πkx

N

), con w0 =

1√N,wk,k 6=0 =

√2

N

che interpola f nei punti dell griglia.Il calcolo della trasformata inversa segue una strada molto simile a quella di-

retta. La DCT-III, cioè l'inversa di (A.2) di un vettore reale Y = [y0, y1, . . . , yn−1]è il vettore reale X , le cui componenti sono date da:

xn =

N−1∑k=0

wkyk cos

N

(n+

1

2

)k

]n = 0, . . . , N − 1. (A.6)

wk =1√N, k = 0 wk =

√2

N, altrimenti

Anche qui i coecienti wk servono per rendere la matrice corrispondente ortogonale;inoltre questa matrice è la trasposta di quella della DCT-II.

46

Page 55: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Appendice A FFT, DCT e DST

Seguendo ancora l'algoritmo proposto in [14], sfruttiamo la IFFT (Fast FourierTransform inversa) per calcolare (A.6).

Riordiniamo dapprima gli elementi di Y nel seguente modo:

IDCT(y) = y[a0, an−1, a1, . . . , an/2+1, an/2−1, an/2]. (A.7)

consideriamo i pesi:

wk =

2ne−ikπ2n

√2n

, k 6= 0

√n, k = 0

(A.8)

e sia y = IFFT(wk · y) .

Dall'1D al 2D Si noti che le trasformate in (A.2) e (A.6) calcolano la trasfor-mata 1D delle colonne della matrice in ingresso. Per calcolare la trasformata di unarray bidimensionale è suciente applicare due volte quella monodimensionale, nelseguente modo:

Y = DCT2D(X) = DCT((DCT(X))T )T (A.9)

Y = IDCT2D(X) = IDCT((IDCT(X))T )T (A.10)

Implementazione

Il nostro codice permette di calcolare la dct di array bidimensionali.2

La classe Scalar_field ha come attributo un array di cufftComplex di nomearray_complex di dimensioneNx×Ny (tipo derivato in CUDA, in cui ogni elementoha due campi per rappresentare un generico numero complesso) che memorizza ivalori di un campo scalare in ogni punto di una griglia rettangolare. Per calcolarela sua dct, si è impiegato il metodo descritto sopra:

• la dct in 2 dimensioni è data dall'applicazione alternata di trasformate monodi-mensionali e di trasposizioni;

• la dct monodimensionale è ottenuta riordinando opportunamente i valori di(ogni riga di) array_complex, eettuando una FFT e moltiplicando ognielemento per un peso opportuno.

Di seguito riportiamo il codice dei metodi e dei kernels che intervengono nel calcolodella dct bidimensionale. Il calcolo della trasformata inversa segue un percorso deltutto analogo. (Per brevità omettiamo la denizione delle funzioni C che chiamanoi rispettivi kernels.)

void Sca l a r_ f i e l d : : dct2D_C2C( ) dct1D_C2C( ) ;t ranspose ( ) ;dct1D_C2C( ) ;t ranspose ( ) ;dctwe ights (1 ) ;

2Osserviamo che nel seguito si commette un piccolo abuso di linguaggio: il codicenon calcola i coecienti delle trasformate discrete di seni e coseni in senso stretto, madirettamente i coecienti dei polinomi trigonometrici interpolanti corrispondenti; taleabuso è giusticato dal fatto che, come osservato sopra, si tratta della stessa cosa a menodell'applicazione di pesi opportuni.

47

Page 56: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Appendice A FFT, DCT e DST

;

void Sca l a r_ f i e l d : : dct1D_C2C( ) cu f f tHand le p landct ;cu f f tP lan1d ( &plandct , Nxx , CUFFT_C2C,Nyy) ;

// arrange vec to r e lements as necessarypredct ( array_complex ,Nxx ,Nyy) ;

// c a l c u l a t e f f tcufftExecC2C ( plandct , array_complex , array_complex ,CUFFT_FORWARD) ;

// and mu l t i p l y by proper c o e f f i c i e n t spostdct ( array_complex ,Nxx ,Nyy) ;cu f f tDe s t r oy ( plandct ) ;

;

// ke rne l s

__global__ void predct_k (cufftComplex ∗ in , const int Nxx , const intNyy)

// compute idx and idy , the l o c a t i on o f the element in the o r i g i n a lNxN array

int idx = blockIdx . x∗blockDim . x+threadIdx . x ;int idy = blockIdx . y∗blockDim . y+threadIdx . y ;int index = idx + idy ∗Nxx ;

f loat k ;k=in [ index ] . x ;__syncthreads ( ) ;

// rearrange vec to r e lementsi f ( idx < Nxx && idy <Nyy)

i f ( index%2==0)

in [ idx/2+Nxx∗ idy ] . x=k ;in [ idx/2+Nxx∗ idy ] . y=0;

else

in [Nyy−( idx+1)/2+Nxx∗ idy ] . x=k ;in [Nyy−( idx+1)/2+Nxx∗ idy ] . y=0;

__global__ void postdct_k (cufftComplex ∗ in , const int Nxx , const intNyy)

// compute idx and idy , the l o c a t i on o f the element in the o r i g i n a l

NxN arrayint idx = blockIdx . x∗blockDim . x+threadIdx . x ;int idy = blockIdx . y∗blockDim . y+threadIdx . y ;int index = idx + idy ∗Nxx ;

cufftComplex k ;k . x=in [ index ] . x ;k . y=in [ index ] . y ;__syncthreads ( ) ;

48

Page 57: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Appendice A FFT, DCT e DST

f loat radN=sq r t ( f loat (Nxx) ) ;

f loat ccos=__cosf(− f loat ( idx ) ∗FM_PI/(2∗Nxx) ) ;f loat s s i n=__sinf(− f loat ( idx ) ∗FM_PI/(2∗Nxx) ) ;// r ea l and imaginary c o e f f i c i e n t sf loat c r e a l=sq r t ( 2 . f ) ∗ ccos /radN ;f loat cimag=sq r t ( 2 . f ) ∗ s s i n /radN ;//mu l t i p l y f o r a weight arrayi f ( idx==0)

in [ index ] . x=k . x/radN ;in [ index ] . y=0;

i f ( idx>0 && idx < Nxx && idy <Nyy)

in [ index ] . x=c r e a l ∗k . x−cimag∗k . y ;in [ index ] . y=0;

// ques to ke rne l e ' invocato da l metodo Sca l a r_ f i e l d : : d c twe i gh t s ( i n t i )__global__ void postdctWeights_k (cufftComplex∗ in , const int i )

int idx = blockIdx . x∗blockDim . x+threadIdx . x ;int idy = blockIdx . y∗blockDim . y+threadIdx . y ;int index = idx + idy ∗Nx;

f loat radNN= 1 . f /( sq r t ( ( f loat ) (Nx∗Ny) ) ) ;f loat rad2=sq r t ( 2 . f ) ;

// DCTi f ( i==1) i f ( idx==0 | | idy==0)

in [ index ] . x=in [ index ] . x∗ rad2∗radNN ;in [ index ] . y=in [ index ] . y∗ rad2∗radNN ;

i f ( idx<Nx && idx >0 && idy < Ny && idy >0)

in [ index ] . x=in [ index ] . x ∗2 . f ∗radNN ;in [ index ] . y=in [ index ] . y ∗2 . f ∗radNN ;

// IDCTi f ( i==2)

i f ( idx==0 | | idy==0)

in [ index ] . x=in [ index ] . x /( rad2∗radNN) ;in [ index ] . y=in [ index ] . y /( rad2∗radNN) ;

i f ( idx<Nx && idx >0 && idy < Ny && idy >0)

in [ index ] . x=in [ index ] . x / ( 2 . f ∗radNN) ;in [ index ] . y=in [ index ] . y / ( 2 . f ∗radNN) ;

Metodi e kernels per il calcolo della dct

49

Page 58: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Appendice A FFT, DCT e DST

Si osservi che il kernel postdctWeights_k(cufftComplex* in, const int

i), invocato dal metodo corrispondente di Scalar_field, viene invocato ponendoi=1 per calcolare i pesi della trasformata diretta, con i=2 per applicare quelli dellatrasformata inversa.

DST e IDST

La variante più utilizzata della dst è quella di tipo I, e può essere anch'essa facil-mente calcolata mediante la FFT. Dato un vettore reale X = [x0, x1, . . . , xn−1] , lasua dst è data dal vettore Y = [y0, y1, . . . , yn−1] tale che:

yk =

N−1∑n=0

xn sin

N + 1(n+ 1)(k + 1)

]k = 0, . . . , N − 1. (A.11)

In pratica, come nel caso della dct, esiste un algoritmo eciente per calcolareY sfruttando le FFT. Sia infatti X il vettore dispari ottenuto raddoppiando einvertendo quello di partenza:

X = [0, x0, . . . , xN−1, 0,−xN−1, . . . ,−x0] (A.12)

e Y la sua dft: Y = FFT(X).Allora la trasformata di soli seni è data dalla parte immaginaria delle compo-

nenti dalla seconda alla N-esima:

Y = DST(X) = −i · [Y1, . . . , YN ] (A.13)

Si noti che anche la dst di un vettore reale è reale. E, analogamente a quanto dettoa riguardo della dct, la dst di un vettore con N elementi permette di determinareil polinomio interpolante (di soli seni) di una funzione reale campionata in N puntiequispaziati: il polinomio trigonometrico che approssima una funzione f continuasu N nodi è dato da

f(x) =

N−1∑k=0

2

Nyksin

(πkx

N

),

dove Y = [y0, y1, . . . , yn−1] è la dst di è la trasformata del vettoreX = [f(x0), f(x1), . . . , f(xn−1)]dei valori di f in N nodi equispaziati.

La trasformata inversa di seni di un vettore Y = [y0, y1, . . . , yn−1] è data dallasua DST-I moltiplicata per un coeciente:

xk =2

N + 1

N−1∑n=0

yn sin

N + 1(n+ 1)(k + 1)

]k = 0, . . . , N − 1. (A.14)

Quindi si ha:

X = IDST(Y ) =2

N + 1DST(Y ). (A.15)

Si noti che le trasformate in (A.11) calcolano la trasformata 1D delle colonnedella matrice in ingresso. Analogamente a quanto detto per la trasformata di coseni,

50

Page 59: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Appendice A FFT, DCT e DST

per calcolare la trasformata diretta di seni di un array bidimensionale è sucienteapplicare due volte quella monodimensionale:

Y = DST2D(X) = DST((DST(X))T )T . (A.16)

Per la trasformata inversa bidimensionale, vale una formula simile a (A.14) :

Y = IDST2D(X) =4

(N + 1)2DST2D(X). (A.17)

Implementazione

Quanto detto per la dct vale in modo del tutto analogo per la dst: la trasforma-ta bidimensionale può essere ottenuta grazie a 2 trasformate monodimensionali, eciascuna trasformata monodimensionale può essere ottenuta con la FFT. Questoapproccio è utilizzato nel metodo void Scalar_field::dst2D_ nonopt(), di cuinon riportiamo il codice per esteso. Osserviamo tuttavia che contrariamente aquanto accade per la dct, la dst di un vettore N dimensionale richiede il calcolodella FFT di un vettore 2N + 2 dimensionale. Ciò rende questo tipo di approc-cio non ottimale per essere eseguito in parallelo con CUDA. Infatti il vettore A.12contiene 2N + 2 elementi e, non essendo multiplo della dimensione dei blocchi, nonpermette di eettuare accessi coalescenti alla memoria globale (si veda Sec. 1.6).Inoltre la stessa libreria CUFFT è ottimizzata per vettori di 2m − 1 elementi, conm intero positivo, come spiegato nella sezione 3.7.

Si è cercato di ottenere una versione ottimizzata della dst, implementata nelmetodo void Scalar_field::dst2D_opt(). Questa implementazione segue lostesso approccio della versione precedente ma raggruppa le operazioni di riordi-no dell'array e di trasposizione in un unico kernel. Questo semplice accorgimentopermette quasi di raddoppiare la velocità di esecuzione della dst bidimensionale.Riportiamo di seguito il codice di quest'ultima versione. Sarebbe interessante con-frontare questa implementazione con un calcolo basato su moltiplicazione matricialediretta.

void Sca l a r_ f i e l d : : dst2D_opt ( ) cu f f tHand le plan1d ;cufftComplex∗ longx ;cufftComplex∗ longy ;c u t i l S a f eC a l l ( cudaMalloc ( (void ∗∗) &longx , 2∗ s izeof (cufftComplex ) ∗(

Nxx+1)∗Nyy) ) ;c u t i l S a f eC a l l ( cudaMalloc ( (void ∗∗) &longy , 2∗ s izeof (cufftComplex ) ∗(

Nyy+1)∗Nxx) ) ;

// r io rd ino e lement i d e l l ' arraypredst1 ( array_complex , longx ) ;

// FFT monodimensionalecu f f tP lan1d ( &plan1d , 2∗(Nxx+1) , CUFFT_C2C,Nyy) ;cufftExecC2C ( plan1d , longx , longx , CUFFT_FORWARD) ;

// tengo so l o e l ement i d a l l ' uno a l Nx ( per ogni colonna ) , ec o s t r u i s c o v e t t o r e longy per FFT d e l l e colonne

predst2 ( longx , longy ) ;

// d i s t ru g go longxcudaFree ( longx ) ;cu f f tDe s t r oy ( plan1d ) ;

51

Page 60: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Appendice A FFT, DCT e DST

// r i f a c c i o FFT di longycu f f tP lan1d ( &plan1d , 2∗(Nyy+1) , CUFFT_C2C,Nxx) ;cufftExecC2C ( plan1d , longy , longy , CUFFT_FORWARD) ;

// copio c o e f f d i i n t e r e s s e in array\_complex e l i d i v i do per (Nx+1)∗(Ny+1)

pos tds t ( array_complex , longy ) ;

cudaFree ( longy ) ;cu f f tDe s t r oy ( plan1d ) ;

// ke rne l s

__global__ void predst1_k ( const cufftComplex ∗ corto , cufftComplex ∗lungo )

// compute idx and idy , the l o c a t i on o f the element in the o r i g i n a lNxN array

int idx = blockIdx . x∗blockDim . x+threadIdx . x ;int idy = blockIdx . y∗blockDim . y+threadIdx . y ;int index = idx + idy ∗Nx;__shared__ f loat2 t i l e [ block_size_x ] [ block_size_y ] ;

//copy in shared memoryi f ( idx < Nx && idy <Ny)

t i l e [ threadIdx . x ] [ threadIdx . y]= cor to [ index ] ;

__syncthreads ( ) ;

//copy the same vec tor twice , with an odd simmetryi f ( idx < Nx && idy <Ny)

lungo [ 2∗ (Nx+1)∗ idy+idx +1] . x=t i l e [ threadIdx . x ] [ threadIdx . y ] . x ;lungo [ 2∗ (Nx+1)∗ idy+idx +1] . y=t i l e [ threadIdx . x ] [ threadIdx . y ] . y ;lungo [ 2∗ (Nx+1)∗( idy+1)−idx −1] . x=−t i l e [ threadIdx . x ] [ threadIdx . y ] . x ;lungo [ 2∗ (Nx+1)∗( idy+1)−idx −1] . y=−t i l e [ threadIdx . x ] [ threadIdx . y ] . y ;

__syncthreads ( ) ;

// add 0s at the beg inningi f ( idx==0 && idy<Ny)

lungo [ 2∗ (Nx+1)∗ idy ] . x=0;lungo [ 2∗ (Nx+1)∗ idy ] . y=0;

// and in the middlei f ( idx==Nx−1 && idy<Ny)

lungo [ 2∗ (Nx+1)∗ idy+idx +2] . x=0;lungo [ 2∗ (Nx+1)∗ idy+idx +2] . y=0;

__global__ void predst2_k ( const cufftComplex ∗ lungo , cufftComplex ∗co r to )

/∗ compute idx and idy , the l o c a t i on o f the element in the o r i g i n a lNxN array ∗/

52

Page 61: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Appendice A FFT, DCT e DST

int idx = blockIdx . x∗blockDim . x+threadIdx . x ;int idy = blockIdx . y∗blockDim . y+threadIdx . y ;

__shared__ f loat2 t i l e [ block_size_x ] [ block_size_y ] ;

//copy in shared memoryi f ( idx < Nx && idy <Ny)

t i l e [ threadIdx . x ] [ threadIdx . y]= lungo [ idx+1+idy ∗ (2∗ (Nx+1) ) ] ;

__syncthreads ( ) ;

i f ( idx < Nx && idy <Ny)

cor to [ 2∗ (Ny+1)∗ idx+idy +1] . x=0;co r to [ 2∗ (Ny+1)∗( idx+1)−idy −1] . x=0;co r to [ 2∗ (Ny+1)∗ idx+idy +1] . y=t i l e [ threadIdx . x ] [ threadIdx . y ] . y ;co r to [ 2∗ (Ny+1)∗( idx+1)−idy −1] . y=−t i l e [ threadIdx . x ] [ threadIdx . y ] . y ;

__syncthreads ( ) ;

i f ( idy==0 && idx<Nx) cor to [ 2∗ (Ny+1)∗ idx ] . x=0;co r to [ 2∗ (Ny+1)∗ idx ] . y=0;

i f ( idy==Ny−1&& idx<Nx)

cor to [ 2∗ (Ny+1)∗ idx+Ny+1] . x=0;co r to [ 2∗ (Ny+1)∗ idx+Ny+1] . y=0;

__global__ void postdst_k (cufftComplex ∗ corto , const cufftComplex ∗lungo )

// compute idx and idy , the l o c a t i on o f the element in the o r i g i n a lNxN array

int idx = blockIdx . x∗blockDim . x+threadIdx . x ;int idy = blockIdx . y∗blockDim . y+threadIdx . y ;int index = idx + idy ∗Nx;

__shared__ f loat2 t i l e [ block_size_x ] [ block_size_y ] ;

i f ( idx < Nx && idy <Ny)t i l e [ threadIdx . x ] [ threadIdx . y]= lungo [ idy+1+idx ∗ (2∗ (Ny+1) ) ] ;

__syncthreads ( ) ;

i f ( idx < Nx && idy <Ny)

cor to [ index ] . x=(1. f /(Nx∗Ny) ) ∗ t i l e [ threadIdx . x ] [ threadIdx . y ] . x ;co r to [ index ] . y=0;

Metodi e kernels per il calcolo della dct

Come esemplicato nella gura A.1, tre kernels si occupano del trattamentodei dati fra una FFT monodimensionale e l'altra. Con riferimento alla gura, leoperazioni sono svolte:

• 1: kernel predst1_k, con ovvio signicato degli argomenti in ingresso;

• 2, 3, 4: kernel void predst2_k, con ovvio signicato degli argomenti iningresso;

53

Page 62: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Appendice A FFT, DCT e DST

Figura A.1: Schema della DST ottimizzata.

• 5, 6: kernel postdst_k.

Si noti che tutti e tre i kernels fanno uso della shared memory per ottimizzare gliaccessi alla memoria globale.

54

Page 63: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Appendice BCodice Matlab

%%−−−−−−I n i z i a l i z a t i o n −−−−−−−−−−−2% No. o f Fourier modes

4 Nx=64;Ny=64;

6 % Domain s i z e ( assumed square )Lx=1;

8 Ly=1;rad2=sqrt (2 ) ;

10 radnx=sqrt (Nx) ;radny=sqrt (Ny) ;

12 % Grid spacinghx=Lx/Nx ;

14 hy=Ly/Ny ;x=(0:(Nx−1) ) ∗hx ;

16 y=(0:(Ny−1) ) ∗hy ;[X Y] = meshgrid (x , y ) ;

18% Vector o f wavenumbers

20 kx = (2∗pi/Lx) ∗ [ 0 : (Nx/2−1) (−Nx/2) :(−1) ] ;ky = (2∗pi/Ly) ∗ [ 0 : (Ny/2−1) (−Ny/2) :(−1) ] ;

22 % wavenumbers f o r pressure l a p l a c i an ( f f t )[KX KY] = meshgrid ( kx , ky ) ;

24 %wavenumbers f o r v e l o c i t y l a p l a c i an ( ds t )[NNx NNy]=meshgrid ( ( 1 :Nx) , ( 1 :Ny) ) ;

26 KK=−(pi^2) . ∗ (NNx.^2+NNy.^2) . / ( Lx∗Lx∗Ly∗Ly) ;%wavenumbers f o r pressure g rad i en t ( dc t )

28 [NNx NNy]=meshgrid ( ( 0 :Nx−1) , ( 0 :Ny−1) ) ;Kcos=−(pi^2) . ∗ ( (NNx) .^2+(NNy) .^2) . / ( Lx∗Lx∗Ly∗Ly) ;

30%phy s i c a l parameters

32 nu=1.0e−3;dt =0.01;

34 t imes teps =100;

36 %i n i t i a l cond i t i onsphat=zeros (Ny,Nx) ;

38 ux=zeros (Ny,Nx) ;uy=zeros (Ny,Nx) ;

40%rhs and transform

55

Page 64: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

42 fx=zeros (Ny,Nx) ;fy=zeros (Ny,Nx) ;

44 fx (Ny−3:Ny , : ) =1;fhatx = (−4.∗ dst ( dst ( fx ) ' ) . / ( (Nx) ∗(Ny) ) ) ' ;

46 fhaty= (−4.∗ dst ( dst ( fy ) ' ) . / ( (Nx) ∗(Ny) ) ) ' ;

48 figure ;

50 %%−−−−−−−−−PROJECTION METHOD−−−−−−−−−−−−−−−−−−

52 for i i =1: t imes teps

54 %v i s u a l i z a t i o ni f (mod( i i , 1 )==0 | | i i ==1)

56 contour f (X,Y, ux.^2+uy .^2 ,50) ;colorbar ; shading f l a t ; colormap ( ' j e t ' ) ;

58 hold onquiver (X,Y, ux , uy )

60 hold o f f ; drawnowend

62%DST of v e l o c i t y f i e l d

64 uhatx=(−4.∗dst ( dst ( ux ) ' ) . / ( (Nx) ∗(Ny) ) ) ' ;uhaty=(−4.∗dst ( dst ( uy ) ' ) . / ( (Nx) ∗(Ny) ) ) ' ;

66%non−l i n e a r term and i t s DST

68 l x=ux .∗ dx (ux ,Nx, hx ) + uy .∗ dy (ux ,Ny, hy ) ;l y=ux .∗ dx (uy ,Nx, hx ) + uy .∗ dy (uy ,Ny, hy ) ;

70 l hatx=(−4.∗dst ( dst ( l x ) ' ) . / ( (Nx) ∗(Ny) ) ) ' ;lhaty=(−4.∗dst ( dst ( l y ) ' ) . / ( (Nx) ∗(Ny) ) ) ' ;

72%so l u t i on o f v e l o c i t y l a p l a c i an in s inus space

74 ut i l d eha tx=(dt .∗(− l hatx+fhatx )+uhatx ) ./(1−dt .∗ nu .∗KK) ;u t i l d eha ty=(dt .∗(− l haty+fhaty )+uhaty ) ./(1−dt .∗ nu .∗KK) ;

76%inver s e transform of v e l o c i t y

78 u t i l d e x=− i d s t ( i d s t ( u t i l d eha tx ) ' ) ' . ∗ (Nx) . ∗ (Ny) . / 4 ;u t i l d e y=−i d s t ( i d s t ( u t i l d eha ty ) ' ) ' . ∗ (Nx) . ∗ (Ny) . / 4 ;

80%divergence c a l c u l a t i o n in cos ine space

82 d i vu t i l d e=dx ( ut i ldex ,Nx, hx )+dy ( ut i ldey ,Ny, hy ) ;d i vu t i l d eha t=(dct ( dct ( d i v u t i l d e ) ' ) ' ) .∗2/ ( radnx∗ radny ) ;

84 d i vu t i l d eha t ( 1 , : )=d i vu t i l d eha t ( 1 , : ) . / rad2 ;d i vu t i l d eha t ( : , 1 )=d i vu t i l d eha t ( : , 1 ) . / rad2 ;

86%pressure grad i en t s o l u t i on in cos ine space

88 Kcos (1 , 1 ) =1;phat =(1./(Kcos .∗ dt ) ) .∗ d i vu t i l d eha t ;

90 phat (1 , 1 ) =0;

92 %inver s e transform for pressurephat ( 1 , : )=phat ( 1 , : ) .∗ rad2 ;

94 phat ( : , 1 )=phat ( : , 1 ) .∗ rad2 ;p=( i d c t ( i d c t ( phat ' ) ' ) ) . ∗ ( radnx∗ radny ) . / 2 ;

96%v e l o c i t y update in r e a l space

98 ux=ut i ldex−dt .∗ dx (p ,Nx, hx ) ;uy=ut i ldey−dt .∗ dy (p ,Ny, hy ) ;

100end

Matlab Code

56

Page 65: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

Bibliograa

[1] NVIDIA Corporation. NVIDIA CUDA C programming guide, 2010.Version 3.2.

[2] M. Harris. Optimizing parallel reduction in cuda, 2009.

[3] Ruetsch G. NVIDIA Corporation Micikevicius P. Optimizing matrixtranspose in cuda, 2009.

[4] D. Shreiner, M. Woo, J. Neider, and T. Davis. OpenGL R© Program-

ming Guide : The Ocial Guide to Learning OpenGL R©, Version 2 (5th

Edition). Addison-Wesley Professional, August 2005.

[5] A. Quarteroni. Modellistica numerica per problemi dierenziali.Springer, 4a edizione. edition, 2008.

[6] Ferzinger. Computational Methods for Fluid Dynamics. Springer.

[7] A. J. Chorin. Numerical solution of the navier-stokes equations. Math.

Comp., 22, 1968.

[8] R. Temam. Une méthode d'approximation des solutions des équationsnavier-stokes. Bull. Soc. Math. France, 98, 1968.

[9] D. Gottlieb and S.A. Orszag. Numerical analysis of spectral meth-ods: Theory and applications. Regional Conference Series in Applied

Mathematics, SIAM, 26, 1977.

[10] B. Long and E. Reinhard. Real-time uid simulation using dis-crete sine/cosine transforms. Proceedings of the 2009 Symposium on

Interactive 3D Graphics and Games, 2009.

[11] Nvidia. CUDA CUFFT Library, April 2008.

[12] J.C. Thibault and I.Senocak. Cuda implementation of a navier-stokessolver on multi- gpu desktop platforms for incompressible ows. Bull.

Soc. Math. France, 98, 1968.

57

Page 66: Risoluzione delle equazioni di NavierStokes su scheda graficaforma/Didattica/ProgettiPacs/... · (General-Purpose computing on Graphics Processing Units) che ha come ... bidimensionali

[13] K. Rao, D.N. Kim, and J.J. Hwang. Fast Fourier Transform. Signalsand Communication Technology. Springer, 2010.

[14] A.K. Jain. Fundamentals of digital image processing. Prentice-Hallinformation and system sciences series. Prentice Hall, 1989.

[15] N. Goodnight. Cuda/opengl uid simulation, April 2007.

58