
// This file is part of the Herezh++ application.
//
// The finite element software Herezh++ is dedicated to the field
// of mechanics for large transformations of solid structures.
// It is developed by Gérard Rio (APP: IDDN.FR.010.0106078.000.R.P.2006.035.20600)
// INSTITUT DE RECHERCHE DUPUY DE LÔME (IRDL) <https://www.irdl.fr/>.
//
// Herezh++ is distributed under GPL 3 license ou ultérieure.
//
// Copyright (C) 1997-2022 Université Bretagne Sud (France)
// AUTHOR : Gérard Rio
// E-MAIL  : gerardrio56@free.fr
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// For more information, please consult: <https://herezh.irdl.fr/>.

#include "ParaGlob.h"
#include "Sortie.h"
#include "Banniere.h"
#include "Handler_exception.h"  
#include "EnumTypeGrandeur.h"
#include "TypeQuelconque.h"
#include "TypeQuelconqueParticulier.h"

// ----------- déclaration des variables statiques -----------------

// elles sont faites dans EnteteParaGlob.h

// ----------- fin de la déclaration des variables statiques -------



// constructeur par defaut
ParaGlob::ParaGlob() :
  typeDeCalcul_maitre(RIEN_TYPECALCUL),soustypeDeCalcul()
  ,avec_typeDeCalcul(false),avec_soustypeDeCalcul()
  ,paraAlgoControle(NULL),chdimensionPb(0),NombreMaxiDeMaillage(5)
  ,etatDeLaLecturePointInfo(-1),demandeLectureSecondaireInPointInfo(0)
  ,listegrandeurs(),listegrandeurs_par_string()
  ,pt_temps_courant(NULL)
 #ifdef UTILISATION_MPI
  ,buffer_ioBI_MPI(),taille_buffer_ioBI_MPI()
 #endif

  { dimensionPb = 1; 
    // calcul du nombre de composantes des tenseurs en absolu 
     nbComposantesTenseur = 1; // cas par défaut 1D
    if (dimensionPb == 2)  nbComposantesTenseur = 3; 
    else if (dimensionPb == 3)  nbComposantesTenseur= 6;
    if (param != NULL)
     {// normalement on devrait pas utiliser le contructeur de copie
      cout << "\n warning : normalement on ne devrait pas avoir plusieurs ParaGlob !!!! ";
      cout << "\n ParaGlob::ParaGlob()";
      };
    param=this; // le seul pointeur dispo
  
    // --- cas de la gestion des interruptions systèmes --- Remplissage de la structure ad hoc
    /* adresse du gestionnaire */
    prepaSignal.sa_handler = &(Handler_signal); // par défaut on utilise la fonction
    /* Mise a zero du champ sa_flags theoriquement ignoré */
    prepaSignal.sa_flags=0;
    /* On ne bloque pas de signaux spécifiques */
    sigemptyset(&prepaSignal.sa_mask);
    // --- gestion d'une interruption ctrl-c ---
    sigaction(SIGINT,&prepaSignal,0); // SIGINT correspond à controle-c
    sigaction(SIGTERM,&prepaSignal,0); // SIGTERM correspond à kill 15
   
    // ajout de la grandeur global TEMPS_COURANT  par défaut
    Grandeur_scalaire_double grand_courant(0.);
    TypeQuelconque typQ1(GENERIQUE_UNE_GRANDEUR_GLOBALE,NU_DDL,grand_courant);
    Ajout_grandeur_consultable(&typQ1,TEMPS_COURANT);
    // mise à jour du pointeur qui fait la liaison avec le temps courant
    {TypeQuelconque* pt_quelc = (TypeQuelconque*) listegrandeurs.at(TEMPS_COURANT);
     Grandeur_scalaire_double& gr
                    = *((Grandeur_scalaire_double*) pt_quelc->Grandeur_pointee()); // pour simplifier
     pt_temps_courant = gr.ConteneurDouble(); // on pointe sur le conteneur
    };
  };
  
// constructeur en fonction de la dimension du pb
ParaGlob::ParaGlob(int dim) :
  typeDeCalcul_maitre(RIEN_TYPECALCUL),soustypeDeCalcul()
  ,avec_typeDeCalcul(false),avec_soustypeDeCalcul()
  ,paraAlgoControle(NULL),chdimensionPb(0),NombreMaxiDeMaillage(5)
  ,etatDeLaLecturePointInfo(-1),demandeLectureSecondaireInPointInfo(0)
  ,listegrandeurs(),listegrandeurs_par_string()
  ,pt_temps_courant(NULL)
#ifdef UTILISATION_MPI
  ,buffer_ioBI_MPI(),taille_buffer_ioBI_MPI()
