Rapport sur la simulation de jet de gouttes d'eau en MPI - EFREI I2
Rougier Dorian - rougier at efrei dot fr
Michel Sebastien - michels at efrei dot fr

Sommaire


Introduction :


Tout le monde à déjà du essayer de diriger un jet d'eau vers le haut et à regarder comment les gouttes tombées au sol.
Ce projet, c'est un peu cela, nous avons des gouttes au départ qui partent à peu près toutes du même endroit et on veut savoir où elles vont tomber.
Mais comme ceci nécessite beaucoup de calcul, pour gagner du temps nous allons faire le programme pour qu'il fonctionne sur plusieurs processeurs, donc nous allons faire de la programmation parallèle en C grâce aux fonction MPI.
Voici donc notre programme et les résultats trouvés.

Code Parallele :

#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"
#include <math.h>


#define FILE_INIT "data_initiale_1000"
#define NB_GOUTTE 1000
#define rayon 0.001
#define false 0
#define true 1

typedef struct goutte{
      float x,y,z;    //position réel de la goutte en metre
      float vx,vy,vz; //vitesse initiale de la goutte en metre/s
      float masse;      //en microgramme
      int numero;     //indispensable pour l'affichage
} Goutte;
                                                     

int main(int argc,char *argv[])
{
   int tasks,i;//,j;
      FILE *fic,*ficf;
      int nb_goutte=NB_GOUTTE;      // 1000
      int nb_goutte_presente,nb_goutte_presente_avant,nb_goutte_fin,nb_goutte_presente_totale;
      int fus=0,fus_tot=0,fus_tot1,sol=0,sol_tot=0,sol_tot1;
      float t=0;              // le temps est en seconde
      float acc=-9.81;        // que sur les z
//    float rayon=0.001;        // .002 rayon d'attraction des gouttes entre elles
      Goutte temp_g,g[nb_goutte],ge0[nb_goutte],ge1[nb_goutte],gr0[nb_goutte],gr1[nb_goutte];
      int tab_num_goutte[nb_goutte],tab_num_goutte2[nb_goutte];
      int ig=0,jg=0;
      // les unités sont en metre
      float maxx=.1,minx=-maxx;
      float       double dt=.01;  //float   //0.01 le temps s'incrémente par 1/100 de seconde
      int nb_envoie0,nb_recu0;
      int nb_envoie1,nb_recu1;
      double MPI_Wtime();
     
      MPI_Datatype types[8]={MPI_FLOAT,MPI_FLOAT,MPI_FLOAT,MPI_FLOAT,MPI_FLOAT,MPI_FLOAT,MPI_FLOAT,MPI_INT};
      int longueur_blocs[8]={1,1,1,1,1,1,1,1};
      double temps_debut,temps_fin;
      int me,right_proc,left_proc;
      MPI_Status status;
      MPI_Aint adresse[8];
      MPI_Aint deplacement[8];
      MPI_Datatype MPI_Goutte;

//  printf("debut de tous le porgramme");
      MPI_Init(&argc,&argv);
      MPI_Comm_rank(MPI_COMM_WORLD,&me);
      MPI_Comm_size(MPI_COMM_WORLD,&tasks);

      MPI_Address(&temp_g.x,&adresse[0]);
      MPI_Address(&temp_g.y,&adresse[1]);
      MPI_Address(&temp_g.z,&adresse[2]);
      MPI_Address(&temp_g.vx,&adresse[3]);
      MPI_Address(&temp_g.vy,&adresse[4]);
      MPI_Address(&temp_g.vz,&adresse[5]);
      MPI_Address(&temp_g.masse,&adresse[6]);
      MPI_Address(&temp_g.numero,&adresse[7]);
     
      for (i=0;i<=7;i++) deplacement[i]=adresse[i]-adresse[0];
       MPI_Type_struct(8,longueur_blocs,deplacement,types,&MPI_Goutte);
      MPI_Type_commit(&MPI_Goutte);

   temps_debut=MPI_Wtime();
 
   if (me<tasks-1) right_proc=me+1;
   else right_proc=MPI_PROC_NULL;
   if (me==0) left_proc=MPI_PROC_NULL;
   else left_proc=me-1;
//   printf("rang :\"%i\"\t\"%i\"\t\"%i\"\n",left_proc,me,right_proc); 
//  
//      temps_debut=MPI_Wtime();
      fic=fopen(FILE_INIT,"r");
      fread(&nb_goutte,sizeof(nb_goutte),1,fic);
      // printf("il y a %i valeur \n",nb_goutte);
      for (ig=0;ig<nb_goutte;ig++)
            {
            fread(&temp_g,sizeof(temp_g),1,fic);
            if ( (temp_g.x>=( (maxx-minx) / tasks* me   +minx ))  &&
                   (temp_g.x<( (maxx-minx) / tasks*(me+1)+minx )))
                  {
                  g[nb_goutte_presente]=temp_g;
                  nb_goutte_presente++;
                  }
            }
// compteur qui nous servira au stockage des gouttes fusionné ou au sol en fin de tableau
// Car a la suite des gouttes_presentes nous allons rajouter des gouttes venant d'autre proc
      nb_goutte_fin=nb_goutte;

/*
// Verif de la distribution des gouttes entre les proc !

if (me==0)
{
for (i=0;i<nb_goutte_presente;i++)
      //printf("Goutte : %f %f %f %f %f %f %f %i\n",g[i].x,g[i].y,g[i].z,g[i].vx,g[i].vy,g[i].vz,g[i].masse,g[i].numero);
}

printf("task %i\n",tasks);
     
/**************** on rentre ds la boucle de calcul **********************/
nb_goutte_presente_totale=nb_goutte;

while (nb_goutte_presente_totale!=0)
      {  // boucle sur le temps
      t=t+dt;
      fus=0; // nbre de gouttes qui fussionnent
      sol=0; // nbre de goutte qui tombent sur le sol
      nb_envoie0=0; //nbre de goutte a envoie
      nb_envoie1=0; //nbre de goutte a envoie
      nb_goutte_presente_avant=nb_goutte_presente;
      // déplacement de goutte (la vitesse ne change pas sauf sur les z)
      for (ig=0;ig<nb_goutte_presente;ig++)
//calcul de toute les nouvelles positions
            {
            temp_g=g[ig];
            g[ig].x =g[ig].x +g[ig].vx*dt ;
            g[ig].y =g[ig].y +g[ig].vy*dt ;
            g[ig].z =g[ig].z +g[ig].vz*dt + acc * (pow (dt,2)) /2;
//la fct pow ne fonctionnement pas !!!???  .... finalement il fallait rajouter -lm en arg de hcc :)
//          g[ig].z =g[ig].z +g[ig].vz*dt + acc * (dt*dt) /2;
            g[ig].vz=g[ig].vz+acc*dt;
            //vx et vy n'évoluent pas, elles restent constantes !
//si la goutte retombe sur le sol
            if (g[ig].z<0)
                  {
//calcul de l'intersection avec le sol
                  g[ig].x=((-g[ig].x*temp_g.z+g[ig].z*temp_g.x)/(g[ig].x-temp_g.x))*((g[ig].x-temp_g.x)/(g[ig].z-temp_g.z));
                  g[ig].y=((-g[ig].y*temp_g.z+g[ig].z*temp_g.y)/(g[ig].y-temp_g.y))*((g[ig].y-temp_g.y)/(g[ig].z-temp_g.z));
                  g[ig].z=0;
//déplacement de la particule en tête des particules déjà traitées
                  // temp_g=g[ig];
                  // g[ig]=g[nb_goutte_presente-1];
                  // g[nb_goutte_presente-1]=temp_g;
                  temp_g=g[nb_goutte_presente-1]; // sauvegarde du g[nb_goutte_presente-1] pout ne pas
                                                                  // le perdre en cas d'ecrasement de la part de g[ig]
                  nb_goutte_fin--;
                  g[nb_goutte_fin]=g[ig];
                  g[ig]=temp_g;
                  nb_goutte_presente--;
                  ig--;
                  sol++;
                }
//stockage des gouttes a envoyer sur le proc de gauche
            else if ((me !=0) && g[ig].x<( (maxx-minx) / tasks* me   +minx ))
                  {
                // temp_g=g[ig];
                   g[ig]=g[nb_goutte_presente-1];
                  // g[nb_goutte_presente-1]=temp_g;
                  g[nb_goutte_presente-1].masse=-t;  // enlever pour optimiser !
                  ge0[nb_envoie0]=temp_g;
                  nb_envoie0++;
                  nb_goutte_presente--;
                  ig--;
                  }
//stockage des gouttes a envoyer sur le proc de droite
            else if ((me !=tasks-1)&& g[ig].x>=( (maxx-minx) / tasks*(me+1)+minx ))
                   {
                   // temp_g=g[ig];
                   g[ig]=g[nb_goutte_presente-1];
                   // g[nb_goutte_presente-1]=temp_g;
                   g[nb_goutte_presente-1].masse=-t;  // enlever pour optimiser !
                   ge1[nb_envoie1]=temp_g;
                   nb_envoie1++;
                   nb_goutte_presente--;
                   ig--;
                   }
            }
//**** envoie non bloquant reception bloquant **
//********   passage des gouttes entre les proc   ***************
//envoie des gouttes changants de zones .
// 1- envoie du nombre de goutte
// 2- envoie des gouttes.

nb_recu0=nb_recu1=0;   
//traitement du proc de gauche
      if (me!=0)
      {
      MPI_Sendrecv(&nb_envoie0,1,MPI_INT,left_proc,100,&nb_recu0,1,MPI_INT,left_proc,100,MPI_COMM_WORLD,&status);
      //if (me==0) nb_recu0=0;
      MPI_Sendrecv(&ge0,nb_envoie0,MPI_Goutte,left_proc,100,&gr0,nb_recu0,MPI_Goutte,left_proc,100,MPI_COMM_WORLD,&status);
      }
//traitement du proc de droite
   if (me!=tasks-1)
   {
      MPI_Sendrecv(&nb_envoie1,1,MPI_INT,right_proc,100,&nb_recu1,1,MPI_INT,right_proc,100,MPI_COMM_WORLD,&status);
      MPI_Sendrecv(&ge1,nb_envoie1,MPI_Goutte,right_proc,100,&gr1,nb_recu1,MPI_Goutte,right_proc,100,MPI_COMM_WORLD,&status);
   }
     
//placement des gouttes nouvellement arrivé ds le tableau
//traitement des gouttes arrive de gauche
for(i=0;i<nb_recu0;i++)
      {
      g[nb_goutte_presente]=gr0[i];
      nb_goutte_presente++;
      }
     
//traitement des gouttes arrive de droite
for(i=0;i<nb_recu1;i++)
      {
      g[nb_goutte_presente]=gr1[i];
      nb_goutte_presente++;
      }
     
      // les gouttes fusionnent t-elles ensemble ? (analyse deux à deux)

       nb_envoie1=0;
      for (ig=0;ig<nb_goutte_presente;ig++)
            {
      for (jg=ig+1;jg<nb_goutte_presente;jg++)
                  {
            if ((fabs(g[ig].x-g[jg].x)<rayon)&&(fabs(g[ig].y-g[jg].y)<rayon)&&(fabs(g[ig].z-g[jg].z)<rayon))
                        {
                      fus++;
                      g[ig].vx=(g[ig].masse*g[ig].vx+g[jg].masse*g[jg].vx)/(g[ig].masse+g[jg].masse);
                      g[ig].vy=(g[ig].masse*g[ig].vy+g[jg].masse*g[jg].vy)/(g[ig].masse+g[jg].masse);
                      g[ig].vz=(g[ig].masse*g[ig].vz+g[jg].masse*g[jg].vz)/(g[ig].masse+g[jg].masse);
                      g[ig].x=(g[ig].x+g[jg].x)/2;
                      g[ig].y=(g[ig].y+g[jg].y)/2;
                      g[ig].z=(g[ig].z+g[jg].z)/2;
                      g[ig].masse=g[ig].masse+g[jg].masse;
                      g[jg].masse=-t;   //pour savoir qd une goutte a été fusionnée
                        temp_g=g[jg];
                        g[jg]=g[nb_goutte_presente-1];
                      g[nb_goutte_presente-1]=temp_g;
                      nb_goutte_presente--;
                      jg--;
                      }
                  }// inserer le stockage des gouttes a envoyer sur le proc droite pour optimiser !
      // pas de changement pour les resultats
             if ((me != tasks-1)&& g[ig].x>=( (maxx-minx) / tasks*(me+1) + minx - rayon ))                               
                   {                                                                                                
                  ge1[nb_envoie1]=g[ig];
                  nb_envoie1++;
                  }
            }
 
    //**** envoie non bloquant reception bloquant **
      //********   passage des gouttes entre les proc   ***************
      //envoie des gouttes changants de zones .
      // 1- envoie du nombre de goutte
      // 2- envoie des gouttes
      //traitement du proc de droite
     
      MPI_Sendrecv(&nb_envoie1,1,MPI_INT,right_proc,100,&nb_recu1,1,MPI_INT,left_proc,100,MPI_COMM_WORLD,&status);
      if (me==0) nb_recu1=0;
      MPI_Sendrecv(&ge1,nb_envoie1,MPI_Goutte,right_proc,100,&gr1,nb_recu1,MPI_Goutte,left_proc,100,MPI_COMM_WORLD,&status);

     
//      printf("casse tete rang %i  recu %i envoie %i\n",me,nb_recu1,nb_envoie1);
     
      // verif des fusions entre les gouttes des frontieres
      nb_envoie0=0;
      for (ig=0;ig<nb_recu1;ig++)
            {
            for (jg=0;jg<nb_goutte_presente;jg++)  
                  {
                  if ((fabs(gr1[ig].x-g[jg].x)<rayon)&&(fabs(gr1[ig].y-g[jg].y)<rayon)&&(fabs(gr1[ig].z-g[jg].z)<rayon))
                        {
                        fus++;
                        g[jg].vx=(gr1[ig].masse*gr1[ig].vx+g[jg].masse*g[jg].vx)/(gr1[ig].masse+g[jg].masse);
                        g[jg].vy=(gr1[ig].masse*gr1[ig].vy+g[jg].masse*g[jg].vy)/(gr1[ig].masse+g[jg].masse);
                        g[jg].vz=(gr1[ig].masse*gr1[ig].vz+g[jg].masse*g[jg].vz)/(gr1[ig].masse+g[jg].masse);
                        g[jg].x=(gr1[ig].x+g[jg].x)/2;
                        g[jg].y=(gr1[ig].y+g[jg].y)/2;
                        g[jg].z=(gr1[ig].z+g[jg].z)/2;
                        g[jg].masse=gr1[ig].masse+g[jg].masse;
                        tab_num_goutte[nb_envoie0]=gr1[ig].numero;
                        nb_envoie0++;
                        break; // pour ne pas retester la goutte deja fusionne avec les autres gouttes
                        }
                  }
            }
      // a la reception des gouttes qui ont ete fusionnees
                       
      MPI_Sendrecv(&nb_envoie0,1,MPI_INT,left_proc,100,&nb_recu0,1,MPI_INT,right_proc,100,MPI_COMM_WORLD,&status);
      if (me ==tasks-1) nb_recu0=0;
      MPI_Sendrecv(&tab_num_goutte,nb_envoie0,MPI_INT,left_proc,100,&tab_num_goutte2,nb_recu0,MPI_INT,right_proc,100,MPI_COMM_WORLD,&status);
           
      for (ig=0;ig<nb_recu0;ig++)
            {
            for (jg=0;jg<nb_goutte_presente;jg++)
                  {
                  if (tab_num_goutte2[ig]==g[jg].numero)
                        {
                        g[jg]=g[nb_goutte_presente-1];
                        g[nb_goutte_presente-1].masse=-t;
                        nb_goutte_presente--;
                        break;                                                                                            
                        }
                  }
            }
     
      fus_tot=fus_tot+fus;
      sol_tot=sol_tot+sol;
//      printf("il y a %i gouttes qui ont fusionné et %i qui sont tombées au sol\n",fus,sol);
    //sauvegarde des data
    // calcul du nb de goutte à sauvegarder
/*                                                                                              // Large possibilite d'optimisation !
      nb_a_sauver=0;
    for (ig=nb_goutte_presente;ig<nb_goutte_presente_avant;ig++)
            {
      if (g[ig].masse>0) nb_a_sauver++ ;
          }
      fwrite(&nb_a_sauver,sizeof(nb_a_sauver),1,fic);
    for (ig=nb_goutte_presente;ig<nb_goutte_presente_avant;ig++)
            {
      if (g[ig].masse>0)
                  {
            fwrite(&g[ig],sizeof(g[ig]),1,fic);
                }
            }
           
*/

      MPI_Allreduce(&nb_goutte_presente,&nb_goutte_presente_totale,1,MPI_INT,MPI_SUM,MPI_COMM_WORLD);
      }
      //printf("nombre fus %i\n",fus_tot);
// ouverture du fichier finale
      //if (me==0)
      //unlink("data_finale_mpi");
      MPI_Reduce(&sol_tot,&sol_tot1,1,MPI_INT,MPI_SUM,0,MPI_COMM_WORLD);//mise en commun des donnees finales
      for (i=0;i<tasks;i++)
      {
            if (me==0 && i==0)
            {
                  ficf=fopen("data_finale_mpi","w");  //pour etre sur que le programme soit bon
                  fwrite(&sol_tot1,sizeof(sol_tot1),1,ficf);
                  for(ig=nb_goutte-1;ig>=nb_goutte_fin;ig--)
                            {
                              //printf("rank : %i => %i gouttes\n",me,sol_tot);
                                                      fwrite(&g[ig],sizeof(g[ig]),1,ficf);
                              }
                  fclose(ficf);
            }
            else if (me==i && i!=0)
            {
                  ficf=fopen("data_finale_mpi","a");
                  for(ig=nb_goutte-1;ig>=nb_goutte_fin;ig--)
                  {
                        //printf("rank : %i => %i gouttes\n",me,sol_tot);
                                          fwrite(&g[ig],sizeof(g[ig]),1,ficf);
                  }
                  fclose(ficf);
            }
            MPI_Barrier(MPI_COMM_WORLD);
      }
       
 MPI_Reduce(&fus_tot,&fus_tot1,1,MPI_INT,MPI_SUM,0,MPI_COMM_WORLD);
      temps_fin=MPI_Wtime();
if(me==0)
 printf("-->il y %i gouttes qui ont fusionné et %i qui sont tombées au sol\n",fus_tot1,sol_tot1);
 fclose(fic);
 //printf("--->la similation a durée %f secondes\n",t);
     
      printf("Tout les proc : %f\n",temps_fin-temps_debut);
      printf("Goutte de %i : %i\n",me,nb_goutte_presente);
     
      MPI_Type_free(&MPI_Goutte);
      MPI_Finalize();
   return 0;
}
 