#endif
  { dimensionPb = dim;
    // calcul du nombre de composantes des tenseurs en absolu 
     nbComposantesTenseur = 1; // cas par défaut 1D
    if (dimensionPb == 2)  nbComposantesTenseur = 3; 
    else if (dimensionPb == 3)  nbComposantesTenseur= 6;
    if (param != NULL)
     {// normalement on devrait pas utiliser le contructeur de copie
      cout << "\n warning : normalement on ne devrait pas avoir plusieurs ParaGlob !!!! ";
      cout << "\n ParaGlob::ParaGlob(int dim)";
      };
    param=this; // le seul pointeur dispo
  
    // --- cas de la gestion des interruptions systèmes --- Remplissage de la structure ad hoc
    /* adresse du gestionnaire */
    prepaSignal.sa_handler = &(Handler_signal); // par défaut on utilise la fonction
    /* Mise a zero du champ sa_flags theoriquement ignoré */
    prepaSignal.sa_flags=0;
    /* On ne bloque pas de signaux spécifiques */
    sigemptyset(&prepaSignal.sa_mask);
    // --- gestion d'une interruption ctrl-c ---
    sigaction(SIGINT,&prepaSignal,0); // SIGINT correspond à controle-c
    sigaction(SIGTERM,&prepaSignal,0); // SIGTERM correspond à kill 15

    // ajout de la grandeur global TEMPS_COURANT  par défaut
    Grandeur_scalaire_double grand_courant(0.);
    TypeQuelconque typQ1(GENERIQUE_UNE_GRANDEUR_GLOBALE,NU_DDL,grand_courant);
    Ajout_grandeur_consultable(&typQ1,TEMPS_COURANT);
    // mise à jour du pointeur qui fait la liaison avec le temps courant
    {TypeQuelconque* pt_quelc = (TypeQuelconque*) listegrandeurs.at(TEMPS_COURANT);
     Grandeur_scalaire_double& gr
                    = *((Grandeur_scalaire_double*) pt_quelc->Grandeur_pointee()); // pour simplifier
     pt_temps_courant = gr.ConteneurDouble(); // on pointe sur le conteneur
    };
  };
  
// Constructeur de copie												
ParaGlob::ParaGlob (const ParaGlob & nd) :
  typeDeCalcul_maitre(nd.typeDeCalcul_maitre),chdimensionPb(nd.chdimensionPb)
  ,NombreMaxiDeMaillage(nd.NombreMaxiDeMaillage)
  ,soustypeDeCalcul(nd.soustypeDeCalcul)
  ,avec_typeDeCalcul(nd.avec_typeDeCalcul)
  ,avec_soustypeDeCalcul(nd.avec_soustypeDeCalcul)
  ,paraAlgoControle(nd.paraAlgoControle)
  ,etatDeLaLecturePointInfo(nd.etatDeLaLecturePointInfo)
  ,demandeLectureSecondaireInPointInfo(nd.demandeLectureSecondaireInPointInfo)
  ,listegrandeurs(nd.listegrandeurs),listegrandeurs_par_string(nd.listegrandeurs_par_string)
#ifdef UTILISATION_MPI
  ,buffer_ioBI_MPI(nd.buffer_ioBI_MPI),taille_buffer_ioBI_MPI(nd.taille_buffer_ioBI_MPI)
#endif

  { // normalement on devrait pas utiliser le contructeur de copie
    cout << "\n ******* warning : normalement on ne devrait pas avoir plusieurs ParaGlob !!!! ";
    cout << "\n ******* ParaGlob::ParaGlob (const ParaGlob & nd)";
  
    // --- cas de la gestion des interruptions systèmes --- Remplissage de la structure ad hoc
    /* adresse du gestionnaire */
    prepaSignal.sa_handler = &(Handler_signal); // par défaut on utilise la fonction
    /* Mise a zero du champ sa_flags theoriquement ignoré */
    prepaSignal.sa_flags=0;
    /* On ne bloque pas de signaux spécifiques */
    sigemptyset(&prepaSignal.sa_mask);
    // --- gestion d'une interruption ctrl-c ---
    sigaction(SIGINT,&prepaSignal,0); // SIGINT correspond à controle-c
    sigaction(SIGTERM,&prepaSignal,0); // SIGTERM correspond à kill 15
    // mise à jour du pointeur qui fait la liaison avec le temps courant
    {TypeQuelconque* pt_quelc = (TypeQuelconque*) listegrandeurs.at(TEMPS_COURANT);
     Grandeur_scalaire_double& gr
                    = *((Grandeur_scalaire_double*) pt_quelc->Grandeur_pointee()); // pour simplifier
     pt_temps_courant = gr.ConteneurDouble(); // on pointe sur le conteneur
    };

   };
   
ParaGlob::~ParaGlob ()
  {// suppression des conteneurs globaux
   {std::map < Enum_GrandeurGlobale, const void * , std::less <Enum_GrandeurGlobale> >::iterator il,ilfin=listegrandeurs.end();
    for (il=listegrandeurs.begin(); il!= ilfin; ++il)
      {TypeQuelconque* gr_quelc = (TypeQuelconque*) il->second;
       delete gr_quelc;
      };
   };
   
  {std::map < string, const void * , std::less <string> >::iterator il,ilfin=listegrandeurs_par_string.end();
   for (il=listegrandeurs_par_string.begin(); il!= ilfin; ++il)
     {TypeQuelconque* gr_ici = (TypeQuelconque*) listegrandeurs_par_string.at(il->first);
      delete gr_ici;
     };
  }

  
  
  
  }; // destructeur

// changement de la dimension du pb : une seule fois
void ParaGlob::ChangeDimension(int nouvelleDimension)
    { if ((chdimensionPb==0)&&(nouvelleDimension<=3)&&(nouvelleDimension>= 1))
         {dimensionPb = nouvelleDimension;chdimensionPb=1;}
      else
         cout << " **changement de dimension au probleme non autorise** ";
    };        
     
// lecture du type de calcul et d'une liste de  sous type éventuel
void ParaGlob::LecTypeCalcul(UtilLecture& lec)
   { if (strstr(lec.tablcar,"TYPE_DE_CALCUL")!=NULL)
         { lec.NouvelleDonnee();
           LectureTypeCalcul(lec,typeDeCalcul_maitre,avec_typeDeCalcul
                             ,soustypeDeCalcul,avec_soustypeDeCalcul);
          }
      else
        { cout << " \n erreur de lecture du mot cle TYPE_DE_CALCUL, arret lecture \n";
          cout << " ParaGlob::LecTypeCalcul(UtilLecture& lec) " << endl;
         }
    };
     
// définition interactive du type de calcul et d'une liste de sous_type éventuel
// écriture du fichier de commande
void ParaGlob::Info_commande_TypeCalcul(UtilLecture& lec)
   { switch (lec.Lec_ent_info())
     {case -12: // cas de la sortie d'un schéma XML
       {// l'idée ici est de définir les infos par défaut permettant de construire le schéma
        typeDeCalcul_maitre=DEF_SCHEMA_XML;
        avec_typeDeCalcul=true;
        // on ne définie pas de sous type
       	break;	
       }
      default: // cas d'une lecture standard sur fichier
       {// affichage des différents type de calcul et choix du type et éventuellement
        //  de sous type
        Info_commande_type_calcul(lec,typeDeCalcul_maitre,avec_typeDeCalcul
                               ,soustypeDeCalcul,avec_soustypeDeCalcul);
       };
     };
   };     
     
// retourne vraie si le type de calcul demandé est celui en cours
// et s'il est actif
// dans le cas où le paramètre optionnel "actif" est true, il n'y a pas de test
// si c'est actif ou pas
bool  ParaGlob::TypeCalcul_principal(EnumTypeCalcul type,bool actif)
  { if ((type == typeDeCalcul_maitre) && (avec_typeDeCalcul || actif))
      return true;
    else
      return false;  
   }; 
// détermine si le sous type de calcul existe et s'il est actif
bool ParaGlob::SousTypeCalcul(EnumSousTypeCalcul soustype)
  {  bool retour = false; // retour faux a priori
     list <bool>::iterator ili;
     list <EnumSousTypeCalcul>::iterator ila;
     list <bool>::iterator ilifin = avec_soustypeDeCalcul.end();
     for (ili = avec_soustypeDeCalcul.begin()
          ,ila = soustypeDeCalcul.begin()
          ;ili!= ilifin; ili++,ila++)
        if ((soustype == (*ila)) && (*ili))
           retour = true;
     return retour;  
   }; 

     
// affichage du type de calcul et des sous_types éventuelles
void ParaGlob::AfficheTypeEtSousTypes()
 {   cout << "\n --> type de calcul  = " 
         << Nom_TypeCalcul(typeDeCalcul_maitre) << '\n';
     // puis les sous types    
     list <bool>::iterator ili;
     list <EnumSousTypeCalcul>::iterator ila;
     list <bool>::iterator ilifin = avec_soustypeDeCalcul.end();
     for (ili = avec_soustypeDeCalcul.begin()
          ,ila = soustypeDeCalcul.begin()
          ;ili!= ilifin; ili++,ila++)
        { cout << "sous_type : " <<  Nom_SousTypeCalcul(*ila) << " ";
          if (*ili)
            cout << " actif \n";
          else
            cout << " inactif \n"; 
         }
  };
	 
// modifie le niveau d'impression
void ParaGlob::Change_niveau_impression(int n ) 
 {  // vérification du niveau d'impression demandée
    if (n >= 10) nivImpression = 10;
    else if (n <= 0) nivImpression = 0;
    else nivImpression = n;
   };
   
// change le nombre de diggit utilisé pour l'affichage des réels en double précision
// int cas = 1 pour le calcul, =2 pour le graphique
void ParaGlob::Change_Nb_diggit_double(int cas,int n_nb_dig)
  { if ( n_nb_dig <= 0)
      { cout << "\n erreur le nombre de chiffre significatif pour l'affichage des reel doit etre >= 1"
             << "\n alors que la valeur demandé est : " << n_nb_dig << "!!!"
             << "\n ParaGlob::Change_Nb_diggit_double(int n_nb_dig)";
        Sortie(1);
       }
    if (cas == 1)   
      nb_diggit_double_calcul = n_nb_dig;
    else if (cas == 2)
      nb_diggit_double_graphique = n_nb_dig;
    else if (cas == 3)
      nb_diggit_double_ecran = n_nb_dig;
    else
      { cout << "\n erreur le cas = " << cas << "n'est pas implanté "
             << "\n ParaGlob::Change_Nb_diggit_double(int n_nb_dig)";
        Sortie(1);
       }
    };         
     