Principe de parallelisation

Analyse des données du problème

le principe est assez simple:
Au debut, les gouttes sont aléatoirement disposées sur un carré de 0.2 de coté.
Elles ont une vitesse et une direction aléatoire mais choisi dans un interval de tel manière à ce que les gouttes ne sortent jamais d'une certaine zone (un carré de 0.4 de coté).
Le but pour nous est de paralleliser le calcul de trajectoire de ces gouttes

Principe retenu:

Après quelques essais de l'execution du code sequentiel permettant de calculer la trajectoire des gouttes, nous avons décidé de couper la zone des gouttes en "n" bandes correspondants aux "n" processeurs disponibles pour le calcul.
Nous avons determiné la largeur des bandes en fonction de de la zone de départ des gouttes (le petit carré de 0.2 de coté).
Aussi nous pensons avoir la topologie la plus simple possible avec toutefois un très bon équilibre des charges de travail des processeurs (cf expliquations ci dessous)
Dans la mesure où, le nombre de goutte sortant d'un seul coté de la zone du petit carré est de seulement 7% à 9%, les processeurs des extremités ne traiteront que 8 à 10% de goutte en plus par rapport aux processeurs du centre.

Communication interprocesseur:

En ce qui concerne la communication interprocesseur, elle se passe en 3 étapes:

Certification de la bonne parallelisation avec les fichiers test

Analyse des performances nb de particules / processeur / temps, acceleration.

Voici le tableau récapitulatif des performances du programme:
1 p: 2 p: 3 p: 4 p: 5 p:
rayon=,001 10000 Gouttes 416 111,159 51,983 31,557 21,194025
rayon=,001 1000 Gouttes 4,567028 1,472331 1,098544 1,023605 1,165001
rayon=,001 100 Gouttes 0,096341 0,284744 0,445053 0,579858 0,688806
rayon=,01 10000 Gouttes 1,040138 0,666881 0,672115 0,785330 0,886456
rayon=,01 1000 Gouttes 0,429747 0,450498 0,574991 0,665447 0,799617
rayon=,01 100 Gouttes 0,081315 0,294300 0,499458 0,600920 0,719120
A partir de ce tableau, nous avons tracé deux graphes qui nous paraissent significatifs:

  • Ce premier graphique montre l'avantage du code parallele par rapport au code sequentiel:
    La courbe rose correspond au nombre de seconde que le code sequentiel prend pour executer le programme avec 10000 gouttes avec un rayon de fusion de 0.001, Alors que la courbe bleue correspond aux perf. du code parallele.
    Nous pouvons clairement voir que l'avantage tourne tres vite et largement en faveur du code parallele.
    Notons que nous en sommes pas allez plus loin en nombre de proc. et que le programme doit s'écrouler au niveau de ces performance(cf tableau d'apres) a un moment ou un autre que l'on essayera de determiner ci-dessous.

  • Ce second graphique montre les limites du code parallele.
    Dans notre cas, nous executons le programme eu seins d'un réseau 100Mb/s de sprac Ultra5.
    Le probleme du code parallele dans ce genre de configuration, c'est la lenteur de communication entre les procs car il faut passer par le LAN.
    Dans ce cas, nous voyons clairement que l'execution parallele n'a aucun interet pour 100 Gouttes comme pour 1000 Gouttes dans la mesure ou le temps d'execution augement des qu'il y a deux procs au lieu d'un. Quant a l'execution avec 10000 Gouttes elle n'est rentable qu'avec 2 procs!...
    En tout cas, nous voyons clairement que les courbe tendent toutes vers une meme asymptote.
    Grace a ces informations, nous pouvons interpreter les résultats afin d'interpoler l'écroulement des performences dans l'execution precedente: Je pense que les performance commence a baisser vers 6 procs pour etre encore plus lent que le code seqentiel vers 11 ou 12 procs. Car la courbe tendrait vers une asymptote linéraire qui est a 0 seconde pour 7 ou 8 procs et 450 secondes pour environ 15 procs.

    Voici le nombre de gouttes qui ont fusionnées:
    1 p: 2 p: 3 p: 4 p: 5 p:
    rayon=,001 10000 Gouttes 976 999 1011 1047 1012
    rayon=,001 1000 Gouttes 11 11 9 10 10
    rayon=,001 100 Gouttes 0 0 0 0 0
    1 p: 2 p: 3 p: 4 p: 5 p:
    rayon=,01 10000 Gouttes 9709 9710 9728 9723 9715
    rayon=,01 1000 Gouttes 780 777 787 782 791
    rayon=,01 100 Gouttes 30 31 30 31 31

    Tableau indiquant le nombre de gouttes passant d'un processeur a l'autre et le nombre de communication

    10000 gouttes R=0,001
    5P 4P 3P 2p
    nombre de gouttes 18534 12994 7165 1625
    nbre gouttes ss fusion 6201 4641 3185 1625
    nbre de com 4386 3204 1900 596
    10000 gouttes R=0,01
    5P 4P 3P 2p
    nombre de gouttes 3704 2581 1142 65
    nbre gouttes ss fusion 162 157 102 65
    nbre de com 2060 1385 867 433
    1000 gouttes R=0,001
    5P 4P 3P 2p
    nombre de gouttes 2110 1448 814 184
    nbre gouttes ss fusion 733 541 386 184
    nbre de com 3634 2560 1631 668
    1000 gouttes R=0,01
    5P 4P 3P 2p
    nombre de gouttes 3010 1734 1001 28
    nbre gouttes ss fusion 98 68 43 28
    nbre de com 2060 1489 964 430
    100 gouttes R=0,001
    5P 4P 3P 2p
    nombre de gouttes 171 147 83 19
    nbre gouttes ss fusion 50 41 31 19
    nbre de com 1778 1364 788 425
    100 gouttes R=0,01
    5P 4P 3P 2p
    nombre de gouttes 1022 599344 14
    nbre gouttes ss fusion 39 19 21 14
    nbre de com 1959 1452931 422

    Evaluation des resources memoire.

    Evaluation des ressources mémoire. Il y différent type de variable dans le programme. En premier nous allons voir les variable de type entier :
    nb_gouttequi représente le nombre totale de goutte que l'on étudie.
    nb_goutte_presentenombre de goutte présente dans un processeur.
    nb_goutte_presente_avantnombre de goutte présente dans le processeur avant de recommencer la boucle de traitement, pour savoir combien de boucle on disparue après chaque boucle.
    nb_goutte_finnous mettons les gouttes a la fin du tableau, lorsqu'elles sont tombées au sol, donc ce compteur permet de savoir combien de gouttes sont tombées qu sol, et où elles sont dans le tableau.
    nb_goutte_presente_totalec'est un compteur pour savoir combien il reste de gouttes présentent en tout dans tous les processeurs.
    fuscompte le nombre de fusion pour une boucle.
    fus_totcompte le nombre de fusion total pour un processeur.
    solcompte le nombre de goutte qui sont tombées au sol pour un processeur
    sol_totcompte le nombre total de gouttes qui sont tombées par terre pour un processeur
    sol_tot1nombre total pour tous les processeurs qui sont tombées par terre.
    i ig et ijcompteurs pour les for
    nb_envoie0 et nb_envoie1variables servant de compteur pour des send pour dire combien d'objet on va envoyer
    nb_recu0 et nb_recu1variables servant pour la réception du nombre d'objet a recevoir au prochain receive.
    mevariable qui sert à garder le numéro du processeur.
    taskssert à savoir combien il y a de processus dans la topologie MPI
    right_proc, left_procdéfinit le processeur de droite et de gauche pour un processeur.


    Un type particulier de int : les tableaux d'entier
    Tab_num_goutte[nb_goutte] permet de stocker le numéro des identificateurs des gouttes qui ont fusionnées et que le processeur de gauche doit supprimer
    Tab_num_goutte2[nb_goutte] permet de recevoir les identificateurs des gouttes qui ont fusionné et que l'on doit supprimer.
    longueur_blocs[8] Tableau qui sert pour la création d'un type MPI

    Nous allons maintenant voir un autre type de variable qui sont les floats :
    T permet de savoir le temps réel de la simulation dans la vie réel.
    Acc cette variable est une constante d'accélération qui sert dans les calculs.
    Maxx, minx sont des variables qui servent à la répartition de gouttes entre les différents processeurs.



    Un autre type qui se rapproche assez du type float : le type double :
    temps_debut,temps_fin ces 2 variables servent pour savoir combien de temps la simulation a duré sur chaque processeur.


    Pour ce projet nous avons du créer une structure goutte qui contient les informations d'une goutte, il y a la position de la goutte dans l'espace 3D, ainsi que les variables contenant la valeur des vitesses des 3 directions de l'espace.
    Ensuite il y a la masse de la goutte, et le numéro d'identificateur.

    typedef struct goutte{
    float x,y,z; //position réel de la goutte en mètre
    float vx,vy,vz; //vitesse initiale de la goutte en mètre/s
    float masse; //en micro gramme
    int numero; //indispensable pour l'affichage

    } Goutte;


    Ensuite nous avons créer des variables à partir de ce type :
    temp_g c'est pour stoker une goutte lors de modification des tableaux de goutte.
    g[nb_goutte] contient toutes les gouttes du processeur.
    ge0[nb_goutte], ge1[nb_goutte] sert de tableaux de stockage des gouttes qui vont partir sur un autre processeur.
    gr0[nb_goutte], gr1[nb_goutte] sert de tableaux pour réceptionner les gouttes qui viennent d'un autre processeur.


    Nous avons besoin d'ouvrir et d'écrire dans des fichiers pour voir les gouttes à utiliser pour que ce soit toujours les mêmes, et pour sauvegarder les gouttes qui tombent.
    fic fichier où l'on prend les gouttes.
    ficf fichier où l'on écrit toutes les gouttes qui sont tombées sur le sol, à la fin du programme.


    Pour finir nous allons voir des qui sont particuliers à MPI :
    MPI_Datatype types[8] c'est un tableau qui contient des types de variables MPI ex :MPI_FLOAT, MPI_INT….
    MPI_Status status sert pour les sendrecv
    MPI_Aint adresse[8] tableau contenant les adresses de variables.
    MPI_Aint deplacement[8] tableau contenant l'espace qui sépare des variables
    MPI_Datatype MPI_Goutte c'est une varaible qui sert en faite de définition pour le type goutte pour le formaliser en MPI pour pourvoir l'envoyer aux autres processeurs.


    Amelioration monoprocesseur


    Suivant le nombre de processeur, nous ne trouvons pas exactement le même nombre de gouttes fusionnées, en faite c'est du au faite que lorsque l'on fusionne une goutte, on ne vérifie le résultat de cette fusion avec les gouttes que l'on a regardé avant, mais lors de la fusion, les gouttes ont une nouvelle position.
    Donc pour améliorer le programme il faudrait à chaque nouvelle fusion recommencer de vérifier tout le tableau avec cette fusion.
    Pour être le plus prêt possible du programme monoprocesseur, nous n'avons pas inclus cette modification dans le programme multiprocesseur.

    Pour finir, l'amelioration que l'on peux faire sur le monoprocesseur et qui fait que notre programme MPI marche bien, c'est que en monoprocesseur, toutes les gouttes sont verifiées deux à deux alors que sur le programme MPI, les gouttes sont deja triées par processeur, il y a donc beaucoup moins de comparaisons.
    Il faudrait donc implemente un systeme de trie des gouttes sur une dimension afin de minimiser le nombre de comparaison.


    Conclusion :


    Voilà, notre premier projet en programmation parallèle est finis.
    Nous avons eu beaucoup de problèmes lors des compilations de notre programme.
    Il faisait souvent des erreurs pendant l'exécution du programme, pour des raisons mysterieuses (trop de variable, un pointeur, un passage par adresse ....).
    Au départ nous avons cru que c'était du à la topologie cartésienne, et que les machines de l'école ne le supportaient pas, mais par la suite il nous a refait l'erreur sur des send et des receive.
    Finalement le problème venait de la déclaration de variable MPI.
    Après tout c'est bien passé, le programme a vite était finis.
    Donc à part le problème de déclaration de variable MPI tout c'est bien passé, mais il nous a fallut beaucoup de temps, avant de comprendre l'erreur (merci monsieur Proux :) ).