//----- lecture écriture de base info -----
//   lecture base info
// = 1 : on récupère tout
// = 2 : on récupère uniquement les données variables (supposées comme telles)
void ParaGlob::Lecture_base_info(istream& entr,const int cas)
{ // a priori il n'y a pas de grandeur qui varie d'où lecture uniquement
   // dans le cas =1
   if  (cas == 1)
     {  // passage de la banniere
        Banniere::Passage_lecture_banniere(entr);
        // lecture du numéro de version de l'entrée
        Lecture_Version(entr);
		  // lecture de la langue
		  ParaGlob::Lecture_Langue(entr);
        // message si numéro de version différent
        if(NbVersionsurfichier != nbVersion)
           cout << "\n **** attention le numero de version de votre fichier d'entree est "
                << " plus ancien que la version actuelle d'HEREZH++, "
                << " \n il peut eventuellement y avoir des disfonctionnements (cf doc)"
                << "\n ************** \n";
        // lecture de la dimension
        string toto;
        entr  >> toto >> dimensionPb;
        if ((dimensionPb!=1)&&(dimensionPb!=2)&&(dimensionPb!=3))
          { // dans le cas ou déjà la dimension est mauvaise
            // on signal qu'il y a un pb dans Paraglob
            cout << "\n*** erreur de lecture de dimension du problème ***\n"
                 << " ParaGlob::Lecture_base_info(istream& entr,const int cas)";
            Sortie(1);
           };      
        // niveau d'impression des commentaires
        entr  >> toto >>  nivImpression ;
        // def du nombre de composantes des tenseurs en absolu 
        nbComposantesTenseur = 1; // cas par défaut 1D
        if (dimensionPb == 2)  nbComposantesTenseur = 3; 
        else if (dimensionPb == 3)  nbComposantesTenseur= 6;
        // lecture du type de pb
        string  typecal,soustypecal;
        int nb_soustype; bool act_soustype;   
        entr  >> toto >> typecal >> toto >> avec_typeDeCalcul
              >> toto >> nb_soustype;
        typeDeCalcul_maitre =  Id_nom_TypeCalcul (typecal.c_str());
        // remplissage de la liste de sous type
        for (int i=1;i<=nb_soustype;i++)
          { entr  >> soustypecal >> act_soustype;
            soustypeDeCalcul.push_back(Id_nom_SousTypeCalcul(soustypecal.c_str()));
            avec_soustypeDeCalcul.push_back(act_soustype);
           }
      } ;
  };
        
//   écriture base info   
// = 1 : on sauvegarde tout
// = 2 : on sauvegarde uniquement les données variables (supposées comme telles)
void ParaGlob::Ecriture_base_info(ostream& sort,const int cas)
{ // a priori il n'y a pas de grandeur qui varie d'où sauvegarde uniquement
   // dans le cas =1
   if  (cas == 1)
     {  // sortie de la banniere
        Banniere::Sortie_banniere( sort);
        // puis affichage de la version
        ParaGlob::Sortie_Version(sort);
		      // puis affichage de la langue
		      ParaGlob::Sortie_Langue(sort);
        sort << "\n";
        // écriture de la dimension
        sort <<  " Dimension  " << dimensionPb << "\n";
        // niveau d'impression des commentaires
        sort <<  " niveau_impression  " << nivImpression << "\n";
        // puis type de calcul
        sort << "\n PARAGLOB  "  << Nom_TypeCalcul(typeDeCalcul_maitre) << " "
             << "activation? " << avec_typeDeCalcul ; 
        sort << " ,NB__sous_type_de_calcul " << avec_soustypeDeCalcul.size() << " \n" ;
        list <bool>::iterator ili;
        list <EnumSousTypeCalcul>::iterator ila;
        list <bool>::iterator ilifin = avec_soustypeDeCalcul.end();
        for (ili = avec_soustypeDeCalcul.begin()
             ,ila = soustypeDeCalcul.begin()
             ;ili!= ilifin; ili++,ila++)
                  sort << Nom_SousTypeCalcul(*ila) << " " << (*ili) << " ";
      } ;
  };
  
#ifdef UTILISATION_MPI
 // version MPI
 void ParaGlob::Lecture_base_info(MPI_File& ent_MPI,const int cas)
   {// on commence par lire la taille du buffer d'offset
    // tous les proc lisent
    
    // 1) ==== la taille de la place  de la taille du buffer
    int taille_buf = 0; // init
    // on encapsule pour ré-utiliser la même trame
    { char * buffer_car = new char [42] ; // def du tableau de travail
      MPI_Status status;
      int ierr = MPI_File_read_all(ent_MPI, buffer_car, 42, MPI_CHAR, &status);
      // on crée un flux sur le tableau de caractères:
      istrstream * ent_inter = new istrstream(buffer_car,42) ;
      // lecture de l'entête
      string toto;
      (*ent_inter) >> toto;
      if (toto != "tout_offset_paraglob" )
        {cout << "\n **** erreur on devait lire : 'tout_offset_paraglob' "
              << " et on lue "<< toto << " la suite n'est pas possible -> arret !! "
              << "\n ParaGlob::Lecture_base_info(... "<<endl;
         Sortie(1);
        };
      // on lit la taille de buf2
      (*ent_inter) >> toto >> taille_buf;
    };
    // 2) ==== lecture de la taille du buffer
    { char * buffer_car = new char [taille_buf] ; // def du tableau de travail
      MPI_Status status;
      int ierr = MPI_File_read_all(ent_MPI, buffer_car, taille_buf, MPI_CHAR, &status);
      // on crée un flux sur le tableau de caractères:
      istrstream * ent_inter = new istrstream(buffer_car,taille_buf) ;
      // lecture de l'entête
      string toto;
      (*ent_inter) >> toto;
      if (toto != "BI_MPI_IO_ParaGlob_taille_buffer" )
        {cout << "\n **** erreur on devait lire : 'BI_MPI_IO_ParaGlob_taille_buffer' "
              << " et on lue "<< toto << " la suite n'est pas possible -> arret !! "
              << "\n ParaGlob::Lecture_base_info(... "<<endl;
         Sortie(1);
        };
      // on lit la taille du buffer
      (*ent_inter) >> taille_buffer_ioBI_MPI;
    };
    // 3) ==== lecture du  buffer  et def du flux d'entrée associé
    // puis lecture de base info sur le flux
    { char * buffer_car = new char [taille_buffer_ioBI_MPI] ;
      MPI_Status status1;
      int ierr1 = MPI_File_read_all(ent_MPI, buffer_car, taille_buffer_ioBI_MPI, MPI_CHAR, &status1);
      // on crée le flux sur le tableau de caractères:
      istrstream * entrons = new istrstream(buffer_car,taille_buffer_ioBI_MPI) ;
      this->Lecture_base_info(*entrons,cas);
    };

   };

 // version MPI
 void ParaGlob::Ecriture_base_info(MPI_File& sort_MPI,const int cas)
   {   int proc_en_cours = ParaGlob::Monde()->rank();
       // 1) dans le cas d'un proc 0 ou i, on redéfinit le flux de sortie, pour bufferiser les infos en sortie
       // qui sont ensuite sortie via MPI i-o à l'aide de la méthode Ecriture_base_info_MPI_IO
       // les récupérations et les sorties sont effectuées de manière asynchrone par rapport aux autres proc
       // 2) seul le proc 0 sort l'entête et les paramètres spécifiques de l'algo

       if (proc_en_cours == 0) // première partie dédiée proc 0
        {// dimensionnement de buffer_ioBI_MPI
         std::ostringstream sort; // définition de sort
         
         // === écriture bufferisée des paramètres ===
         this->Ecriture_base_info(sort,cas);
         // === fin écriture bufferisée des paramètres ===
         
         buffer_ioBI_MPI = sort.str(); // on sauvegarde
         taille_buffer_ioBI_MPI = buffer_ioBI_MPI.size(); // def de la taille
         // maintenant on va écrire la taille puis le buffer
         //  === écriture dans flot string de la taille du buffer
         // pour avoir une sortie lisible, on crée tout d'abord un buffer string avec les infos
         std::ostringstream buf(std::ios_base::out);
         buf << "\n BI_MPI_IO_ParaGlob_taille_buffer \n";  // 36 char
         buf << std::right << setw(20) << taille_buffer_ioBI_MPI << " "; // 21 char
          
         // le buffer du stream n'est pas pérenne il faut le récupérer
         string buf_inter1 = buf.str(); // on récupère le buffer
         int taille_buf1 = buf_inter1.size();
         #ifdef MISE_AU_POINT
          if (taille_buf1 != 57)
            {cout << "\n *** sans doute une erreur, taille_buf1 "<<taille_buf1
                  << " est  different de 57 "
                  << "\n  ParaGlob::Ecriture_base_info(..." << endl;
             Sortie(1);
            };
         #endif

         // 1) ==== écriture de la place pour l'écriture de la taille du buffer
         // on encapsule pour ré-utiliser la même trame
         {
           std::ostringstream bufdeb(std::ios_base::out);
           bufdeb << "\ntout_offset_paraglob "; // 22 char
           bufdeb << std::right << setw(20) << taille_buf1 << " "; // 21 char
           string buf_inter2 = bufdeb.str(); // normalement 43 char
           int taille_buf2 = buf_inter2.size();
           #ifdef MISE_AU_POINT
            if (taille_buf2 != 43)
              {cout << "\n *** sans doute une erreur, taille_buf2 "<<taille_buf2
                    << " est  different de 43 "
                    << "\n  ParaGlob::Ecriture_base_info(..." << endl;
               Sortie(1);
              };
           #endif
           // on se positionne au tout début du fichier car ParaGlob est la
           // première info à sortir
           int offset = 0; // récup de l'offset
           int ierr = MPI_File_seek(sort_MPI, offset, MPI_SEEK_SET); // positionnement
           if (ierr) // traitement erreur éventuelle
               {cout << "\n *** erreur de positionnement MPI_File_seek: code " << ierr << " , stop Herezh "
                     << "\n proc 0, ecriture buf0_Maillages "
                     << "\n ParaGlob::Ecriture_base_info(..." << endl;
                Sortie(1);
               };
           // sortie sur fichier de la taille de buf2
           MPI_Status status; // pour le retour
           ierr = MPI_File_write(sort_MPI,buf_inter2.c_str(),taille_buf2,MPI_CHAR,&status);
           if (ierr)
             {cout << "\n *** erreur d'acces a MPI_File_write: code " << ierr << " , stop Herezh "
                   << "\n proc 0, ecriture taille_buf1 "
                   << "\n  ParaGlob::Ecriture_base_info(..." << endl;
              Sortie(1);
             };
         };

         // 2) ==== écriture de la taille du buffer
         { // sortie sur fichier de buf
           MPI_Status status; // pour le retour
           int ierr = MPI_File_write(sort_MPI,buf_inter1.c_str(),taille_buf1,MPI_CHAR,&status);
           if (ierr)
             {cout << "\n *** erreur d'acces a MPI_File_write: code " << ierr << " , stop Herezh "
                   << "\n proc 0, ecriture  buf_inter1 "
                   << "\n  ParaGlob::Ecriture_base_info(..." << endl;
              Sortie(1);
             };
         };

         // 3) ==== enfin écriture du buffer
         { MPI_Status status; // pour le retour
           int ierr = MPI_File_write(sort_MPI,buffer_ioBI_MPI.c_str()
                                        ,taille_buffer_ioBI_MPI,MPI_CHAR,&status);
           if (ierr)
             {cout << "\n *** erreur d'acces a MPI_File_write: code " << ierr << " , stop Herezh "
                   << "\n proc 0, ecriture  buffer_ioBI_MPI "
                   << "\n  ParaGlob::Ecriture_base_info(..." << endl;
              Sortie(1);
             };
         };
        }; // fin du cas du proc 0

   };

// retour de la taille globale des sorties retardées
// concernant les sorties sur .BI
int ParaGlob::Taille_buffer_sortie_BI()
  { int taille =0;
    taille += 43; // écriture de buf_inter2
    taille += 57;  // écriture de buf_inter1
    taille += taille_buffer_ioBI_MPI; // écriture du buffer
    return taille;
  };

#endif


// ajout d'une nouvelle grandeur qui restera ensuite disponible
void ParaGlob::Ajout_grandeur_consultable(void* grandeur, Enum_GrandeurGlobale enu)
  { // les grandeurs sont systématiquement et uniquement des types quelconque
    TypeQuelconque* gr_quelc = (TypeQuelconque*) grandeur;
   
//     // liste des grandeurs consultables de partout sous forme d'un arbre pour faciliter la recherche
//     std::map < Enum_GrandeurGlobale, const void * , std::less <Enum_GrandeurGlobale> > listegrandeurs;
    std::map < Enum_GrandeurGlobale, const void * , std::less <Enum_GrandeurGlobale> >::iterator il;
    il = listegrandeurs.find(enu);
    if (il == listegrandeurs.end())
      // cas où la grandeur n'existe pas, on l'enregistre
      {TypeQuelconque* nouveau_tq = new TypeQuelconque(*gr_quelc);
       listegrandeurs[enu] = nouveau_tq;
      }
    else // sinon comme elle existe, on peut faire un cast
     { TypeQuelconque* gr_ici = (TypeQuelconque*) listegrandeurs.at(enu);
       TypeQuelconque::Grandeur* gr_ici_pt = gr_ici->Grandeur_pointee();
       TypeQuelconque::Grandeur* gr_quelc_pt = gr_quelc->Grandeur_pointee();
       // on vérifie que la place est identique sinon on couine
       if (   (gr_ici_pt->Type_grandeurAssocie() != gr_quelc_pt->Type_grandeurAssocie() )
            || (gr_ici_pt->Type_structure_grandeurAssocie() != gr_quelc_pt->Type_structure_grandeurAssocie() )
            || (gr_ici_pt->Type_enumGrandeurParticuliere() != gr_quelc_pt->Type_enumGrandeurParticuliere() )
            || (gr_quelc->EnuTypeQuelconque() != GENERIQUE_UNE_GRANDEUR_GLOBALE)
           )
         { cout << "\n *** erreur , la grandeur "<<Nom_GrandeurGlobale(enu) << " existe deja mais avec une place differente ";
           if (nivImpression > 5)
             {cout << "\n ParaGlob::Ajout_grandeur_consultable(..";};
           Sortie(1);
         };
       // sinon tout va bien
      };
  };

// récupération d'une grandeur quelconque pour modification
// après l'appel de cette méthode, la grandeur quelconque est réputée updaté
void * ParaGlob::Mise_a_jour_grandeur_consultable(Enum_GrandeurGlobale enu)
  { if (listegrandeurs.find(enu) == listegrandeurs.end())
     { cout << "\n *** erreur en acces à la grandeur "<<Nom_GrandeurGlobale(enu)
            << " pour modification, or la grandeur n'existe pas "
            << endl;
       Sortie(1);
       return NULL; // pour taire le compilo
     };
    TypeQuelconque* gr_ici = (TypeQuelconque*) listegrandeurs.at(enu);
    return gr_ici;
  };



// raccourci pour mettre à jour une grandeur consultable de type: Grandeur_scalaire_double
void  ParaGlob::Mise_a_jour_grandeur_consultable_Scalaire_double(Enum_GrandeurGlobale enu,const double& valeur)
  { if (listegrandeurs.find(enu) == listegrandeurs.end())
     { cout << "\n *** erreur en acces à la grandeur "<<Nom_GrandeurGlobale(enu)
            << " pour modification, or la grandeur n'existe pas "
            << endl;
       Sortie(1);
     };
    TypeQuelconque* pt_quelc = (TypeQuelconque*) listegrandeurs.at(enu);
    Grandeur_scalaire_double& gr
                   = *((Grandeur_scalaire_double*) pt_quelc->Grandeur_pointee()); // pour simplifier
    *(gr.ConteneurDouble()) = valeur;
  };

// suppression d'une grandeur
void ParaGlob::Suppression_grandeur_consultable(Enum_GrandeurGlobale enu)
//// suppression d'une grandeur
//void ParaGlob::Suppression_place_grandeur_consultable(string& nom)
  { // on regarde si la grandeur existe déjà,
    std::map < Enum_GrandeurGlobale, const void * , std::less <Enum_GrandeurGlobale> >::iterator il=listegrandeurs.find(enu);
    if (il == listegrandeurs.end())
      // cas où la grandeur n'existe pas  on couine
      { cout << "\n *** erreur , la grandeur "<<Nom_GrandeurGlobale(enu)
             << " n'existe pas, on ne peut pas la supprimer ";
        if (nivImpression > 5)
             {cout << "\n ParaGlob::Suppression_place_grandeur_consultable(..";};
        Sortie(1);
      }
    else // on supprime
     {listegrandeurs.erase(il);};
  };


// ajout d'une nouvelle grandeur qui restera ensuite disponible
// mais ici pour une grandeur typée par une chaine de caractère,
void ParaGlob::Ajout_grandeur_consultable(void* grandeur, string nom_ref)
  { // les grandeurs sont systématiquement et uniquement des types quelconque
    TypeQuelconque* gr_quelc = (TypeQuelconque*) grandeur;
   
//     // liste des grandeurs consultables de partout sous forme d'un arbre pour faciliter la recherche
//     std::map < Enum_GrandeurGlobale, const void * , std::less <Enum_GrandeurGlobale> > listegrandeurs;
    std::map < string, const void * , std::less <string> >::iterator il;
    il = listegrandeurs_par_string.find(nom_ref);
    if (il == listegrandeurs_par_string.end())
      // cas où la grandeur n'existe pas, on l'enregistre
      {TypeQuelconque* nouveau_tq = new TypeQuelconque(*gr_quelc);
       listegrandeurs_par_string[nom_ref] = nouveau_tq;
      }
    else // sinon comme elle existe, on peut faire un cast
     { TypeQuelconque* gr_ici = (TypeQuelconque*) listegrandeurs_par_string.at(nom_ref);
       TypeQuelconque::Grandeur* gr_ici_pt = gr_ici->Grandeur_pointee();
       TypeQuelconque::Grandeur* gr_quelc_pt = gr_quelc->Grandeur_pointee();
       // on vérifie que la place est identique sinon on couine
       if (   (gr_ici_pt->Type_grandeurAssocie() != gr_quelc_pt->Type_grandeurAssocie() )
            || (gr_ici_pt->Type_structure_grandeurAssocie() != gr_quelc_pt->Type_structure_grandeurAssocie() )
            || (gr_ici_pt->Type_enumGrandeurParticuliere() != gr_quelc_pt->Type_enumGrandeurParticuliere() )
// pas forcément ici            || (gr_quelc->EnuTypeQuelconque() != GENERIQUE_UNE_GRANDEUR_GLOBALE)
           )
         { cout << "\n *** erreur , la grandeur "<<nom_ref << " existe deja mais avec une place differente ";
           if (nivImpression > 5)
             {cout << "\n ParaGlob::Ajout_grandeur_consultable(..";};
           Sortie(1);
         };
       // sinon tout va bien
      };
  };

// récupération d'une grandeur quelconque pour modification
// mais ici pour une grandeur typée par une chaine de caractère,
// après l'appel de cette méthode, la grandeur quelconque est réputée updaté
void * ParaGlob::Mise_a_jour_grandeur_consultable(const string& nom_ref)
  { if (listegrandeurs_par_string.find(nom_ref) == listegrandeurs_par_string.end())
     { cout << "\n *** erreur en acces à la grandeur "<< nom_ref
            << " pour modification, or la grandeur n'existe pas "
            << endl;
       Sortie(1);
       return NULL; // pour taire le compilo
     };
    TypeQuelconque* gr_ici = (TypeQuelconque*) listegrandeurs_par_string.at(nom_ref);
    return gr_ici;
  };
void  ParaGlob::Mise_a_jour_grandeur_consultable_Scalaire_double(const string& nom_ref,const double& valeur)
  { if (listegrandeurs_par_string.find(nom_ref) == listegrandeurs_par_string.end())
     { cout << "\n *** erreur en acces à la grandeur "<< nom_ref
            << " pour modification, or la grandeur n'existe pas "
            << endl;
       Sortie(1);
     };
    TypeQuelconque* gr_ici = (TypeQuelconque*) listegrandeurs_par_string.at(nom_ref);
    Grandeur_scalaire_double& gr
                   = *((Grandeur_scalaire_double*) gr_ici->Grandeur_pointee()); // pour simplifier
    *(gr.ConteneurDouble()) = valeur;   
 };

// suppression d'une grandeur
// mais ici pour une grandeur typée par une chaine de caractère,
void ParaGlob::Suppression_grandeur_consultable(const string& nom_ref)
//// suppression d'une grandeur
//void ParaGlob::Suppression_place_grandeur_consultable(string& nom)
  { // on regarde si la grandeur existe déjà,
    std::map < string, const void * , std::less <string> >::iterator il=listegrandeurs_par_string.find(nom_ref);
    if (il == listegrandeurs_par_string.end())
      // cas où la grandeur n'existe pas  on couine
      { cout << "\n *** erreur , la grandeur "<< nom_ref
             << " n'existe pas, on ne peut pas la supprimer ";
        if (nivImpression > 5)
             {cout << "\n ParaGlob::Suppression_place_grandeur_consultable(..";};
        Sortie(1);
      }
    else // on supprime
     {listegrandeurs_par_string.erase(il);};
  };

// affichage de la liste des noms de grandeurs actuelles accessibles globalement
void ParaGlob::Affiche_GrandeurGlobal(ostream & sort) const
{
  sort << "\n --- liste des grandeurs consultables de partout --- ";
  if (listegrandeurs.size() != 0)
   {// tout d'abord les grandeurs de types énuméré
    sort << "\n  === les enumeres : === \n ";
    std::map < Enum_GrandeurGlobale, const void * , std::less <Enum_GrandeurGlobale> >::const_iterator il,ilfin;
    ilfin=listegrandeurs.end(); il = listegrandeurs.begin();
    for (;il != ilfin;il++)
     sort << Nom_GrandeurGlobale(il->first)<< "\n ";
   };
  if (listegrandeurs_par_string.size() != 0)
   {// puis les grandeurs typée par une chaine de caractères
    sort << "\n === les grandeurs typees par une chaine de caracteres : ===\n ";
    std::map < string, const void * , std::less <string> >::const_iterator il,ilfin;
    ilfin=listegrandeurs_par_string.end(); il = listegrandeurs_par_string.begin();
    for (;il != ilfin;il++)
     sort << (il->first)<< "\n ";
   };
  sort << endl;
};

// récupération de la liste des noms de grandeurs actuelles accessibles globalement
// la liste passée en paramètre est vidée puis remplie par les grandeurs actuelles
void ParaGlob::Recup_list_GrandeurGlobal(list <string >& list_grandeurs_globals) const
{ list_grandeurs_globals.clear();
  if (listegrandeurs.size() != 0)
   {// tout d'abord les grandeurs de types énuméré
    std::map < Enum_GrandeurGlobale, const void * , std::less <Enum_GrandeurGlobale> >::const_iterator il,ilfin;
    ilfin=listegrandeurs.end(); il = listegrandeurs.begin();
    for (;il != ilfin;il++)
      list_grandeurs_globals.push_back(Nom_GrandeurGlobale(il->first));
   };
  if (listegrandeurs_par_string.size() != 0)
   {// puis les grandeurs typée par une chaine de caractères
    std::map < string, const void * , std::less <string> >::const_iterator il,ilfin;
    ilfin=listegrandeurs_par_string.end(); il = listegrandeurs_par_string.begin();
    for (;il != ilfin;il++)
     list_grandeurs_globals.push_back(il->first);
   };
};

// récup d'une composante d'une grandeur quelconque d'une grandeur globale
const void * ParaGlob::GrandeurGlobal_relaie(const string& nom_ref ) const
  {// il s'agit d'une variable utilisateur relai
   // on commence par récupérer le conteneur
   std::map< string, const void * , std::less <string> >::const_iterator
   il_ref =listegrandeurs_par_string.find(nom_ref);
   if (il_ref == listegrandeurs_par_string.end())
      {return NULL;} // cas où la variable pointée n'existe pas
    else
      {// cas où on a la variable pointée
       // on l'affecte
       TypeQuelconque* gr_quelc = (TypeQuelconque*) (il_ref->second);
       // on récupère le conteneur multidimensionnel associé
       const string* nom_cont = gr_quelc->Const_Grandeur_pointee()->Nom_ref();
       if (nom_cont != NULL)
        { std::map< string, const void * , std::less <string> >::const_iterator
            it=listegrandeurs_par_string.find(*nom_cont);
          if (it == listegrandeurs_par_string.end())
            {return NULL;} // cas où le conteneur multi n'existe pas
          else
            {// cas où on a le conteneur
             // on le récupère
             const TypeQuelconque* gr_quelc_cont = (const TypeQuelconque*) (it->second);
             // on affecte le conteneur scalaire
             Grandeur_Double_Nommer_indicer& tyTQ= *((Grandeur_Double_Nommer_indicer*) gr_quelc->Grandeur_pointee());
             *(tyTQ.ConteneurDouble()) = gr_quelc_cont->GrandeurNumOrdre(tyTQ.Indice_const());
             // retour du conteneur modifié
             return (il_ref->second);
            }
        }
       else // le conteneur multi n'est pas disponible
        { return NULL; }; // on ramène un pointeur NULL
      };
  };




