
// 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 "LesContacts.h"
#include <vector>
#include "ReferenceNE.h"
#include "ReferenceAF.h"
#include "CharUtil.h"

#include "Enum_TypeQuelconque.h"
#include "TypeQuelconqueParticulier.h"
#include "FrontPointF.h"


 

// récupération des ddl ou des grandeurs actives de tdt vers t 
void LesContacts::TdtversT()
	{  LaLIST <ElContact>::iterator il,ilfin=listContact.end();
    for (il=listContact.begin();il != ilfin; il++)
          (*il).TdtversT();
    // on nettoie les indicateurs transitoires
    listContact_nouveau_tatdt.clear();
    listContact_efface_tatdt.clear();
	};

// actualisation des ddl et des grandeurs actives de t vers tdt
// dans le cas //, on ajoute dans les messages, le num du prog
void LesContacts::TversTdt()
	{
  #ifdef UTILISATION_MPI
    int proc_en_cours = ParaGlob::Monde()->rank();
  #endif

    // on essaie de mettre la situation de contact comme elle était à t
    // a) on supprime les nouveaux contacts
    {LaLIST <LaLIST <ElContact>::iterator>::iterator al,alfin=listContact_nouveau_tatdt.end();
     for (al=listContact_nouveau_tatdt.begin();al != alfin; al++)
       { ElContact& elc = (*(*al));
         Noeud* no = elc.Esclave();
         int n_noee = no->Num_noeud();
         int num_mail_noe_esclave = no->Num_Mail();
         #ifdef MISE_AU_POINT
         if (tesN_encontact(num_mail_noe_esclave).find(no)
                 == tesN_encontact(num_mail_noe_esclave).end() )
            {
            #ifdef UTILISATION_MPI
              cout << "\n proc " << proc_en_cours
            #else
              cout << "\n"
            #endif
                   << "*** Erreur : on ne trouve pas la liste d'element en contact avec le noeud esclave "
                   << n_noee << " du maillage " << num_mail_noe_esclave
                   << " la suite n'est pas possible "
                   << " LesContacts::TversTdt(.. \n";
              Sortie(1);
            };
         #endif
         LaLIST < LaLIST<ElContact>::iterator >  & list_tesN
                            = tesN_encontact(num_mail_noe_esclave)[no];
         list_tesN.remove(*al);
//         tesN_encontact(num_mail_noe_esclave)(n_noee).remove(*al);
         listContact.erase(*al);
// cout << "\n debug toto ";
        };
    };
    // b) on rajoute les contacts supprimés
    {LaLIST <ElContact>::iterator al,alfin=listContact_efface_tatdt.end();
     for (al=listContact_efface_tatdt.begin();al != alfin; al++)
       { ElContact& elc = (*al);
         Noeud* no = elc.Esclave();
         int n_noee = no->Num_noeud();
         int num_mail_noe_esclave = no->Num_Mail();
         
      // je supprime le test suivant, car on pourrait peut-être avoir en début d'incrément précédent,
      // deux elements de contact avec le même noeud esclave mais pas la même frontière
      // qui ensuite à cause d'un changement de frontière, deviennent identique -> un des deux est inactivé
      // mais il y en a deux identiques.
      // du coup si on revient en arrière c'est de ne pas tenir compte du doublon et s'il y a besoin, au niveau de Nouveau
      // on aura une création d'un nouvel élément
//         #ifdef MISE_AU_POINT
//          if (Element_contact_deja_present(*al))
//           {
//          #ifdef UTILISATION_MPI
//            cout << "\n proc " << proc_en_cours
//          #else
//            cout << "\n"
//          #endif
//                 << "\n*** Erreur : l'element de contact existe déjà, ce n'est pas normal "
//                 << (*al).Esclave()->Num_noeud()
//                 << " du maillage " << (*al).Esclave()->Num_Mail()
//                 << " la suite n'est pas possible "
//                 << " LesContacts::TversTdt(.. \n";
//              tempsContact.Arret_du_comptage(); // fin cpu
//              Sortie(1);
//            };
//         #endif
         if (!(Element_contact_deja_present(*al)))
           listContact.push_front(*al);
         // on met à jour le tableau tesN_encontact
         #ifdef MISE_AU_POINT
           if (tesN_encontact(num_mail_noe_esclave).find(no)
                 == tesN_encontact(num_mail_noe_esclave).end() )
            {
            #ifdef UTILISATION_MPI
              cout << "\n proc " << proc_en_cours
            #else
              cout << "\n"
            #endif
                   << "\n*** Erreur : on ne trouve pas la liste d'element en contact avec le noeud esclave "
                   << n_noee << " du maillage " << num_mail_noe_esclave
                   << " la suite n'est pas possible "
                   << " LesContacts::TversTdt(.. \n";
              Sortie(1);
            };
         #endif
         tesN_encontact(num_mail_noe_esclave)[no].push_front(listContact.begin());
        };
    };
    // c) on nettoie les indicateurs transitoires
    listContact_nouveau_tatdt.clear();
    listContact_efface_tatdt.clear();
    // d) on revient à t pour les éléments en contact
    {LaLIST <ElContact>::iterator il,ilfin=listContact.end();
     for (il=listContact.begin();il != ilfin; il++)
          (*il).TversTdt();
    };
    Calcul_Nb_contact_actif();
    if ((ParaGlob::NiveauImpression() > 4)  || (Permet_affichage() > 4 ))
      #ifdef UTILISATION_MPI
        cout << "\n proc " << proc_en_cours
      #else
        cout << "\n"
      #endif
             << "\n >> LesContacts::TversTdt : nb contact actif = "<< nb_contact_actif  << endl ;
	};
	 
		
// cas d'une méthode avec pénalisation: calcul éventuel d'un pas de temps idéal, 
// si oui retour de la valeur delta_t proposé
// sinon dans tous les autres cas retour de 0.
// le calcul se fait en fonction du pas de temps courant et de la pénétration
// donc nécessite que le contact ait déjà été étudié
// Dans le cas d'un   calcul  parallèle, il y a transfert du résultat au  cpu 0
// seules les cpu i calculent le pas de temps idéal pour leur portion et le proc 0 choisit le min
double LesContacts::Pas_de_temps_ideal()
{ tempsContact.Mise_en_route_du_comptage(); // def deb compt
  double delta_optimal=ConstMath::tresgrand;
#ifdef UTILISATION_MPI
  int proc_en_cours = ParaGlob::Monde()->rank();
  if (proc_en_cours != 0)
   {
#endif
     {
      // on passe en revue tous les contacts
      LaLIST <ElContact>::const_iterator il,ilfin=listContact.end();
      for (il=listContact.begin();il != ilfin; il++)
        { double dtelem_optimal = (*il).Pas_de_temps_ideal();
          if (dtelem_optimal != 0.)
            delta_optimal = MiN(delta_optimal, dtelem_optimal);
        };
      // retour du temps proposé
      if (delta_optimal==ConstMath::tresgrand)
        // cela veut dire qu'il n'a pas été modifié, on le met à 0 pour indiquer que l'on a rien
        // n'a proposer
        delta_optimal = 0.;
      tempsContact.Arret_du_comptage(); // fin cpu
     }
#ifdef UTILISATION_MPI
    temps_transfert_court.Mise_en_route_du_comptage(); // comptage cpu
    // maintenant on va   transmettre au cpu 0
    mpi::request reqs1 = ParaGlob::Monde()->isend(0, 48, delta_optimal);
    // on attend pas
    temps_transfert_court.Arret_du_comptage(); // fin comptage cpu
   }
  else // cas du cpu 0
   {// l'objectif ici est de récupérer  les infos
    tempsContact.Arret_du_comptage(); // fin cpu
    int nb_proc_terminer = 0; // permettra de terminer
    while (nb_proc_terminer < (ParaGlob::Monde()->size()-1)) // gérer par les valeurs de tyfront
     { // on récupère un résultat de cpu i
       temps_transfert_court.Mise_en_route_du_comptage(); // comptage cpu
       double dtelem_optimal ;
       mpi::request reqs1 = ParaGlob::Monde()->irecv(mpi::any_source, 48, dtelem_optimal );
       reqs1.wait(); // on attend que le conteneur soit rempli
       delta_optimal = MiN(delta_optimal, dtelem_optimal);
       nb_proc_terminer++; //  on prend en compte que l'on a récupéré un conteneur
     };
   };
 // il faut que tous les proc aient le retour global, car le retour sert dans l'algo général
 broadcast(*ParaGlob::Monde(), delta_optimal, 0);
#endif

  return delta_optimal;

};

//   écriture base info   
// cas donne le niveau de sauvegarde
void LesContacts::Ecri_base_info_LesContacts(ostream& sort)
  {
  
#ifdef UTILISATION_MPI
   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 le temps cpu du proc 0
   // 3) seules les proc i sortent les éléments de contact
   
   // tout d'abord le proc 0 doit récupérer le nombre total d'élément
   int nb_elem_contact = 0; // init
   MPI_Offset taille_buffer_proc = 0; // variable inter qui stocke la taille
     // buffer pour le proc i != 0 en cours
   int taille_elem_cont_du_proc = 0;
   if ( proc_en_cours != 0)
      taille_elem_cont_du_proc = listContact.size();
   reduce(*ParaGlob::Monde(), taille_elem_cont_du_proc, nb_elem_contact, std::plus<int>(), 0);
   // le proc 0 écrit le nombre d'elcontact
   if (proc_en_cours == 0)
    {std::ostringstream sort; // redéfinition de sort}
     sort << "\n LesContacts: taille " << nb_elem_contact << " " ;
//     sort << "\n ";
     buffer_ioBI_MPI(1) = sort.str(); // on sauvegarde
    };

#else // sinon cas d'un mono proc

   // globalement on sauvegarde toujours tout, car les éléments de contact peuvent apparaître à tout moment
   // tout d'abord on indique le type
  { sort << "\n LesContacts: taille " << listContact.size() << "  " ;};
#endif
   // NB: le nombre de maillages esclave et en auto-contact, peut-être redéfinit lors d'un restart
   // donc n'est pas sauvegardé
#ifdef UTILISATION_MPI
   // sauvegarde des éléments de contact par les proc i
   if (proc_en_cours != 0) // seules les proc i gèrent les éléments de contact
    {std::ostringstream sort; // redéfinition de sort
#endif
     // ensuite on sauvegarde la liste des éléments de contact: en fait tout d'abord les infos
     // qui permettent de construire un élément ad hoc, et ensuite les infos spécifiques internes à l'élément
     for (LaLIST <ElContact>::iterator ic = listContact.begin();ic != listContact.end(); ic++)
       {// les infos permettants de contruire l'élément de contact: on considère que les numéros des noeuds, maillages, éléments
        // restent identiques, par contre on peut avoir un échange des zones de restriction de contact d'une sauvegarde à un restart
        sort << "\nN_es " << (*ic).Esclave()->Num_Mail() <<" " << (*ic).Esclave()->Num_noeud();  // le noeud esclave
        Front * elfront = (*ic).Elfront();
        sort << " El " << elfront->NumMail() << " " << elfront->PtEI()->Num_elt_const() << " "
             <<  elfront->Eleme()->Type_geom_front()  << " "<< elfront->Num_frontiere() ; // la frontière
        // on fait de même pour la frontière initiale
        Front * elfront_initiale = (*ic).Elfront_initiale();
        sort << " El_0 " << elfront_initiale->NumMail() << " " << elfront_initiale->PtEI()->Num_elt_const() << " "
             <<  elfront_initiale->Eleme()->Type_geom_front()  << " "<< elfront_initiale->Num_frontiere() ; // la frontière initiale
        // les infos spécifiques éventuelles
        (*ic).Ecri_base_info_ElContact(sort);
       };
#ifdef UTILISATION_MPI
     buffer_ioBI_MPI(2) = sort.str(); // on sauvegarde
     taille_buffer_proc = buffer_ioBI_MPI(2).size();
////--- debug
//{   cout << "\n debug LesContacts::Ecri_base_info_LesContacts()"
//         << "\n proc "<< ParaGlob::Monde()->rank()
//         << " buffer_ioBI_MPI(2).size() = " << buffer_ioBI_MPI(2).size()<<endl;
//}
////-- fin debug     //on transmet l'info à proc 0 dans un vecteur de taille nb_proc
//     gather(*ParaGlob::Monde(), taille_buffer_proc, 0);
    }
   else
    {
//    //on récup l'info sur proc 0 dans un vecteur de taille nb_proc
//     taille_buffer_proc = 0; // au cas où
//     gather(*ParaGlob::Monde(), taille_buffer_proc, tailles_buffer_ioBI_MPI, 0);
//
     std::ostringstream sort; // redéfinition de sort}
#endif
     sort << "\n tps_rech_contact: "<<tempsContact;
#ifdef UTILISATION_MPI
     buffer_ioBI_MPI(3) = sort.str(); // on sauvegarde
     
    };
    
   //on transmet l'info à proc 0 dans un vecteur de taille nb_proc
   gather(*ParaGlob::Monde(), taille_buffer_proc, tailles_buffer_ioBI_MPI, 0); 
////--- debug
//{
//    if (proc_en_cours == 0)
//      {cout << "\n debug LesContacts::Ecri_base_info_LesContacts()"
//            << "\n tailles_buffer_ioBI_MPI= ";
//       int nb_process =  ParaGlob::Monde()->size();
//       for (int i=0;i< nb_process;i++)
//        cout << tailles_buffer_ioBI_MPI[i] << "  ";
//      cout << endl;
//     };
//}
////-- fin debug

#endif
  };

#ifdef UTILISATION_MPI
// retour de la taille globale des sorties retardées
// concernant les sorties sur .BI
MPI_Offset LesContacts::Taille_buffer_sortie_BI()
  {MPI_Offset taille_totale = 0; // init
   int proc_en_cours = ParaGlob::Monde()->rank();
   if (proc_en_cours == 0) // partie dédiée proc 0
     {// tout d'abord on prend en compte les buffers propres
      taille_totale += buffer_ioBI_MPI(1).size();
      taille_totale += buffer_ioBI_MPI(3).size();
      // puis on intègre la taille de l'ensemble des offsets:
      MPI_Offset taille_entete = 42 + (4)*21+27; // cf. LesContacts::Ecriture_base_info_MPI_IO
      taille_totale += taille_entete;
      // on ajoute la partie gérée par les proc i
      int nb_process =  ParaGlob::Monde()->size();
      // NB: on exclu le proc 0
      for (int i=1;i<nb_process;i++) // < absolu,
        taille_totale += tailles_buffer_ioBI_MPI[i];
////--- debug
//   cout << "\n debug LesContacts::Taille_buffer_sortie_BI()"
//        << "\n taille_totale= "<<taille_totale
//        << "\n buffer_ioBI_MPI(3).size()= "<<buffer_ioBI_MPI(3).size()
//        <<endl;
//
////-- fin debug
     };
   // retour
   return taille_totale; // seule la valeur retournée par proc 0 est licite
  };

// récupération de l'offset global sur .BI pour la classe
// et mise à jour des offsets de classes dérivées si besoin
void LesContacts::Mise_a_jour_Offset_BI_ecriture(MPI_Offset offset,const int cas)
 {// seul le proc 0 calcule les offsets qui sont ensuites transmis à tous les proc
  // mais il faut que tous les proc passent par toutes les méthodes
  int proc_en_cours = ParaGlob::Monde()->rank();
  if (proc_en_cours == 0)
   {
    // on commence par prendre en compte l'offset général puis ceux propres à proc 0
    // ce sont des données qui se situent avant ceux des proc i
    offset_LesContacts[0] = offset;
    // puis la première partie propre à proc 0
    MPI_Offset a_ajouter = offset+buffer_ioBI_MPI(1).size();;
    // puis on intègre la taille de l'ensemble des offsets:
    // on ne prend en compte que les 4 offsets qui sont indépendants du nb de proc
    MPI_Offset taille_entete = 42 + (4)*21+27; // cf. LesContacts::Ecriture_base_info_MPI_IO
    offset_LesContacts[1] = taille_entete+a_ajouter; // début des éléments de contact
    // maintenant on s'occupe des proc i
    int nb_process =  ParaGlob::Monde()->size();
    // NB: on exclu le proc 0, pour le premier proc
    for (int i=1;i< nb_process;i++)
      //offset_LesContacts[i+1] : pour le proc i+1
      // [nb_process + 1] : pour la deuxième partie de proc 0
      offset_LesContacts[i+1] = offset_LesContacts[i] + tailles_buffer_ioBI_MPI[i];
    // le second buffer de proc 0 vient d'être définit -> offset_LesContacts[nb_process]
    // puis la fin
    offset_LesContacts[nb_process+1] = offset_LesContacts[nb_process] + buffer_ioBI_MPI(3).size();
////--- debug
//   cout << "\n debug LesContacts::Mise_a_jour_Offset_BI_ecriture()";
//   cout << std::right << setw(20) << offset_LesContacts[0] << " ";
//   cout << std::right << setw(20) << offset_LesContacts[1] << " ";
//   cout << std::right << setw(20) << offset_LesContacts[nb_process] << " ";
//   cout << std::right << setw(20) << offset_LesContacts[nb_process+1] << " ";
//   cout     <<endl;
//
////-- fin debug
   };
     
  // on transmets à tous les proc i les offsets
  broadcast(*ParaGlob::Monde(), offset_LesContacts, 0);
 };

// idem en lecture, mais là il s'agit uniquement de la mise à jour
// de l'offset seul puis c'est la classe qui s'en débrouille
void LesContacts::Mise_a_jour_Offset_BI_lecture(MPI_Offset offset,const int cas)
  { offset_LesContacts[0] = offset;
  };

// lecture MPI des infos bufferisées
void LesContacts::Lecture_base_info_MPI_IO(MPI_File * ent_MPI,const int cas)
 { int proc_en_cours = ParaGlob::Monde()->rank();
   int nb_process =  ParaGlob::Monde()->size();

  // tous les proc lisent, on commence par se positionner
 {
  MPI_Offset offset = offset_LesContacts[0]; // récup de l'offset
  int ierr = MPI_File_seek(*ent_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 " << proc_en_cours <<", positionnement initial "
            << "\n LesContacts::Lecture_base_info_MPI_IO(... "<<endl;
       Sortie(1);
      };
 };
  // 1) ==== la taille de l'ensemble des offsets
  MPI_Offset 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);
    if (ierr)
      {cout << "\n *** erreur d'acces a MPI_File_read_all: code " << ierr << " , stop Herezh "
            << "\n proc "<<proc_en_cours << ", lecture  de la taille de l'ensemble des offsets "
            << "\n LesContacts::Lecture_base_info_MPI_IO(..." << endl;
       Sortie(1);
      };
    // 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 != "buf0_LesContacts" )
      {cout << "\n **** erreur on devait lire : 'buf0_LesContacts' "
            << " et on lue "<< toto << " la suite n'est pas possible -> arret !! "
            << "\n LesContacts::Lecture_base_info_MPI_IO(... "<<endl;
       Sortie(1);
      };
    // on lit la taille de buf2
    (*ent_inter)  >> taille_buf;
  };
  // 2) ==== lecture de la taille de l'ensemble des offsets
  { 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);
    if (ierr)
      {cout << "\n *** erreur d'acces a MPI_File_read_all: code " << ierr << " , stop Herezh "
            << "\n proc "<<proc_en_cours << ", lecture  de la taille de l'ensemble des offsets "
            << "\n LesContacts::Lecture_base_info_MPI_IO(..." << endl;
       Sortie(1);
      };
    // 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 != "offset_base_info_MPI_IO" )
      {cout << "\n **** erreur on devait lire : 'offset_base_info_MPI_IO' "
            << " et on lue "<< toto << " la suite n'est pas possible -> arret !! "
            << "\n LesContacts::Lecture_base_info_MPI_IO(... "<<endl;
       Sortie(1);
      };
    // on lit les offsets
    (*ent_inter) >>  offset_LesContacts[0];
    (*ent_inter) >>  offset_LesContacts[1];
    (*ent_inter) >>  offset_LesContacts[nb_process];
    (*ent_inter) >>  offset_LesContacts[nb_process+1];
   };

  // 3) ==== on dimensionne  les_taille_buffer_ioBI_MPI
  MPI_Offset taille_dernier_buffer = 0; // init
  MPI_Offset taille_buffer_tous_les_contacts = 0; // ""
  { MPI_Offset taille_entete = 42 + (4)*21+27; // cf. Ecriture_base_info_MPI_IO

    tailles_buffer_ioBI_MPI[0] = offset_LesContacts[1] - (offset_LesContacts[0]+taille_entete);
    // en lecture tailles_buffer_ioBI_MPI[1] correspond au début de l'ensemble des éléments
    // de contact. Tous les proc lisent le buffer contenant tous les éléments
    // ensuite au niveau de Lec_base_info_LesContacts, chaque proc i récupère
    // uniquement les éléments qui le concerne
    taille_buffer_tous_les_contacts = offset_LesContacts[nb_process] - offset_LesContacts[1];
    // puis le dernier buffer qui sert uniquement à proc 0
    taille_dernier_buffer = offset_LesContacts[nb_process+1] - offset_LesContacts[nb_process];
   };
  
  // 3) ==== lecture du  buffer 1 de proc 0
  //   tous les proc  lisent (lecture collective)
  { char * buffer_car = new char [tailles_buffer_ioBI_MPI[0]] ; // def du tableau de travail
    MPI_Status status;
    int ierr = MPI_File_read_all(*ent_MPI, buffer_car, tailles_buffer_ioBI_MPI[0], MPI_CHAR, &status);
    if (ierr)
      {cout << "\n *** erreur d'acces a MPI_File_read_all: code " << ierr << " , stop Herezh "
            << "\n proc "<<proc_en_cours << ", lecture  buffer1 "
            << "\n LesContacts::Lecture_base_info_MPI_IO(..." << endl;
       Sortie(1);
      };
    buffer_ioBI_MPI(1).resize(tailles_buffer_ioBI_MPI[0]);
    buffer_ioBI_MPI(1).replace(0,tailles_buffer_ioBI_MPI[0],buffer_car);
  };
  
  // 4) ==== lecture du  buffer 2 qui correspond à tous les éléments de contact
  //   tous les proc lisent
  { char * buffer_car = new char [taille_buffer_tous_les_contacts] ; // def du tableau de travail
    MPI_Status status;
    int ierr = MPI_File_read_all(*ent_MPI, buffer_car, taille_buffer_tous_les_contacts, MPI_CHAR, &status);
    if (ierr)
      {cout << "\n *** erreur d'acces a MPI_File_read_all: code " << ierr << " , stop Herezh "
            << "\n proc "<<proc_en_cours << ", lecture  buffer contenant tous contacts "
            << "\n LesContacts::Lecture_base_info_MPI_IO(..." << endl;
       Sortie(1);
      };
    buffer_ioBI_MPI(2).resize(taille_buffer_tous_les_contacts);
    buffer_ioBI_MPI(2).replace(0,taille_buffer_tous_les_contacts,buffer_car);
  };
  
  // 5) ==== lecture du  buffer 2 (donc le dernier) de proc 0
  //   seul le proc 0 lit
  { char * buffer_car = new char [taille_dernier_buffer] ; // def du tableau de travail
    MPI_Offset offset = offset_LesContacts[nb_process]; // récup de l'offset
    int ierr = MPI_File_seek(*ent_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, lecture buffer 2 de proc 0 "
              << "\n LesContacts::Lecture_base_info_MPI_IO(... "<<endl;
         Sortie(1);
        };
    MPI_Status status;
    ierr = MPI_File_read(*ent_MPI, buffer_car, taille_dernier_buffer, MPI_CHAR, &status);
    if (ierr)
      {cout << "\n *** erreur d'acces a MPI_File_read_all: code " << ierr << " , stop Herezh "
            << "\n proc 0, lecture  buffer1 "
            << "\n LesContacts::Lecture_base_info_MPI_IO(..." << endl;
       Sortie(1);
      };
    buffer_ioBI_MPI(3).resize(taille_dernier_buffer);
    buffer_ioBI_MPI(3).replace(0,taille_dernier_buffer,buffer_car);
  };

 };
  
// écriture MPI des infos bufferisées
void LesContacts::Ecriture_base_info_MPI_IO(MPI_File * sort_MPI, const int cas)
  { int proc_en_cours = ParaGlob::Monde()->rank();
    int nb_process =  ParaGlob::Monde()->size();
    // si proc 0, on sort les ref
    if (proc_en_cours == 0)
     {//  === écriture dans flot string de la taille des buffers propres
      // pour avoir une sortie lisible, on crée tout d'abord un buffer string avec les infos
      std::ostringstream buf2(std::ios_base::out);
      buf2 << "\n offset_base_info_MPI_IO \n"; // 27 char
      // ici on sort uniquement les parties indépendantes du nb de proc
      // 4 positions
      buf2 << std::right << setw(20) << offset_LesContacts[0] << " ";
      buf2 << std::right << setw(20) << offset_LesContacts[1] << " ";
      buf2 << std::right << setw(20) << offset_LesContacts[nb_process] << " ";
      buf2 << std::right << setw(20) << offset_LesContacts[nb_process+1] << " ";

      // le buffer du stream n'est pas pérenne il faut le récupérer
      string buf_inter2 = buf2.str(); // on récupère le buffer
      MPI_Offset taille_buf = buf_inter2.size();
      #ifdef MISE_AU_POINT
       if (taille_buf != (4)*21+27) // 4 positions
         {cout << "\n *** sans doute erreur, taille_buf "<<taille_buf
               << " est  different de (taille_offset)*21+27 = " << (3)*21+27
               << "\n  LesContacts::Ecriture_base_info_MPI_IO(..." << endl;
          Sortie(1);
         };
      #endif

      // 1) ==== écriture de l'entête
      // on encapsule pour ré-utiliser la même trame
      {
        std::ostringstream bufdeb(std::ios_base::out);
        bufdeb << "\n  buf0_LesContacts  "; // 21 char
        bufdeb << std::right << setw(20) << taille_buf; // on justifie à droite et on utilise 20 char
        bufdeb << " "; // 1 char
        string buf_inter = bufdeb.str(); // normalement 42 char
        // sortie sur fichier de la taille de buf2
        MPI_Status status; // pour le retour
        int ierr = MPI_File_write(*sort_MPI,buf_inter.c_str(),42,MPI_CHAR,&status);
        if (ierr)
          {cout << "\n *** erreur d'acces a MPI_File_write: code " << ierr << " , stop Herezh "
                << "\n proc 0, ecriture entete buffer "
                << "\n  LesContacts::Ecriture_base_info_MPI_IO(..." << endl;
           Sortie(1);
          };

      };

      // 2) ==== écriture des offsets indépendant du nb de proc c-a-d sortie sur fichier de buf2
      { MPI_Status status; // pour le retour
        int ierr = MPI_File_write(*sort_MPI,buf_inter2.c_str(),taille_buf,MPI_CHAR,&status);
        if (ierr)
          {cout << "\n *** erreur d'acces a MPI_File_write: code " << ierr << " , stop Herezh "
                << "\n proc 0, ecriture des offsets "
                << "\n  LesContacts::Ecriture_base_info_MPI_IO(..." << endl;
           Sortie(1);
          };
      };


      // 3) ==== écriture des 2  buffers propres au proc 0
      // le premier à suivre
      { MPI_Status status; // pour le retour
        int ierr = MPI_File_write(*sort_MPI,buffer_ioBI_MPI(1).c_str()
                                     ,buffer_ioBI_MPI(1).size(),MPI_CHAR,&status);
        if (ierr)
            {cout << "\n *** erreur d'acces a MPI_File_write: code " << ierr << " , stop Herezh "
                  << "\n proc 0, ecriture premier buffer propre "
                  << "\n  LesContacts::Ecriture_base_info_MPI_IO(..." << endl;
             Sortie(1);
         };
      };
      // pour le deuxième il faut se décaler
      {MPI_Offset offset = offset_LesContacts[nb_process]; // récup de l'offset
////--- debug
//   cout << "\n debug LesContacts::Ecriture_base_info_MPI_IO()"
//        << "\n offset= "<<offset
//        << "\n buffer_ioBI_MPI(3)= "<<buffer_ioBI_MPI(3)
//        <<endl;
//
////-- fin debug
       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_LesContacts "
                 << "\n  LesContacts::Ecriture_base_info_MPI_IO(..." << endl;
            Sortie(1);
           };
        MPI_Status status; // pour le retour
        ierr = MPI_File_write(*sort_MPI,buffer_ioBI_MPI(3).c_str()
                                     ,buffer_ioBI_MPI(3).size(),MPI_CHAR,&status);
        if (ierr)
            {cout << "\n *** erreur d'acces a MPI_File_write: code " << ierr << " , stop Herezh "
                  << "\n proc 0, ecriture deuxieme buffer propre "
                  << "\n  LesContacts::Ecriture_base_info_MPI_IO(..." << endl;
             Sortie(1);
         };
      };
     }
    else // cas des proc i != 0, on sort les éléments de contact concernant le proc i
         // tous les el de contact se suivent, il n'y a pas de numérotation
         // pour chaque proc i , il faut se décaler
     {if (buffer_ioBI_MPI(2).size())
       {MPI_Offset offset = offset_LesContacts[proc_en_cours]; // 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 " << proc_en_cours <<", ecriture des elements de Contacts "
                  << "\n  LesContacts::Ecriture_base_info_MPI_IO(..." << endl;
             Sortie(1);
            };
         MPI_Status status; // pour le retour
         ierr = MPI_File_write(*sort_MPI,buffer_ioBI_MPI(2).c_str()
                                      ,buffer_ioBI_MPI(2).size(),MPI_CHAR,&status);
         if (ierr)
             {cout << "\n *** erreur d'acces a MPI_File_write: code " << ierr << " , stop Herezh "
                   << "\n proc " << proc_en_cours <<", ecriture des elements de Contacts "
                   << "\n  LesContacts::Ecriture_base_info_MPI_IO(..." << endl;
              Sortie(1);
          };
       };
     }

  };
  
#endif
     

  // Il s'agit ici de mettre à jour les conteneurs stockés aux noeuds et/ou aux éléments
  // qui servent à récupérer les infos liés aux contact correspondant à liTQ
  // actuellement les conteneurs passés en paramètre  servent que pour
  // les énumérés, et les informations résultantes sont stockées au niveau des noeuds
  // constituant les éléments de contact
  // absolue: indique si oui ou non on sort les tenseurs dans la base absolue ou une base particulière
  //--> important : les conteneurs sont supposés initialisés avec l'appel
void LesContacts::Mise_a_jour_Pour_Grandeur_particuliere(
                                List_io < TypeQuelconque >& li_restreinte_TQ
                               )
{
   LaLIST<ElContact>::iterator ili_deb= listContact.begin();
   LaLIST<ElContact>::iterator ilifin= listContact.end();
   LaLIST<ElContact>::iterator ili;

   // on passe en revue la liste de quelconque
  {List_io<TypeQuelconque>::iterator itq,itqfin=li_restreinte_TQ.end();
   for (itq=li_restreinte_TQ.begin();itq!=itqfin;itq++)
     {const TypeQuelconque_enum_etendu& enuTypeQuelconque = (*itq).EnuTypeQuelconque();
      if (enuTypeQuelconque.Nom_vide()) // veut dire que c'est un enum pur
       switch (enuTypeQuelconque.EnumTQ())
       {  case NOEUD_PROJECTILE_EN_CONTACT:
           {//  ----- cas des noeuds projectiles en contact
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              // on attribue une grandeur arbitraire de 100 aux noeuds pour un  contact actif
              // pour les éléments de contact inactif -> -0.1
              // un noeud peut être plusieurs fois en contact -> les nombres sont cumulées
              // on fait l'hypothèse qu'un ne sera jamais inactif plus de 99 fois, du coup
              // en regardant le nombre modulo 100 on peut en déduire le nombre d'inactivité
              // et en regardant le nombre de centaine on en déduit le nombre de contact
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) += 100.; }
              else
               {*(tyTQ.ConteneurDouble()) -= 0.1; }
             };
            break;
           }
          case NOEUD_FACETTE_EN_CONTACT:
           {//  ----- cas des noeuds facettes en contact
            // on parcours la liste des éléments de contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              // on attribue une grandeur arbitraire de 100 aux noeuds pour un  contact actif
              // pour les éléments de contact inactif -> -0.1
              // un noeud peut être plusieurs fois en contact -> les nombres sont cumulées
              // on fait l'hypothèse qu'un ne sera jamais inactif plus de 99 fois, du coup
              // en regardant le nombre modulo 100 on peut en déduire le nombre d'inactivité
              // et en regardant le nombre de centaine on en déduit le nombre de contact
              Tableau <Noeud*>& tablN = elcont.TabNoeud();
              int NBNF=tablN.Taille();
              // on boucle sur les noeuds de la facette (le premier noeud = noeud esclave)
              if (elcont.Actif())
               {for (int i=2;i<=NBNF;i++)
                 {TypeQuelconque& tyqN = tablN(i)->ModifGrandeur_quelconque(enuTypeQuelconque);
                  Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
                  *(tyTQ.ConteneurDouble()) += 100.;
                 }
               }
              else
               {for (int i=2;i<=NBNF;i++)
                 {TypeQuelconque& tyqN = tablN(i)->ModifGrandeur_quelconque(enuTypeQuelconque);
                  Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
                  *(tyTQ.ConteneurDouble()) -= 0.1;
                 }
               };
             };
            break;
           }
          case PENETRATION_CONTACT:
           {//  ----- cas des noeuds projectiles en contact
            // on parcours la liste des élément en contact
            //  on cumule s'il y a plusieurs contacts
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_coordonnee& tyTQ= *((Grandeur_coordonnee*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {// on définit un vecteur au niveau du noeud en contact, qui part du noeud
                // projectile et va au point d'impact
                const Coordonnee& Mtdt = elcont.Point_intersection();
                *(tyTQ.ConteneurCoordonnee()) += Mtdt - elcont.Esclave()->Coord2();
               }; // sinon on ne fait rien
             };
            break;
           }
          case GLISSEMENT_CONTACT:
           {//  ----- cas des noeuds projectiles en contact
            // on parcours la liste des élément en contact
            //  on cumule s'il y a plusieurs contacts
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_coordonnee& tyTQ= *((Grandeur_coordonnee*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {// on définit un vecteur au niveau du noeud en contact, qui part du noeud
                // projectile et va au point d'impact
                *(tyTQ.ConteneurCoordonnee()) += elcont.Dep_tangentiel();
               }; // sinon on ne fait rien
             };
            break;
           }
          case NORMALE_CONTACT:
           {//  ----- cas des noeuds projectiles en contact
            // on parcours la liste des élément en contact
            //  on cumule s'il y a plusieurs contacts
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_coordonnee& tyTQ= *((Grandeur_coordonnee*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {// on définit un vecteur au niveau du noeud en contact, qui part du noeud
                // projectile et va au point d'impact
                *(tyTQ.ConteneurCoordonnee()) += elcont.Normale_actuelle();
               }; // sinon on ne fait rien
             };
            break;
           }
//          case N_FRONT:
//           {//  ----- cas des noeuds projectiles en contact
//            // on parcours la liste des élément en contact
//            //  on cumule s'il y a plusieurs contacts
//            for (ili=ili_deb;ili!=ilifin;ili++)
//             {ElContact & elcont = (*ili); // pour simplifier
//              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
//              Grandeur_coordonnee& tyTQ= *((Grandeur_coordonnee*)
//                                                     tyqN.Grandeur_pointee());
//              // on récupère la normale au noeud projectile
//              *(tyTQ.ConteneurCoordonnee()) = elcont.Normale_actuelle();
//             };
//            break;
//           }
          case FORCE_CONTACT:
           {//  ----- cas des noeuds projectiles en contact
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_coordonnee& tyTQ= *((Grandeur_coordonnee*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {// on récupère la force de contact
                *(tyTQ.ConteneurCoordonnee()) += elcont.Force_contact();
               }; // sinon on ne fait rien
             };
            break;
           }
          case CONTACT_PENALISATION_N:
           {//  ----- on attribue la pénalisation au noeud projectile
            //  on met la pénalisation totale s'il y a plusieurs contacts
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) += elcont.Penalisation(); };
             };
            break;
           }
          case CONTACT_PENALISATION_T:
           {//  ----- on attribue la pénalisation au noeud projectile
            //  on met la pénalisation totale s'il y a plusieurs contacts
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) += elcont.Penalisation_tangentielle(); };
             };
            break;
           }
          case CONTACT_ENERG_PENAL:
           {//  ----- on attribue l'énergie de pénalisation au noeud projectile
            //  on met l'énergie totale s'il y a plusieurs contacts
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) += elcont.EnergiePenalisation(); };
             };
            break;
           }
          case CONTACT_ENERG_GLISSE_ELAS:
           {//  ----- on attribue l'énergie de pénalisation au noeud projectile
            //  on met l'énergie totale s'il y a plusieurs contacts
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) += elcont.EnergieFrottement().EnergieElastique(); };
             };
            break;
           }
          case CONTACT_ENERG_GLISSE_PLAS:
           {//  ----- on attribue l'énergie de pénalisation au noeud projectile
            //  on met l'énergie totale s'il y a plusieurs contacts
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) += elcont.EnergieFrottement().DissipationPlastique(); };
             };
            break;
           }
          case CONTACT_ENERG_GLISSE_VISQ:
           {//  ----- on attribue l'énergie de pénalisation au noeud projectile
            //  on met l'énergie totale s'il y a plusieurs contacts
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) += elcont.EnergieFrottement().DissipationVisqueuse(); };
             };
            break;
           }
          case CONTACT_NB_DECOL:
           {//  ----- on attribue le nombre de décolement au noeud projectile
            //  on met le totale s'il y a plusieurs contacts
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) += elcont.Nb_decol(); };
             };
            break;
           }
          case CONTACT_COLLANT:
           {//  ----- on attribue le fait d'être collant ou pas au noeud projectile
            //  on met le totale s'il y a plusieurs contacts
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) += elcont.Collant(); };
             };
            break;
           }
          case NUM_ZONE_CONTACT:
           {//  ----- on attribue le numéro de zone de contact au noeud projectile
            //  on met le totale s'il y a plusieurs contacts
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) += elcont.Num_zone_contact(); };
             };
            break;
           }
          case CONTACT_NB_PENET:
           {//  ----- on attribue le nombre de décolement au noeud projectile
            //  on met le totale s'il y a plusieurs contacts
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) += elcont.Nb_pene(); };
             };
            break;
           }
          case CONTACT_CAS_SOLIDE:
           {//  ----- on attribue le nombre au noeud projectile
            // =0 contact bi déformable, =1 le noeud est libre et la frontière est bloqué (solide)
            // = 2 le noeud est bloqué (solide) la frontière est libre
            // = 3 tout est solide

            //  ici on ne met pas le totale s'il y a plusieurs contacts !!
            // c'est le dernier élément qui contient le noeud projectile, qui donne le résultat
            
            // on parcours la liste des élément en contact
            for (ili=ili_deb;ili!=ilifin;ili++)
             {ElContact & elcont = (*ili); // pour simplifier
              TypeQuelconque& tyqN = elcont.Esclave()->ModifGrandeur_quelconque(enuTypeQuelconque);
              Grandeur_scalaire_double& tyTQ= *((Grandeur_scalaire_double*)
                                                     tyqN.Grandeur_pointee());
              if (elcont.Actif())
               {*(tyTQ.ConteneurDouble()) = elcont.Cas_solide(); };
             };
            break;
           }

          default: ;// on ne fait rien
       };

	    };
   };
 

};
   
// récupération de la liste de tous les grandeurs particulières disponibles avec le contact
// cette liste est identique quelque soit le maillage: il n'y a donc pas de tableau indicé du num de maillage
// absolue: indique si oui ou non on sort les tenseurs dans la base absolue ou une base particulière
List_io<TypeQuelconque> LesContacts::ListeGrandeurs_particulieres(bool absolue) const
 {
  // def de la liste de retour
  List_io <TypeQuelconque> liTQ;
  
  // on ne ramène les infos que si le contact est actif
  if (ParaGlob::param->ParaAlgoControleActifs().ContactType()!= 0)
    {
      // grandeurs de travail
      int dim = ParaGlob::Dimension();
     
      // ici on est en 3D et les grandeurs sont par principe en absolue, donc la variable absolue ne sert pas
      Grandeur_scalaire_double grand_courant(0.);
      
      Coordonnee  inter(dim);
      Grandeur_coordonnee   grand_coor_courant(inter);
      
      // def d'un type quelconque représentatif à chaque grandeur
      // a priori ces grandeurs sont défini aux noeuds
      //on regarde si ce type d'info existe déjà: si oui on augmente la taille du tableau, si non on crée
      // $$$  cas de la visualisation des noeuds projectiles en contact
      {TypeQuelconque typQ1(NOEUD_PROJECTILE_EN_CONTACT,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
      // $$$  cas de la visualisation des noeuds des facettes en contact
      {TypeQuelconque typQ1(NOEUD_FACETTE_EN_CONTACT,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
      // $$$  cas de la visualisation du glissement des noeuds projectiles actifs
      {TypeQuelconque typQ1(GLISSEMENT_CONTACT,X1,grand_coor_courant);
       liTQ.push_back(typQ1);
      };
      // $$$  cas de la visualisation de la normale de contact (moyenne)
      {TypeQuelconque typQ1(NORMALE_CONTACT,X1,grand_coor_courant);
       liTQ.push_back(typQ1);
      };
//      // $$$  cas de la visualisation de la normale aux noeuds frontières
//      {TypeQuelconque typQ1(N_FRONT,X1,grand_coor_courant);
//       liTQ.push_back(typQ1);
//      };
      // $$$  cas de la visualisation de la pénétration en contact
      {TypeQuelconque typQ1(PENETRATION_CONTACT,X1,grand_coor_courant);
       liTQ.push_back(typQ1);
      };
      // $$$  cas de la visualisation des forces de contact
      {TypeQuelconque typQ1(FORCE_CONTACT,X1,grand_coor_courant);
       liTQ.push_back(typQ1);
      };
      //  $$$ nombre de pénétration
      {TypeQuelconque typQ1(CONTACT_NB_PENET,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
      //  $$$ nombre de décolement
      {TypeQuelconque typQ1(CONTACT_NB_DECOL,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
      //  $$$ cas_solide
      {TypeQuelconque typQ1(CONTACT_CAS_SOLIDE,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
      {TypeQuelconque typQ1(CONTACT_ENERG_PENAL,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
      {TypeQuelconque typQ1(CONTACT_ENERG_GLISSE_ELAS,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
      {TypeQuelconque typQ1(CONTACT_ENERG_GLISSE_PLAS,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
      {TypeQuelconque typQ1(CONTACT_ENERG_GLISSE_VISQ,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
      {TypeQuelconque typQ1(CONTACT_PENALISATION_N,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
      {TypeQuelconque typQ1(CONTACT_PENALISATION_T,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
      {TypeQuelconque typQ1(CONTACT_COLLANT,X1,grand_courant);
       liTQ.push_back(typQ1);
      };
     {TypeQuelconque typQ1(NUM_ZONE_CONTACT,X1,grand_courant);
      liTQ.push_back(typQ1);
     };
    };
  
  // retour
  return liTQ;

 };


  // concernant les grandeurs gérées par le contact:
  // ramène une liste d'enuméré correspondant aux List_io<TypeQuelconque> passé en paramètre
  // idem pour une List_io < Ddl _enum_etendu >
void LesContacts::List_reduite_aux_contact(const List_io<TypeQuelconque>& liTQ
                                            ,List_io < TypeQuelconque >& li_restreinte_TQ
                                           )
  {
   // initialisation des listes de retour
   li_restreinte_TQ.clear();
   // on passe en revue la liste
   List_io<TypeQuelconque>::const_iterator itq,itqfin=liTQ.end();
   for (itq=liTQ.begin();itq!=itqfin;itq++)
     {const TypeQuelconque& tipParticu = (*itq); // pour simplifier
      const TypeQuelconque_enum_etendu& enuTypeQuelconque = tipParticu.EnuTypeQuelconque();
      if (enuTypeQuelconque.Nom_vide()) // veut dire que c'est un enum pur
       switch (enuTypeQuelconque.EnumTQ())
       {  case NOEUD_PROJECTILE_EN_CONTACT:  case NOEUD_FACETTE_EN_CONTACT:
          case PENETRATION_CONTACT:          case FORCE_CONTACT:
          case CONTACT_PENALISATION_N:       case CONTACT_PENALISATION_T:
          case CONTACT_ENERG_PENAL:          case NORMALE_CONTACT:
          case GLISSEMENT_CONTACT:           case CONTACT_ENERG_GLISSE_ELAS:
          case CONTACT_ENERG_GLISSE_VISQ:    case CONTACT_ENERG_GLISSE_PLAS:
          case CONTACT_NB_DECOL:             case CONTACT_NB_PENET:
          case CONTACT_CAS_SOLIDE:           case CONTACT_COLLANT:
          case NUM_ZONE_CONTACT:             //case N_FRONT :
           {//  ----- cas des noeuds projectiles en contact
            li_restreinte_TQ.push_front(TypeQuelconque(tipParticu));
            break;
           }
          default: ;// on ne fait rien
       };
     };
  };

// initialisation des listes de grandeurs qu'ils faudra transférérer aux niveaux des noeuds des élements
// de contact, on définit si besoin, les conteneurs ad hoc au niveau des noeuds
void LesContacts::Init_Grandeur_particuliere (bool absolue,List_io<TypeQuelconque>& liTQ)
  {liQ_en_sortie = liTQ; // on récupère la liste --> sans doute que cela ne sert à rien

   LaLIST<ElContact>::iterator ili_deb= listContact.begin();
   LaLIST<ElContact>::iterator ilifin= listContact.end();
   LaLIST<ElContact>::iterator ili;

   // on passe en revue la liste
   List_io<TypeQuelconque>::iterator itq,itqfin=liTQ.end();
   //    on part du principe qu'il y a plus de noeuds en contact que de grandeur donc on fait une
   //    boucle externe sur les noeuds en contact:  on parcours la liste des noeuds en contact
   for (ili=ili_deb;ili!=ilifin;ili++)
    {ElContact& elcont = (*ili); // pour simplifier
     for (itq=liTQ.begin();itq!=itqfin;itq++)
      {TypeQuelconque& tipParticu = (*itq); // pour simplifier
       EnumTypeQuelconque enuTQ = tipParticu.EnuTypeQuelconque().EnumTQ();

       switch (enuTQ)
          { case NOEUD_PROJECTILE_EN_CONTACT:  case NOEUD_FACETTE_EN_CONTACT:
            case GLISSEMENT_CONTACT:           case PENETRATION_CONTACT:
            case FORCE_CONTACT:                case CONTACT_NB_PENET:
            case CONTACT_PENALISATION_N:       case CONTACT_PENALISATION_T:
            case CONTACT_NB_DECOL:             case CONTACT_CAS_SOLIDE:
            case CONTACT_ENERG_PENAL:          case CONTACT_COLLANT:
            case NUM_ZONE_CONTACT:             //case N_FRONT :
            case NORMALE_CONTACT:              case CONTACT_ENERG_GLISSE_ELAS:
            case CONTACT_ENERG_GLISSE_VISQ:    case CONTACT_ENERG_GLISSE_PLAS:
            
             {//  ----- cas des noeuds projectiles en contact
              elcont.Esclave()->AjoutUnTypeQuelconque(*itq); // ajout du conteneur
              break;
             }
            default: ; // sinon rien
          };
       };
    };

  } ;

//==========================  fonction protegee =============================

// mise à jour des boites d'encombrement pour les éléments qui contiennent une frontière
void LesContacts::Mise_a_jour_boite_encombrement_element_contenant_front()
 {
   list  <Element *>::iterator il,ilfin = liste_elemens_front.end();
   for (il = liste_elemens_front.begin();il!=ilfin;il++)
      (*il)->Boite_encombre_element(TEMPS_tdt);

 };

// suppression du gap de contact pour les noeuds "collant avec suppression de gap"
void LesContacts::Suppression_gap_pour_noeud_collant()
  {
    int niveau_commentaire_lescontacts = Permet_affichage();
  #ifdef UTILISATION_MPI
    int proc_en_cours = ParaGlob::Monde()->rank();
  #endif
    // on passe en revue les zones de contact et si nécessaire on supprime les gaps
    // sinon retour
    bool continuer = false; // init par défaut
    //     list <Quatre_string_un_entier> nom_ref_zone_contact; // liste des noms de références des zones de contact
    list <Quatre_string_un_entier>::iterator iu,iufin = nom_ref_zone_contact.end();
    for (iu = nom_ref_zone_contact.begin();iu != iufin; iu++)
     if ((*iu).n == 2)
       {continuer = true; break;};
    if (!continuer)
      return;
   
    // sinon il faut effectivement faire une suppression de gap
  #ifdef UTILISATION_MPI
    if (proc_en_cours == 0)
  #endif
    if (niveau_commentaire_lescontacts > 2)
     cout << "\n >>>> Suppression_gap_pour_noeud_collant : ";
    // on va iterer sur les noeuds esclaves collants dont on veut supprimer le gap
    LaLIST_io <Front>::iterator iM,iMfin; // des itérator de travail
    int nb_zone = MaX(1,nom_ref_zone_contact.size());
    double dist_max =  Dabs(ParaGlob::param->ParaAlgoControleActifs().DistanceMaxiAuPtProjete());
   
    int tout_projeter = true; // init
    int boucle_maxi = 10; // 10 boucle maxi
    int boucle = 1;
    do
     { tout_projeter = true; // init
       // si tout_projeter ne pas pas à false, alors on sort de la boucle
       // pour mémoire il passe à false s'il y a une nouvelle projection faite
       // on boucle, car on peut avoir alternativement des noeuds qui sont esclave puis appartenir à des maîtres
       // dans ce cas les projections se font petit à petit par ajustement successif
     #ifdef UTILISATION_MPI
       if (proc_en_cours == 0)
     #endif
       if (niveau_commentaire_lescontacts >2 )
           cout << "\n boucle : "<<boucle << flush;
       for (int intot = 1;intot<= nb_mail_Esclave;intot++) // boucle sur les maillages esclaves
        for (int j=1;j<=nb_zone;j++) // boucle sur les zones de contact
         {const Tableau <Noeud*>& tesc= tesctotal(intot)(j); // pour simplifier la notation:
          const Tableau <int> tesN_col = tesN_collant(intot)(j); // pour simplifier
          int tesc_taille=tesc.Taille();                   //  tab des noeuds esclaves à considérer
          int compteur_noeuds_projecte=0; // le nombre de noeuds qui sont projetés
          double le_maxi_des_distances_trouve = 0.;
          for (int inesc = 1;inesc<= tesc_taille;inesc++) // boucle sur les noeuds esclaves
           if (tesN_col(inesc) == 2 ) // cas noeud collant avec volonté de supprimer le gap
            { Noeud* noee = tesc(inesc); // pour simplifier
              int n_noee = noee->Num_noeud();
              *(gr_pour_noeud->ConteneurEntier()) = n_noee; // pour l'affichage
              *(gr_pour_elem->ConteneurEntier()) = 0; //pas d'élément ici
    //--- debug
   //if (noee->Num_noeud() == 5)
   // {cout << "\n arrivée au bon element ";
   // }
              const Coordonnee pt_esc = noee->Coord0(); // position du noeud esclave initial
              list <Coordonnee > list_P; // la liste des projetés
              list <LaLIST_io <Front>::iterator> li_pt_front_P; // liste sur les faces pour le debug
              for (int jlf=1;jlf<=nbmailMaitre;jlf++) // boucle sur les maillages maitres
               {//Tableau < Tableau < LaLIST_io <Front> > >  t_listFront;
                LaLIST_io <Front>& t_l_front = t_listFront(jlf)(j);
                iMfin=t_l_front.end();
              #ifdef MISE_AU_POINT
                list <LaLIST_io <Front>::iterator> liste_boite_contenant;
              #endif

                for (iM = t_l_front.begin() ; iM != iMfin; iM++) // boucle sur les front maitres
                 {// si c'est dans le volume de la frontière augmenté de la distance ad hoc on test
                  // plus précisément
               #ifdef MISE_AU_POINT
                  {if (Permet_affichage(&li_pour_noeuds_element) > 6)
                    {cout << "\n test boite d'encombrement: noeud "<<pt_esc
                          << "\n min: "<< (*iM).Encom_mini_FR()
                          << "\n max: "<< (*iM).Encom_maxi_FR();
                    }
                  }
               #endif
                  if ((*iM).In_boite_emcombrement_front(pt_esc,dist_max))
                   {
                 #ifdef MISE_AU_POINT
                    liste_boite_contenant.push_back(iM);
                    if (Permet_affichage(&li_pour_noeuds_element) > 6)
                      cout << "\n test positif boite d'encombrement: ";
                 #endif

                    // on projete le noeud sur la frontière
                    Coordonnee P;
   ////--- debug
   //if ((noee->Num_noeud() == 5) && ((*iM).PtEI()->Num_elt()==367))
   // {cout << "\n arrivée au bon element ";
   //
   // }
   //{double Ndis = (P-pt_esc).Norme();
   // if (Ndis > 20)
   //   (*iM).Eleme()->Projection_normale(pt_esc,P);
   //}
   //-- fin debug

   // //----debug
   //{if ((noee->Num_noeud() == 12)
   //     && ((*iM).Num_frontiere() == 1) && ((*iM).PtEI()->Num_elt() == 3130))
   //    { cout << "\n debug : frontiere 1 de l'element 3130";}
   //}
   ////----fin debug

                    // si la projection est valide on enregistre
                    if ((*iM).Eleme()->Projection_normale(pt_esc,P))
                      { list_P.push_back(P);
                        li_pt_front_P.push_back(iM);
//  #ifdef MISE_AU_POINT
//    // le point projeté doit donc être hors de l'élément
//     { if ((Permet_affichage(&li_pour_noeuds_element) > 6) && ((*iM).PtEI()->Interne_t(pt_esc)==1))
//         {cout << "\n pb *** le noeud projete " << noee->Num_noeud() << " est a l'interieur de l'element "
//            << (*iM).PtEI()->Num_elt();
//          (*iM).PtEI()->Interne_t(pt_esc); // pour le debug
//         }
//     }
//  #endif

  
  
  
                      };
   //--- debug
   //{double Ndis = (P-pt_esc).Norme();
   // if (Ndis > 20)
   //   (*iM).Eleme()->Projection_normale(pt_esc,P);
   //}
   //-- fin debug
                  
                   }
                #ifdef MISE_AU_POINT
                  else
                   {if (Permet_affichage(&li_pour_noeuds_element) > 6)
                     cout << "\n test negatif boite d'encombrement: ";
                   }
                #endif
                 };
                #ifdef MISE_AU_POINT
                if (liste_boite_contenant.size() == 0)
                  if (Permet_affichage(&li_pour_noeuds_element) > 3)
                   {cout << "\n aucune boite d'encombrement ne contient le noeud "
                         << " Noe: "<<noee->Num_noeud()
                         << " mail: " <<  noee->Num_Mail() << " (zone"<<j<<")"
                         << " coordonnees: " << pt_esc ;
                   };
                #endif
               };
              // maintenant on va garder le plus proche
              bool projection_ok = false; // init
              if (list_P.size() != 0)
               {list <Coordonnee >::iterator il,ilfin=list_P.end();
              #ifdef UTILISATION_MPI
                if (proc_en_cours == 0)
              #endif
                if (Permet_affichage(&li_pour_noeuds_element) > 5)
                  {cout << "\n " << list_P.size() << " proj trouvees   "
                        << " Noe: "<<noee->Num_noeud()
                        << " mail: " <<  noee->Num_Mail() << " (zone"<<j<<")";
                  };
                Coordonnee M = pt_esc;  // init
                Coordonnee P(M); // la projection
                double distance =  dist_max;
                LaLIST_io <LaLIST_io <Front>::iterator>::iterator iface = li_pt_front_P.begin();
                LaLIST_io <LaLIST_io <Front>::iterator>::iterator iface_maxi ;
                for (il=list_P.begin(); il!= ilfin;il++,iface++)
                 { double Ndis = (M-(*il)).Norme();
                 #ifdef UTILISATION_MPI
                   if (proc_en_cours == 0)
                 #endif
                   if (niveau_commentaire_lescontacts >5 )
                     cout << " dist: "  << Ndis ;
                   if (Ndis < distance)
                    {P = (*il); distance = Ndis;
                     projection_ok = true; // au moins un nouveau point
                     iface_maxi = iface;
                    };
                 };
                // si la distance == 0., il n'y a pas à changer les coordonnées
                // car le point projeté == le point initial
                
                // pour le test on utilise la précision qui a été utilisée pour la projection * 10
                // car d'une boucle à l'autre on ne peut pas être mieux que cette précision
                
                // distance maxi entre le point d'inter du plan et le point correspondant de la surface
                double dismini = ParaGlob::param->ParaAlgoControleActifs().Precision_pt_sur_front();

                if (distance > dismini * 10.) //ConstMath::petit)
                 {// on change les coordonnées du noeud
                  noee->Change_coord0(P);
                  noee->Change_coord1(P);
                  noee->Change_coord2(P);
                  compteur_noeuds_projecte++;
                  tout_projeter=false;
                  if (distance > le_maxi_des_distances_trouve)
                    le_maxi_des_distances_trouve = distance;
                #ifdef UTILISATION_MPI
                  if (proc_en_cours == 0)
                #endif
                  if (Permet_affichage(&li_pour_noeuds_element) >3 )
                     cout << "\n suppression gap=("<<distance<<") du noeud "<<noee->Num_noeud()
                          << " du maillage " <<  noee->Num_Mail() << " (zone "<<j<<")";
                #ifdef UTILISATION_MPI
                  if (proc_en_cours == 0)
                #endif
                  if (Permet_affichage(&li_pour_noeuds_element) >4 )
                    {cout << "\n ancienne coordonnee "<< pt_esc
                          << " nouvelle " << P;
                     // dans le cas où iface_maxi a été attribué
                     if (projection_ok)
                       (*(*iface_maxi)).Affiche();
               
//   //--- debug
//   {// on va vérifier que la nouvelle position n'est pas à l'intérieur d'un élément fini associé aux front maître
//    if (Permet_affichage(&li_pour_noeuds_element) > 5)
//     {cout << "\n debug LesContacts::Suppression_gap_pour_noeud_collant() ";
//      for (int jlf=1;jlf<=nbmailMaitre;jlf++) // boucle sur les maillages maitres
//       {//Tableau < Tableau < LaLIST_io <Front> > >  t_listFront;
//        LaLIST_io <Front>& t_l_front = t_listFront(jlf)(j);
//        iMfin=t_l_front.end();
//        for (iM = t_l_front.begin() ; iM != iMfin; iM++) // boucle sur les front maitres
//         {if ((*iM).PtEI()->Interne_t(pt_esc)==1)
//            {cout << "\n pb *** le noeud projete " << noee->Num_noeud() << " est a l'interieur de l'element "
//                   << (*iM).PtEI()->Num_elt();
//             (*iM).PtEI()->Interne_t(pt_esc); // pour le debug
//            };
//         };
//       };
//     };
////
////   const Coordonnee pt_esc  = noee->Coord0();
////   Coordonnee PP;
////   (*(*iface_maxi)).Eleme()->Projection_normale(pt_esc,PP);
////   cout << "\n $$$ nouvelle coordonnees projetee "<< PP;
//   }
//   //-- fin debug

                    };
                 }
                else if (projection_ok)
                  // si on n'a pas de projection avec changement de position
                  // mais par contre on a quand même trouvé un projeté on enregistre
                 {compteur_noeuds_projecte++;};
               };
              if ((!projection_ok) && (boucle==1)) // au premier passage
               {
               #ifdef UTILISATION_MPI
                 if (proc_en_cours == 0)
               #endif
                 if (Permet_affichage(&li_pour_noeuds_element) >3 )
                   cout << "\n pas de projection trouvee donc de suppression gap du noeud "<<noee->Num_noeud()
                        << " du maillage" <<  noee->Num_Mail() << " (zone "<<j<<")";
               } ;
            }; //-- fin de la boucle sur inesc
        #ifdef UTILISATION_MPI
          if (proc_en_cours == 0)
        #endif
          if (niveau_commentaire_lescontacts >2 )
           {if (compteur_noeuds_projecte)
                 cout << "\n zone: " << j <<", " << compteur_noeuds_projecte << " suppression(s) de gap, maxi_distance= "
                      << le_maxi_des_distances_trouve << "( passage"<<boucle<<")" ;
            int non_gap = tesc_taille-compteur_noeuds_projecte;
            if ((non_gap)&& (boucle==1)) // au premier passage
              cout << "\n  zone: " << j <<", *** attention pour "<<non_gap
                   << " noeud(s) pas de projection trouvee !! ";
           };
        }; //-- fin de la boucle sur intot
       boucle++;
     }
    while ((!tout_projeter) && (boucle < boucle_maxi));
//     Sortie(1);
  };

// récupération de la zone de contact d'un élément de contact existant
// c'est un peu lourdinge, mais l'avantage c'est que cela s'adapte à une situation qui
// a par exemple changé
// si en retour le numéro de zone = 0, cela signifie que le contact ne peut plus exister
int LesContacts::Recup_ref( ElContact& al)
 { int nb_zone = MaX(1,nom_ref_zone_contact.size());
   const Tableau <Noeud*>& tn = al.TabNoeud();
  
   // on parcours la liste des frontières succeptibles d'entrer en contact
   // pour repérer la zone
   int num_zone = 0; // init
   int nb_maitre = t_listFront.Taille();
   int nb_esclave = tesctotal.Taille();
   for (int i=1;i<=nb_maitre;i++)
    {for (int j=1;j<=nb_zone;j++)
      {LaLIST_io <Front> & list_fronta = t_listFront(i)(j); // pour simplifier
       LaLIST_io <Front>::iterator il,ilfin = list_fronta.end();
       for (il = list_fronta.begin();il != ilfin; il++)
         {if ((*il)==(*al.Elfront()))
           // les deux pointeurs pointent sur la même frontière, donc
           // maintenant on regarde du coté des noeuds esclaves
           { for (int ie = 1; ie<=nb_esclave;ie++)
              {const Tableau <Noeud*>& tesc= tesctotal(i)(j); // pour simplifier la notation:
               int tesc_taille=tesc.Taille();                   //  tab des noeuds esclaves à considérer
               for (int inesc = 1;inesc<= tesc_taille;inesc++) // boucle sur les noeuds esclaves
                {if (tesc(inesc) == al.Esclave())
                  // les deux pointeurs pointent sur le même noeud, c'est ok
                  {num_zone = j; break;};
                };
               if (num_zone)
                 break;
              };
           };
          if (num_zone)
             break;
         };
       if (num_zone)
          break;
      };
     if (num_zone)
        break;
    };
   // retour des infos
   return num_zone;
 };


  // création d'un tableau de condition linéaire, correspondant à tous les éléments de contact en cours
  // qu'ils soient actifs ou pas (a prior cette méthode est conçu pour donner des infos relativement à la largeur
  // de bandes en noeuds due aux CLL)
  // chacune des condition ne contient "d'exploitable" que le tableau de noeuds associés à la CLL,
list <Condilineaire>&   LesContacts::ConnectionCLL()
   {
       tempsContact.Mise_en_route_du_comptage(); // def deb compt
     #ifdef UTILISATION_MPI
       int proc_en_cours = ParaGlob::Monde()->rank();
       if (proc_en_cours != 0)
        {
     #endif
         {
      //     // on boucle sur le tableau et pour chaque element on crée une condition lineaire
      //     int tabTaille = listContact.size();
      //     t_connectionCLL.Change_taille(tabTaille);
           t_connectionCLL.clear();
           // un tableau servant à la def de la CLL
           Tableau <Enum_ddl > t_enu(1); t_enu(1)=X1 ;
           LaLIST <ElContact>::const_iterator il,ilfin=listContact.end();
           int posi=1; // init pour le tableau
           for (il=listContact.begin();il != ilfin;il++,posi++)
            { const Tableau < Noeud *>& t_n = (*il).Const_TabNoeud();
              // on est dans le cas ou l'on connait les infos relatives uniquements aux noeuds, aux enum ddl
              t_connectionCLL.push_back(Condilineaire(t_enu, t_n));
            };
           tempsContact.Arret_du_comptage(); // fin cpu
         }
    #ifdef UTILISATION_MPI
        temps_transfert_long.Mise_en_route_du_comptage(); // comptage cpu
        // maintenant on va  les transmettres au cpu 0
        // pour cela on va utiliser un conteneur intermédiaire
        // on calcul la taille nécessaire pour le conteneur  (a découper éventuellement ??)
        int taille_conteneur=0;
        list <Condilineaire>::iterator il,ilfin  = t_connectionCLL.end();
        for (il = t_connectionCLL.begin();il != ilfin;il++)
           taille_conteneur += (*il).Taille_Pack();
        inter_transfer2.Change_taille(taille_conteneur); //  le conteneur
        //   on rempli le conteneur
        int rang  = 1; // init
        for (il = t_connectionCLL.begin();il != ilfin;il++)
          rang  = (*il).Pack_vecteur(inter_transfer2,rang);
        // on transfert
        mpi::request reqs1 = ParaGlob::Monde()->isend(0, 42, taille_conteneur);
        // si la taille du conteneur est nulle, on ne le transfert pas
        if (taille_conteneur > 0) 
           mpi::request reqs2 = inter_transfer2.Ienvoi_MPI(0,43);
        // on attend pas
        temps_transfert_long.Arret_du_comptage(); // fin comptage cpu
       }
      else // cas du cpu 0
       {// l'objectif ici est de récupérer  les conditions linéaires
        tempsContact.Arret_du_comptage(); // fin cpu
        temps_transfert_long.Mise_en_route_du_comptage(); // comptage cpu
        int nb_proc_terminer = 0; // permettra de terminer
        t_connectionCLL.clear();// Change_taille(nb_contact_actif);
        while (nb_proc_terminer < (ParaGlob::Monde()->size()-1)) // gérer par les valeurs de tyfront
           { // on récupère un résultat de cpu i

             int  taille_transfert;
             mpi::request reqs1 = ParaGlob::Monde()->irecv(mpi::any_source, 42, taille_transfert);
             mpi::status stat = reqs1.wait(); // on attend que le conteneur soit rempli
             nb_proc_terminer++; //  on prend en compte que l'on a récupéré un conteneur
             // si la taille == 0, il n'y a rien à faire
             if (taille_transfert > 0)
               {inter_transfer2.Change_taille(taille_transfert); //  le conteneur
                int source = stat.source(); // récupération du numéro de la source
                //   on récupère
                mpi::request reqs2 = inter_transfer2.Irecup_MPI(source, 43);
                reqs2.wait(); // on attend que le conteneur soit rempli
                // on va  remplir la liste des  conditions  limites
                int rang  = 1; // init
                while (rang !=  0)
                  { Condilineaire  condi; // une condition  intermédiaire
                    rang  = condi.UnPack_vecteur(*lesMaille,inter_transfer2,rang,
                                      &LesMaillages::Noeud_LesMaille);
                    t_connectionCLL.push_back(condi);
                  };
               };
           };
        temps_transfert_long.Arret_du_comptage();
       };
    #endif

     // retour
     return t_connectionCLL;
   };


// récupère le nombre de contact actif et met à jour ce nombre
// normalement devrait toujours être correct mais ?? il y a quelque chose d'incorrecte quelque part
int LesContacts::Calcul_Nb_contact_actif()
  {  LaLIST <ElContact>::iterator il,ilfin = listContact.end();
     nb_contact_actif = 0; // init
     for (il = listContact.begin();il != ilfin; il++)
       if ((*il).Actif())
          nb_contact_actif++;
     return nb_contact_actif;
  };


// création du conteneur Fct_nD_contact
void LesContacts::Creation_Fct_nD_contact()
 {
#ifdef UTILISATION_MPI
  int proc_en_cours = ParaGlob::Monde()->rank();
#endif

  {// cas de la penalisation normale
   string nom_fct = ParaGlob::param->ParaAlgoControleActifs().Fct_nD_penalisationPenetration();
   if (nom_fct != "_")
     {// on va récupérer la fonction
      if (sauve_lesFonctionsnD->Existe(nom_fct))
       { Fonction_nD * pt_fonct = sauve_lesFonctionsnD->Trouve(nom_fct);
         // sauvegarde
         fct_nD_contact.fct_nD_penalisationPenetration = pt_fonct;
       }
      else
       {
       #ifdef UTILISATION_MPI
         cout << "\n proc " << proc_en_cours
       #else
         cout << "\n"
       #endif
              << " *** erreur dans la definition de la fonction nD de pilotage de la penalisation normale "
              << " le nom : " << nom_fct
              << " ne correspond pas a une fonction nD existante !! ";
         Sortie(1);
       };
     }
   else
     {fct_nD_contact.fct_nD_penalisationPenetration=NULL;};
  };

  {// cas fct_nD_penetration_contact_maxi
   string nom_fct = ParaGlob::param->ParaAlgoControleActifs().Fct_nD_penetration_contact_maxi();
   if (nom_fct != "_")
     {// on va récupérer la fonction
      if (sauve_lesFonctionsnD->Existe(nom_fct))
       { Fonction_nD * pt_fonct = sauve_lesFonctionsnD->Trouve(nom_fct);
         // sauvegarde
         fct_nD_contact.fct_nD_penetration_contact_maxi = pt_fonct;
       }
      else
       {
        #ifdef UTILISATION_MPI
          cout << "\n proc " << proc_en_cours
        #else
          cout << "\n"
        #endif
               << " *** erreur dans la definition de la fonction nD de pilotage de la  "
               << " penetration maxi, le nom : " << nom_fct
               << " ne correspond pas a une fonction nD existante !! ";
          Sortie(1);
       };
     }
   else
     {fct_nD_contact.fct_nD_penetration_contact_maxi=NULL;};
  };
  
  {// cas Fct_nD_penetration_borne_regularisation
   string nom_fct = ParaGlob::param->ParaAlgoControleActifs().Fct_nD_penetration_borne_regularisation();
   if (nom_fct != "_")
     {// on va récupérer la fonction
      if (sauve_lesFonctionsnD->Existe(nom_fct))
       { Fonction_nD * pt_fonct = sauve_lesFonctionsnD->Trouve(nom_fct);
         // sauvegarde
         fct_nD_contact.fct_nD_penetration_borne_regularisation = pt_fonct;
       }
      else
       {
        #ifdef UTILISATION_MPI
          cout << "\n proc " << proc_en_cours
        #else
          cout << "\n"
        #endif
               << " *** erreur dans la definition de la fonction nD de pilotage de la "
               << " borne de regularisation, le nom : " << nom_fct
               << " ne correspond pas a une fonction nD existante !! ";
          Sortie(1);
       };
     }
   else
     {fct_nD_contact.fct_nD_penetration_borne_regularisation=NULL;};
  };
  
  {// cas fct_nD_force_contact_noeud_maxi
   string nom_fct = ParaGlob::param->ParaAlgoControleActifs().Fct_nD_force_contact_noeud_maxi();
   if (nom_fct != "_")
     {// on va récupérer la fonction
      if (sauve_lesFonctionsnD->Existe(nom_fct))
       { Fonction_nD * pt_fonct = sauve_lesFonctionsnD->Trouve(nom_fct);
         // sauvegarde
         fct_nD_contact.fct_nD_force_contact_noeud_maxi = pt_fonct;
       }
      else
       {
        #ifdef UTILISATION_MPI
          cout << "\n proc " << proc_en_cours
        #else
          cout << "\n"
        #endif
               << " *** erreur dans la definition de la fonction nD de pilotage de la "
               << " force de contact normale, le nom : " << nom_fct
               << " ne correspond pas a une fonction nD existante !! ";
          Sortie(1);
       };
     }
   else
     {fct_nD_contact.fct_nD_force_contact_noeud_maxi=NULL;};
  };
  
  {// cas fct_nD_penalisationTangentielle
   string nom_fct = ParaGlob::param->ParaAlgoControleActifs().Fct_nD_penalisationTangentielle();
   if (nom_fct != "_")
     {// on va récupérer la fonction
      if (sauve_lesFonctionsnD->Existe(nom_fct))
       { Fonction_nD * pt_fonct = sauve_lesFonctionsnD->Trouve(nom_fct);
         // sauvegarde
         fct_nD_contact.fct_nD_penalisationTangentielle = pt_fonct;
       }
      else
       {
       #ifdef UTILISATION_MPI
         cout << "\n proc " << proc_en_cours
       #else
         cout << "\n"
       #endif
              << " *** erreur dans la definition de la fonction nD de pilotage de la penalisation tangentielle "
              << " le nom : " << nom_fct
              << " ne correspond pas a une fonction nD existante !! ";
         Sortie(1);
       };
     }
   else
     {fct_nD_contact.fct_nD_penalisationTangentielle=NULL;};
  };
  
  {// cas fct_nD_tangentielle_contact_maxi
   string nom_fct = ParaGlob::param->ParaAlgoControleActifs().Fct_nD_tangentielle_contact_maxi();
   if (nom_fct != "_")
     {// on va récupérer la fonction
      if (sauve_lesFonctionsnD->Existe(nom_fct))
       { Fonction_nD * pt_fonct = sauve_lesFonctionsnD->Trouve(nom_fct);
         // sauvegarde
         fct_nD_contact.fct_nD_tangentielle_contact_maxi = pt_fonct;
       }
      else
       {
        #ifdef UTILISATION_MPI
          cout << "\n proc " << proc_en_cours
        #else
          cout << "\n"
        #endif
               << " *** erreur dans la definition de la fonction nD de pilotage du "
               << " deplacement tangentiel maxi, le nom : " << nom_fct
               << " ne correspond pas a une fonction nD existante !! ";
          Sortie(1);
       };
     }
   else
     {fct_nD_contact.fct_nD_tangentielle_contact_maxi=NULL;};
  };
  
  {// cas fct_nD_tangentielle_borne_regularisation
   string nom_fct = ParaGlob::param->ParaAlgoControleActifs().Fct_nD_tangentielle_borne_regularisation();
   if (nom_fct != "_")
     {// on va récupérer la fonction
      if (sauve_lesFonctionsnD->Existe(nom_fct))
       { Fonction_nD * pt_fonct = sauve_lesFonctionsnD->Trouve(nom_fct);
         // sauvegarde
         fct_nD_contact.fct_nD_tangentielle_borne_regularisation = pt_fonct;
       }
      else
       {
        #ifdef UTILISATION_MPI
          cout << "\n proc " << proc_en_cours
        #else
          cout << "\n"
        #endif
               << " *** erreur dans la definition de la fonction nD de pilotage de la  "
               << " borne de regularisation tangentielle, le nom : " << nom_fct
               << " ne correspond pas a une fonction nD existante !! ";
          Sortie(1);
       };
     }
   else
     {fct_nD_contact.fct_nD_tangentielle_borne_regularisation=NULL;};
  };
  
  {// cas fct_nD_force_tangentielle_noeud_maxi
   string nom_fct = ParaGlob::param->ParaAlgoControleActifs().Fct_nD_force_tangentielle_noeud_maxi();
   if (nom_fct != "_")
     {// on va récupérer la fonction
      if (sauve_lesFonctionsnD->Existe(nom_fct))
       { Fonction_nD * pt_fonct = sauve_lesFonctionsnD->Trouve(nom_fct);
         // sauvegarde
         fct_nD_contact.fct_nD_force_tangentielle_noeud_maxi = pt_fonct;
       }
      else
       {
        #ifdef UTILISATION_MPI
          cout << "\n proc " << proc_en_cours
        #else
          cout << "\n"
        #endif
               << " *** erreur dans la definition de la fonction nD de pilotage de la  "
               << " force tangentielle maxi, le nom : " << nom_fct
               << " ne correspond pas a une fonction nD existante !! ";
          Sortie(1);
       };
     }
   else
     {fct_nD_contact.fct_nD_force_tangentielle_noeud_maxi=NULL;};
  };
 
 {// cas fct_niveau_commentaire
  string nom_fct = ParaGlob::param->ParaAlgoControleActifs().Fct_niveau_commentaire_contact();
  if (nom_fct != "_")
    {// on va récupérer la fonction
     if (sauve_lesFonctionsnD->Existe(nom_fct))
      { Fonction_nD * pt_fonct = sauve_lesFonctionsnD->Trouve(nom_fct);
        // sauvegarde
        fct_nD_contact.fct_niveau_commentaire = pt_fonct;
      }
     else
      {
       #ifdef UTILISATION_MPI
         cout << "\n proc " << proc_en_cours
       #else
         cout << "\n"
       #endif
              << " *** erreur dans la definition de la fonction nD de pilotage du  "
              << " niveau de commentaire en contact, le nom : " << nom_fct
              << " ne correspond pas a une fonction nD existante !! ";
         Sortie(1);
      };
    }
  else
    {fct_nD_contact.fct_niveau_commentaire=NULL;};
 };
 
 // initialisation des conteneurs statique des fonction nD
 fct_nD_contact.Init_conteneur_statique();


 };

/*// mise à jour du stockage inter, pour prendre en
// compte une nouvelle numérotation des noeuds
void LesContacts::Prise_en_compte_nouvelle_numerotation_noeud()
 { //on va reconstruire ta_inverse
   int nb_zone = MaX(1,nom_ref_zone_contact.size());
   // on garde en mémoire les anciens numéros pour faire le pontage
   Tableau < Tableau < Tableau <int> > >  ta_inverse_old(ta_inverse);
  
   // on recherche les maxi des numéros de noeuds esclave, pour dimensionner ta_inverse !!
   Tableau < Tableau <int> > maxi_num_noe_esclave(nb_mail_Esclave); // init à 0
   for (int i=1;i<=nb_mail_Esclave;i++)
    {maxi_num_noe_esclave(i).Change_taille(nb_zone);
     for (int j=1;j<=nb_zone;j++)
      {const Tableau <Noeud*>& tesc= tesctotal(i)(j); // pour simplifier la notation:
       int tesc_taille=tesc.Taille();                   //  tab des noeuds esclaves à considérer
       int& max_num_esclave = maxi_num_noe_esclave(i)(j); // pour simplifier
       for (int inesc = 1;inesc<= tesc_taille;inesc++) // boucle sur les noeuds esclaves
           { Noeud* noee = tesc(inesc); // pour simplifier
             int num_noeud = noee->Num_noeud();
             if (num_noeud > max_num_esclave)
                 max_num_esclave = num_noeud;
           };
      };
    };
   // Maintenant on peut dimensionner ta_inverse
   for (int i=1;i<=nb_mail_Esclave;i++)
    {ta_inverse(i).Change_taille(nb_zone);
     for (int j=1;j<=nb_zone;j++)
      ta_inverse(i)(j).Change_taille(maxi_num_noe_esclave(i)(j),0);
    };

   // ensuite on va remplir  ta_inverse
   for (int intot=1;intot<=nb_mail_Esclave;intot++)
    {for (int j=1;j<=nb_zone;j++)
       {const Tableau <Noeud*>& tesc= tesctotal(intot)(j); // pour simplifier la notation:
        int tesc_taille=tesc.Taille();                   //  tab des noeuds esclaves à considérer
        for (int inesc = 1;inesc<= tesc_taille;inesc++) // boucle sur les noeuds esclaves
           { Noeud* noee = tesc(inesc); // pour simplifier
             ta_inverse(intot)(j)(noee->Num_noeud()) = inesc;
           };
       };
    };
  
   // on va créer un tableau de changement des numéros de noeuds
   Tableau < Tableau <int> > nouveau_Noe(nb_mail_Esclave);
   // on cherche la taille maxi
   Tableau <int>  maxi_maxi_num_noe_esclave(nb_mail_Esclave); // les maxis pour chaque maillage
   for (int i=1;i<=nb_mail_Esclave;i++)
    {int& max_max_esclave = maxi_maxi_num_noe_esclave(i) = 0; // init
     for (int j=1;j<=nb_zone;j++)
      {int& max_num_esclave = maxi_num_noe_esclave(i)(j); // pour simplifier
       if (max_num_esclave > max_max_esclave)
         max_max_esclave = max_num_esclave;
      };
    };
   // on balaie les anciens noeuds
   for (int i=1;i<=nb_mail_Esclave;i++)
    {nouveau_Noe(i).Change_taille(maxi_maxi_num_noe_esclave(i));
     Tableau <int>& nouveau_Noe_i = nouveau_Noe(i);
     for (int j=1;j<=nb_zone;j++)
      { Tableau <int> & ta_inverse_old_ij = ta_inverse_old(i)(j);
        Tableau <int> & ta_inverse_ij = ta_inverse(i)(j);
        int nbN = ta_inverse_old_ij.Taille();
        for (int k=1;k<= nbN;k++) // k c'est le numéro de l'ancien noeud
         {if (ta_inverse_old_ij(k) != 0)
           {for (int k2=1;k2<= nbN;k2++)
             { if (ta_inverse_old_ij(k) == ta_inverse_ij(k2))
                 {nouveau_Noe_i(k)=k2;break;};
             };
           };
         };
      };
    };
  
//  // maintenant on met à jour le tableau tesN_encontact
//  //     Tableau < Tableau < LaLIST < LaLIST<ElContact>::iterator > > >  tesN_encontact;
//  // on sauvegarde l'ancien
//  Tableau < Tableau < LaLIST < LaLIST<ElContact>::iterator > > >  tesN_encontact_old(tesN_encontact);
//  // et on fait la passation
//  for (int i=1;i<=nb_mail_Esclave;i++)
//   {Tableau <int>& nouveau_Noe_i = nouveau_Noe(i);
//    int nbn = tesN_encontact(i).Taille();
//    for (int j=1;j<=nbn;j++)
//     { tesN_encontact(i)(nouveau_Noe_i(j)) = tesN_encontact_old(i)(j);
//     };
//   };
  
  // on continue avec le tableau: liste pour chaque noeud, des éléments qui contient ce noeud
  // indice(i)(j) : = la liste des éléments qui contiennent le noeud j, pour le maillage i
  Tableau < const Tableau <List_io < Element* > > *>  indice_old(indice);
  int nb_mail = indice_old.Taille();
  for (int i=1;i<=nb_mail;i++)
   {if (indice_old(i) != NULL)
     { int nbn = indice_old(i)->Taille();
//       const Tableau <List_io < Element* > > & indice_i = indice(i);
//       for (int j=1;j<=nbn;j++)
//indice
     
     }
   
   }
cout << "\n LesContacts::Prise_en_compte_nouvelle_numerotation_noeud() pas terminé !!"
     << " il faut changer lesMaillages pour récupérer directement la nouvelle numérotation !!";
Sortie(1);

 };
*/


// récupération via les éléments de contact des forces maxis
// et affichage éventuel
// un : le maxi en effort normal, deux: le maxi en effort tangentiel
// Dans le cas d'un   calcul  parallèle, il y a transfert du résultat au  cpu 0
// seules les cpu i calculent
DeuxDoubles LesContacts::Forces_contact_maxi(bool affiche)
 {tempsContact.Mise_en_route_du_comptage(); // def deb compt
  DeuxDoubles retour_min,retour_max; // def et init à 0
  int niveau_commentaire_lescontacts = Permet_affichage();
#ifdef UTILISATION_MPI
  int proc_en_cours = ParaGlob::Monde()->rank();
  if (proc_en_cours != 0)
   {
#endif
     {
      LaLIST <ElContact>::const_iterator iNmin,iNmax,iTmax,il,ilfin = listContact.end();
      LaLIST <ElContact>::const_iterator ilbegin = listContact.begin();
      for (il = iNmax = iTmax = iNmin  = ilbegin;il != ilfin; il++)
        if ((*il).Actif())
         { const ElContact& icont = (*il); // pour simplifier
           double fNmax =  icont.F_N_MAX();
           double fTmax =  icont.F_T_MAX();
           if (fNmax < retour_min.un )
             {retour_min.un = fNmax;iNmin = il;
             };
           if (fNmax > retour_max.un )
             {retour_max.un = fNmax;iNmax = il;
             };
           // fTmax est toujours positif, donc on ne teste que le maxi
           if (fTmax > retour_max.deux)
             {retour_max.deux = fTmax;iTmax = il;
             };
         };
      // affichage si on n'est pas en //
    #ifndef UTILISATION_MPI
      if ((affiche && (ParaGlob::NiveauImpression() > 2))
          || (niveau_commentaire_lescontacts > 3) // spécifiquement pour le contact
         )
          { cout << "\n contact: reaction ==>  F_N => [" << retour_min.un << " : " << retour_max.un << "]"
                 << ", F_T max = "  << retour_max.deux;
          };
       if (niveau_commentaire_lescontacts > 6)
        {if (iNmin != ilbegin ){cout << "\n mini F_N : "; (*iNmin).Affiche(0);};
         if (iNmax != ilbegin ){cout << "\n maxi F_N : "; (*iNmax).Affiche(0);};
         if (iTmax != ilbegin ){cout << "\n maxi F_T : "; (*iTmax).Affiche(0);};
         cout << "\n ";
        }
       else if (niveau_commentaire_lescontacts > 3)
        {if (iNmin != ilbegin ) {cout << "\n mini F_N : ";  (*iNmin).Affiche(1);};
         if (iNmax != ilbegin ){cout << "\n maxi F_N : ";  (*iNmax).Affiche(1);};
         if (iTmax != ilbegin ){cout << "\n maxi F_T : "; (*iTmax).Affiche(1);};
         cout << "\n ";
        }
    #endif
////-------- debug ------
// cout << "\n debug LesContacts::Forces_contact_maxi( ";
//      {if (iNmin != ilbegin ){cout << "\n mini F_N : "; (*iNmin).Affiche(0);};
//       if (iNmax != ilbegin ){cout << "\n maxi F_N : "; (*iNmax).Affiche(0);};
//       if (iTmax != ilbegin ){cout << "\n maxi F_T : "; (*iTmax).Affiche(0);};
//       cout << "\n ";
//      }
////-------- fin debug ------

//          cout << "\n contact: reaction ==> F_N max " << retour.un << " F_T max " << retour.deux;
//      if (niveau_commentaire_lescontacts > 6)
//       {if (iNmax != itbegin) {cout << "\n F_N max: "; (*iNmax).Affiche(0);};
//        if (iTmax != itbegin) {cout << "\n F_T max: "; (*iTmax).Affiche(0);};
//        cout << "\n";
//       }
//      else if (niveau_commentaire_lescontacts > 3)
//       {if (iNmax != itbegin) {cout << "\n F_N max: "; (*iNmax).Affiche(1);};
//        if (iTmax != itbegin) {cout << "\n F_T max: "; (*iTmax).Affiche(1);};
//        cout << "\n";
//       };
      tempsContact.Arret_du_comptage(); // fin cpu
     }
 #ifdef UTILISATION_MPI
     temps_transfert_court.Mise_en_route_du_comptage(); // comptage cpu
     // maintenant on va   transmettre au cpu 0
     Vecteur v_inter(3);
     v_inter(1) = retour_max.un; v_inter(2) = retour_max.deux;
     v_inter(3) = retour_min.un;
     // envoi
     v_inter.Ienvoi_MPI(0,49);
     // on attend pas
     temps_transfert_court.Arret_du_comptage(); // fin comptage cpu
   }
  else // cas du cpu 0
   {// l'objectif ici est de récupérer  les infos
    tempsContact.Arret_du_comptage(); // fin cpu
    int nb_proc_terminer = 0; // permettra de terminer
    while (nb_proc_terminer < (ParaGlob::Monde()->size()-1)) // gérer par les valeurs de tyfront
     { // on récupère un résultat de cpu i
        temps_transfert_court.Mise_en_route_du_comptage(); // comptage cpu
        Vecteur v_inter(3);
        mpi::request reqs1 = v_inter.Irecup_MPI(mpi::any_source, 49);
        reqs1.wait(); // on attend que le conteneur soit rempli

        if (v_inter(1) > retour_max.un)
          {retour_max.un = v_inter(1);};
        if (v_inter(2) > retour_max.deux)
          {retour_max.deux = v_inter(2);
          };
        if (v_inter(3) < retour_min.un)
          {retour_min.un = v_inter(3);};
        temps_transfert_court.Arret_du_comptage(); // fin comptage cpu
        nb_proc_terminer++; //  on prend en compte que l'on a récupéré un conteneur
      };
    if ((affiche && (ParaGlob::NiveauImpression() > 2))
         || (niveau_commentaire_lescontacts > 3) // spécifiquement pour le contact
        )
//         cout << "\n contact: reaction ==> |F_N max| = " << Dabs(retour_min.un) << " |F_T max| = " << DabsMaX(retour_min.deux,retour_max.deux);
         cout << "\n contact: reaction ==>  F_N => [" << retour_min.un << " : " << retour_max.un << "]"
         << ", F_T max = "  << retour_max.deux;
    };
 #endif

  tempsContact.Arret_du_comptage(); // fin cpu
  return retour_max;
 };

// récupération via les éléments de contact des gaps maxi en négatif, donc les mini
// un : le maxi en gap normal, deux: le maxi en gap tangentiel
// Dans le cas d'un   calcul  parallèle, il y a transfert du résultat au  cpu 0
// seules les cpu i calculent
DeuxDoubles LesContacts::Gap_contact_maxi(bool affiche)
 {tempsContact.Mise_en_route_du_comptage(); // def deb compt
  DeuxDoubles retour_max,retour_min;
  int niveau_commentaire_lescontacts = Permet_affichage();
#ifdef UTILISATION_MPI
  int proc_en_cours = ParaGlob::Monde()->rank();
  if (proc_en_cours != 0)
   {
#endif
     {
      LaLIST <ElContact>::const_iterator iNmax,iTmax,iNmin,iTmin,il,ilfin = listContact.end();
      LaLIST <ElContact>::const_iterator ilbegin=listContact.begin();
      for (il = iNmax = iTmax = iNmin = iTmin = ilbegin;il != ilfin; il++)
        if ((*il).Actif())
         { const ElContact& icont = (*il); // pour simplifier
           double fNmax =  icont.Gaptdt();
           double fTmax =  icont.Dep_T_tdt();
           if (fNmax > retour_max.un )
             {retour_max.un = fNmax;iNmax = il;
             };
           if (fNmax < retour_min.un )
             {retour_min.un = fNmax;iNmin = il;
             };
           if (fTmax > retour_max.deux )
             {retour_max.deux = fTmax;iTmax = il;
             };
           if (fTmax < retour_min.deux )
             {retour_min.deux = fTmax;iTmin = il;
             };
         };
      // affichage si on n'est pas en //
    #ifndef UTILISATION_MPI
      if ((affiche && (ParaGlob::NiveauImpression() > 2))
          || (niveau_commentaire_lescontacts > 3) // spécifiquement pour le contact
         )
          cout << ", [" << retour_min.un << " <= gap_N <= " << retour_max.un << "], [" << retour_min.deux <<" <= gap_T <= " << retour_max.deux<<"] ";
      if (niveau_commentaire_lescontacts > 6)
       {if (iNmin != ilbegin ){cout << "\n mini gap_N : "; (*iNmin).Affiche(0);};
        if (iNmax != ilbegin ){cout << "\n maxi gap_N : "; (*iNmax).Affiche(0);};
        if (iTmin != ilbegin ){cout << "\n mini gap_T : "; (*iTmin).Affiche(0);};
        if (iTmax != ilbegin ){cout << "\n maxi gap_T : "; (*iTmax).Affiche(0);};
        cout << "\n ";
       }
      else if (niveau_commentaire_lescontacts > 3)
       {if (iNmin != ilbegin ) {cout << "\n mini gap_N : ";  (*iNmin).Affiche(1);};
        if (iNmax != ilbegin ){cout << "\n maxi gap_N : ";  (*iNmax).Affiche(1);};
        if (iTmin != ilbegin ){cout << "\n mini gap_T : "; (*iTmin).Affiche(1);};
        if (iTmax != ilbegin ){cout << "\n maxi gap_T : "; (*iTmax).Affiche(1);};
        cout << "\n ";
       }
    #endif
      tempsContact.Arret_du_comptage(); // fin cpu
     }
 #ifdef UTILISATION_MPI
     temps_transfert_court.Mise_en_route_du_comptage(); // comptage cpu
     // maintenant on va   transmettre au cpu 0
     Vecteur v_inter(4);v_inter(1) = retour_max.un; v_inter(2) = retour_max.deux;
     v_inter(3) = retour_min.un;v_inter(4) = retour_min.deux;
     // envoi
     v_inter.Ienvoi_MPI(0,60);
     // on attend pas
     temps_transfert_court.Arret_du_comptage(); // fin comptage cpu
   }
  else // cas du cpu 0
   {// l'objectif ici est de récupérer  les infos
    tempsContact.Arret_du_comptage(); // fin cpu
    int nb_proc_terminer = 0; // permettra de terminer
    while (nb_proc_terminer < (ParaGlob::Monde()->size()-1)) // gérer par les valeurs de tyfront
     { // on récupère un résultat de cpu i
        temps_transfert_court.Mise_en_route_du_comptage(); // comptage cpu
        Vecteur v_inter(4);
        mpi::request reqs1 = v_inter.Irecup_MPI(mpi::any_source, 60);
        reqs1.wait(); // on attend que le conteneur soit rempli

        if (v_inter(1) > retour_max.un)
          {retour_max.un = v_inter(1);};
        if (v_inter(2) > retour_max.deux)
          {retour_max.deux = v_inter(2);
          };
        if (v_inter(3) < retour_min.un)
          {retour_min.un = v_inter(3);};
        if (v_inter(4) < retour_min.deux)
          {retour_min.deux = v_inter(4);};
        temps_transfert_court.Arret_du_comptage(); // fin comptage cpu
        nb_proc_terminer++; //  on prend en compte que l'on a récupéré un conteneur
      };
    if ((affiche && (ParaGlob::NiveauImpression() > 2))
         || (niveau_commentaire_lescontacts > 3) // spécifiquement pour le contact
        )
         cout << ", [" << retour_min.un << " <= gap_N <= " << retour_max.un << "], [" << retour_min.deux <<" <= gap_T <= " << retour_max.deux<<"] ";
    };
 #endif
  
  tempsContact.Arret_du_comptage(); // fin cpu
  return retour_min;
 };


// création et ajout des éléments de frontière correspondant aux angles morts
// en 2D : noeud frontière + éléments interne
// en 3D : ligne frontière + éléments interne
void LesContacts::ElementAngleMort(LesMaillages& lesMail)
 {
 // la liste des éléments qui contiennent des frontières, est reconstitué au démarrage avec Init_contact(..
//>>>>> list  <Element *> liste_elemens_front;
 // la liste pour chaque noeud, des éléments qui contient ce noeud : construite avec Init_contact
 // indice(i)(j) : = la liste des éléments qui contiennent le noeud j, pour le maillage i
//>>>>> Tableau < const Tableau <List_io < Element* > > *>  indice;
  // la liste des éléments frontières succeptibles d'entrer en contact
  // ces Front (qui contiennent que des pointeurs sauf une boite d'encombrement)
  // sont différents de ceux des maillages, et sont donc stocké en interne
//>>>>>   Tableau < Tableau < LaLIST_io <Front> > >  t_listFront;
       // t_listFront(i)(j)(k) : maillage maître (i)
       //  zone de contact (j)
       // (K) = kième frontière (dans l'ordre de la liste)
       // ** pour le tableaux t_listFront, le numéros dit de maillage, n'est pas le numéro
       // ** intrinsèque de maillage (telle que ceux associés aux noeuds et éléments)
       // ** mais uniquement un numéro locale d'ordre
       // ** mais on a: les éléments de frontière de t_listFront(i) font partie du maillage
       // i + (nb_mail_Esclave-nbmailautocontact)
   int niveau_commentaire_lescontacts = Permet_affichage();
 #ifdef UTILISATION_MPI
   if (ParaGlob::Monde()->rank() == 0)
 #endif
   if (niveau_commentaire_lescontacts > 4)
      cout << "\n ==> LesContacts::ElementAngleMort:   ";

   // --- à la fin du traitement on a besoin de mettre à jour les éléments mitoyens des Front
   // --- on sauvegarde des infos
   Tableau < Tableau < list <Front*> > >  t_listFront_initial; // les pointeurs de front initiaux sans les angles morts
   
   // t_liFront_noeud(i)(j)(k) : la liste des frontières initiales qui contiennent le noeud k de la zone j du maillage i
   Tableau < Tableau < Tableau < list <Front*> > > > t_liFront_noeud;
   t_liFront_noeud.Change_taille(lesMail.NbMaillage());
   //**** à faire la même chose pour les elements segment .....
   
   int tail1 = t_listFront.Taille(); // num ordre dans les maillages
   t_listFront_initial.Change_taille(tail1);
   for (int i =1;i<= tail1;i++)
    {int i_mail = i + (nb_mail_Esclave-nbmailautocontact); // le num de maillage
     int nb_noe_total = lesMail.Nombre_noeud(i_mail); // le nombre total de noeud du maillage
    
     int tail2=t_listFront(i).Taille(); // nombre de zones
     t_listFront_initial(i).Change_taille(tail2);
     t_liFront_noeud(i).Change_taille(tail2);
     
     for (int j=1;j<= tail2;j++) // on balaie les zones
      {list <Front*> & t_listFront_initial_ij = t_listFront_initial(i)(j); // pour simplifier
       LaLIST_io <Front> & t_listFront_ij = t_listFront(i)(j); // pour simplifier
       // on dimensionne au nombre maxi de noeud pour chaque zone ...
       // ça peut faire des gros tableaux mais il faut bien que l'on sache à quelle zone on s'intéresse ...
       // donc pour l'instant c'est l'idée que je retiens
       t_liFront_noeud(i)(j).Change_taille(nb_noe_total);
       Tableau <list <Front*> >& t_liFront_noeud_ij = t_liFront_noeud(i)(j);
       LaLIST_io <Front>::iterator il,ilfin=t_listFront_ij.end();
       for (il = t_listFront_ij.begin();il != ilfin; il++)
         {t_listFront_initial_ij.push_back(&(*il));
          // on va remplir t_liFront_noeud_ij
          ElFrontiere* elfro = (*il).Eleme(); // pour simplifier
          const Tableau <Noeud *>& tabN = elfro->TabNoeud_const();
          int nbNF = tabN.Taille();
          for (int l=1;l<=nbNF;l++)
            { Noeud * noe = tabN(l);
              t_liFront_noeud_ij(noe->Num_noeud()).push_back(&(*il));
            };
         };
      };
    };
       
   // --- constitution des front d'angle mort
   // l'idée est de rechercher les éléments qui sont en relation directe avec la frontière
   // via une ligne ou un point, mais qui n'ont été pris en compte du fait qu'ils n'étaient pas
   // reliés à une frontière de base
   // on commence par les lignes ce qui conduit éventuellement à augmenter t_listFront
   // ensuite on regarde les noeuds. Ainsi, si un élément nouveau est introduit via les lignes
   // les noeuds de la ligne + l'élément ne vont pas s'ajouter
       
   
      
   // 1) === cas des lignes frontières + éléments internes
   //        ce cas ne concerne que les espaces de travail 3D non axisymétrique
   if ((ParaGlob::Dimension() == 3) && !(ParaGlob::AxiSymetrie()))
     { // on crée les frontières lignes au cas où
       lesMail.CreeListFrontiere_ligne_3D();
       // on récupère toutes les frontières lignes
       // ramène le tableau de pointeurs de frontières ligne 3D créées par CreeListFrontiere_ligne_3D
       Tableau <LaLIST <Front>*>&  lifrLigne = lesMail.ListFront_ligne_3D();
       if (nom_ref_zone_contact.size() == 0)
        { // cas d'un contact non restreint
          // cela correspond finalement à une seule zone de contact (cf. Init_contact(..))
          //on boucle sur les maillages maitres de t_listFront
          for (int im=1;im<=nbmailMaitre;im++)
            { int im_mail = im + (nb_mail_Esclave-nbmailautocontact); // le num de maillage
              // ici on ne boucle pas sur les zones restreintes car il n'y en n'a pas
              LaLIST_io <Front> & la_list_des_front = t_listFront(im)(1);
              int nb_front_init = la_list_des_front.size(); // la taille initiale
              LaLIST <Front>& lifrLigne_mail = *lifrLigne(im_mail); // pour simplifier

              // avant de balayer les lignes, on fait une liste des éléments qui contiennent des frontières déjà référencées
              list <const Element* > li_Elem_avec_front;
              LaLIST_io <Front>::iterator il,ilfin = la_list_des_front.end();
              for (il = la_list_des_front.begin(); il != ilfin; il++)
                li_Elem_avec_front.push_back((*il).PtEI());
              // on supprime les doublons et on ordonne, c'est pour l'opti des recherches ensuites
              li_Elem_avec_front.sort();  // ordonne
              li_Elem_avec_front.unique(); // supprime les doublons
              
              // maintenant on regarde s'il faut ajouter des éléments d'angle mort
              // on va boucler sur les frontières lignes 3D
              LaLIST <Front>::iterator jl,jlfin=lifrLigne_mail.end();
              for (jl = lifrLigne_mail.begin(); jl != jlfin; jl++)
               { Element * el = (*jl).PtEI();// récup de l'élément
                 // on regarde si cet élément ne fait pas partie de la liste d'élément
                 // qui contient déjà une frontière
                 if (find(li_Elem_avec_front.begin(),li_Elem_avec_front.end(),el) == li_Elem_avec_front.end())
                   // on n'a pas trouvé d'élément,
                    //on ajoute l'élément front sauf cas particulier
                   {// on fait quelques tests pour écarter des cas non recevable
                    bool continuer = true; // par défaut
                    // si on est en 3D et que l'élément est lui même un élément ligne, ou un
                    // élément de surface, il ne constitue pas un angle mort puisqu'il n'y à pas de volume mort associé
                    Enum_type_geom enu_type_geom_ele = Type_geom_generique(el->Id_geometrie());
                    if ((enu_type_geom_ele == LIGNE) && (enu_type_geom_ele == SURFACE))
                     {continuer = false;}
                    if (continuer)
                      // on doit indiquer qu'il s'agit d'un élément d'angle mort
                      // on se sert d'un élément intermédiaire
                      {Front inter((*jl));
                       inter.Change_angle_mort(1);
                       la_list_des_front.push_back(inter);
                      };
                   }
               };
              int nb_front_fin = la_list_des_front.size(); // la taille finale
            #ifdef UTILISATION_MPI
              if (ParaGlob::Monde()->rank() == 0)
            #endif
              if (niveau_commentaire_lescontacts >2 )
                  cout << "\n ajout angle mort pour contact: mail. maitre "<< im_mail
                       <<" : "<<(nb_front_fin-nb_front_init)
                       << " frontieres segment  ";

            }; // fin de la boucle sur les maillages maitres
        }
       else
        { // cas d'une zone restreinte de contact
          // on va boucler sur les zones définies pour le contact
          //on boucle sur les maillages maitres de t_listFront
          for (int im=1;im<=nbmailMaitre;im++)
            { int im_mail = im + (nb_mail_Esclave-nbmailautocontact); // le num de maillage
              LaLIST <Front>& lifrLigne_mail = *lifrLigne(im_mail); // pour simplifier
              // ici on boucle sur les zones restreintes
              int nb_zone_et1 = 1+t_listFront(im).Taille();
              for (int i_zone=1;i_zone < nb_zone_et1;i_zone++)
               {// la liste des front de la zone
                LaLIST_io <Front> & la_list_des_front = t_listFront(im)(i_zone);
                int nb_front_init = la_list_des_front.size(); // la taille initiale
                // avant de balayer les lignes, on fait une liste des éléments relatifs à la zone
                // qui contiennent des frontières déjà référencées
                // on définit également la liste des noeuds correspondant à ces éléments frontières
                // de manière ensuite à ne garder que les éléments lignes qui contiendront des noeuds
                // de la zone restreinte
                // on travaille uniquement avec des numéros: on considère qu'une zone est relative à un
                // même maillage
                list < int > li_Elem_avec_front;
                list < int > li_Noeud_front_restreint;
                LaLIST_io <Front>::iterator il,ilfin = la_list_des_front.end();
                for (il = la_list_des_front.begin(); il != ilfin; il++)
                  {li_Elem_avec_front.push_back((*il).PtEI()->Num_elt());
                   const Tableau <Noeud *> titi = (*il).Eleme()->TabNoeud_const();
                   int nbNoeud = titi.Taille();
                   for (int i=1;i<= nbNoeud;i++)
                     li_Noeud_front_restreint.push_back(titi(i)->Num_noeud());
                   }
                // on supprime les doublons et on ordonne, c'est pour l'opti des recherches ensuites
                li_Elem_avec_front.sort();  // ordonne
                li_Elem_avec_front.unique(); // supprime les doublons
                li_Noeud_front_restreint.sort();
                li_Noeud_front_restreint.unique();
               
                // maintenant on regarde s'il faut ajouter des éléments d'angle mort
                // on va boucler sur les frontières lignes 3D
                LaLIST <Front>::iterator jl,jlfin=lifrLigne_mail.end();
                for (jl = lifrLigne_mail.begin(); jl != jlfin; jl++)
                 { // la liste des lignes comprend tous les maillages, on ne s'intéresse qu'au maillage
                   // de la zone
                   if ((*jl).NumMail() == im_mail)
                    {// on regarde si les noeuds de la ligne appartiennent à la zone restreinte
                     bool continuer = true;
                     // récup du tableau de noeuds
                     const Tableau <Noeud *> tita = (*jl).Eleme()->TabNoeud_const();
                     int nbNae = tita.Taille();
                     for (int ii=1;ii<= nbNae;ii++)
                       if (find(li_Noeud_front_restreint.begin(),li_Noeud_front_restreint.end(),tita(ii)->Num_noeud()) == li_Noeud_front_restreint.end())
                        {// un des noeuds de la ligne n'appartient pas à la zone restreinte, on arrête
                         continuer = false;
                         break;
                        };
                     // si ok pour l'appartenance à la zone, on continue
                     if (continuer)
                      {Element * el = (*jl).PtEI();// récup de l'élément
                       // on regarde si cet élément ne fait pas partie de la liste d'élément
                       // qui contient déjà une frontière
                       if (find(li_Elem_avec_front.begin(),li_Elem_avec_front.end(),el->Num_elt()) == li_Elem_avec_front.end())
                        // on n'a pas trouvé d'élément,
                        //on ajoute l'élément front sauf cas particulier
                        {// on fait quelques tests pour écarter des cas non recevable
                         bool continuer = true; // par défaut
                         // si on est en 3D et que l'élément est lui même un élément ligne, ou un
                         // élément de surface, il ne constitue pas un angle mort puisqu'il n'y à pas de volume mort associé
                         Enum_type_geom enu_type_geom_ele = Type_geom_generique(el->Id_geometrie());
                         if ((enu_type_geom_ele == LIGNE) && (enu_type_geom_ele == SURFACE))
                          {continuer = false;}
                         if (continuer)
                          // on n'a pas trouvé d'élément, on ajoute l'élément front
                          // on doit indiquer qu'il s'agit d'un élément d'angle mort
                          // on se sert d'un élément intermédiaire
                          {Front inter((*jl));
                           inter.Change_angle_mort(1);
                           la_list_des_front.push_back(inter);
                          };
                         };
                      };
                    };
                 };
                 
                la_list_des_front.sort();
                la_list_des_front.unique(egal_font);

                int nb_front_fin = la_list_des_front.size(); // la taille finale
              #ifdef UTILISATION_MPI
                if (ParaGlob::Monde()->rank() == 0)
              #endif
                if (niveau_commentaire_lescontacts >2 )
                     cout << "\n ajout angle mort pour contact: mail. maitre "<< im_mail
                          << " : zone de contact: " << i_zone
                          <<" : "<<(nb_front_fin-nb_front_init)
                          << " frontieres segment  ";
                 
               };// fin de la boucle sur les zones
            }; // fin de la boucle sur les maillages maitres de t_listFront
        };  // fin du test avec ou sans zone de contact
     };

   // 2) === maintenant on s'intéresse aux cas des noeuds frontières + éléments internes
   Tableau <LaLIST <Front>*>& listFrontiere = lesMail.ListFrontiere();
   int ntmail = listFrontiere.Taille(); // nb maxi de maillages
   // on regarde si la zone de contact est restreinte
   if (nom_ref_zone_contact.size() == 0)
    { // cas d'un contact non restreint
      // cela correspond finalement à une seule zone de contact (cf. Init_contact(..))
      
      int ilistfront = 1; // compteur pour le tableau t_listFront
      // on parcours les elements maitres: à cause d'un auto contact
      // éventuelle on peut avoir des maillages esclaves qui jouent le rôle également de
      // maillages maitres
      //on boucle sur les maillages maitres de t_listFront
      for (int jf=ntmail-nbmailMaitre+1;jf<=ntmail;jf++,ilistfront++)
       {  // ici on ne boucle pas sur les zones restreintes car il n'y en n'a pas
          LaLIST_io <Front> & la_list_des_front = t_listFront(ilistfront)(1);
          int nb_front_init = la_list_des_front.size(); // la taille initiale
          // pour continuer il faut: 1) qu'il y a des front, 2) que ces fronts appartiennent au maillage
          if (nb_front_init > 0)
           if ((*la_list_des_front.begin()).NumMail() == jf )
            {
             // on va créer une liste de numéro des noeuds de la zone de frontière concernée
             // et en même temps
             // avant de balayer les noeuds, on fait une liste des éléments relatifs à la zone
             // qui contiennent des frontières déjà référencées
             list <int> num_noe_front;
             list <const Element* > li_Elem_avec_front;
             LaLIST_io <Front>::iterator il,ilfin = la_list_des_front.end();
             for (il = la_list_des_front.begin(); il != ilfin; il++)
              { const Tableau <Noeud *>& tnoe = (*il).Eleme()->TabNoeud_const();
                int nb_noe_et1 = tnoe.Taille();
                for (int m =1;m<nb_noe_et1;m++)
                  num_noe_front.push_back(tnoe(m)->Num_noeud());
                li_Elem_avec_front.push_back((*il).PtEI());
              };
             // on ordonne et on supprime les doublons
             num_noe_front.sort(); // ordonne
             num_noe_front.unique(); // suppression des doublons
             li_Elem_avec_front.sort();  // ordonne
             li_Elem_avec_front.unique(); // supprime les doublons
             
             // on définit une liste temporaire des nouvelles front d'angle mort créé
             // c'est utilisé pour définir les mitoyens des éléments d'angle morts
             // qui sont apparus avec les angles morts (== les mitoyens angle mort des éléments angle morts !!)
             list <Front *> front_angle_mort;

             // maintenant on parcours les noeuds contenu dans num_noe_front
             // et on regarde s'il faut ajouter des éléments point d'angle mort
             list <int>::iterator ih,ih_fin=num_noe_front.end();
             for (ih = num_noe_front.begin();ih != ih_fin;ih++)
               {int i_noe = (*ih); // le num du noeud maître que l'on va tester
                Tableau <Noeud *> ttn(1);
                ttn(1)= &(lesMail.Noeud_LesMaille(jf,i_noe)); // récup du pointeur de noeud
////------ debug
//if (ttn(1)->Num_noeud() == 37 )
//  cout << "\n debug LesContacts::ElementAngleMort(...";
//// fin debug
                   
                // récup de la liste pour le noeud i_noe, des éléments qui contient ce noeud : construite avec Init_contact
                const List_io < Element* >& liel = (*indice(jf))(i_noe);
                // on boucle sur chaque élément qui contient le noeud
                List_io < Element* >::const_iterator il,ilfin=liel.end();
                for (il = liel.begin();il != ilfin;il++)
                  {const Element* el=(*il); // pour simplifier
                   // on regarde si cet élément ne fait pas partie de la liste d'élément
                   // qui contient déjà une frontière
                   bool trouver = false;
                   list <const Element* >::const_iterator ikl,iklfin=li_Elem_avec_front.end();
                   for (ikl = li_Elem_avec_front.begin();ikl != iklfin; ikl++)
                     {if (((*ikl)->Num_elt_const() == el->Num_elt_const()) && ((*ikl)->Num_maillage() == el->Num_maillage()))
                       {trouver= true; break;}
                     };
                   if (!trouver)
                    {// on n'a pas trouvé d'élément,
                     // 1. on récup le num local EF du noeud
                     int nb_noeu_el_et_1 = 1+el->Nombre_noeud(); // le nombre de noeud de l'élément
                     int num_noe_local = 0;
                     for (int k =1;k<nb_noeu_el_et_1;k++)
                       if (el->Num_noeud(k) == ttn(1)->Num_noeud())
                         {num_noe_local=k; break;};
                     // 2. on récupère la frontière noeud correspondante en forçant une création éventuelle
                     // on passe par l'intermédiaire de lesMail pour pouvoir modifier l'élément
                     // car c'est l'élément qui stocke et gère la frontière
                     Element& elem_non_const = lesMail.Element_LesMaille(el->Num_maillage(), el->Num_elt_const());
                     // on fait quelques tests pour écarter des cas non recevable
                     bool continuer = true; // par défaut
                     // a) si on est en 2D axi et que l'élément est un élément noeud ou un élément ligne, cela veut dire
                     // qu'il génére par rotation en 3D : une ligne ou une surface, or lignes ou surfaces n'intègrent pas
                     // d'élément de contact d'angle mort puisqu'il n'y à pas de volume mort associé
                     Enum_type_geom enu_type_geom_ele = Type_geom_generique(elem_non_const.Id_geometrie());
                     if (ParaGlob::AxiSymetrie() && ((enu_type_geom_ele == POINT_G) || (enu_type_geom_ele == LIGNE)))
                       {continuer = false;}
                     // b) si on est en axi et qu'il s'agit d'un élément surface, là c'est bon
                     else if (ParaGlob::AxiSymetrie() && (enu_type_geom_ele == SURFACE))
                       {continuer = true;}
                     // c) si on est en 3D et que l'élément est un élément noeud, ou ligne, ou surface, même sentence
                     else if ((ParaGlob::Dimension() == 3)
                         && ((enu_type_geom_ele == POINT_G) || (enu_type_geom_ele == LIGNE) || (enu_type_geom_ele == SURFACE))
                        )
                       {continuer = false;}
                     // on continue sauf cas particulier
                     if (continuer)
                       {// 3. on crée l'élément de frontière ad hoc
                        ElFrontiere* const elfpoint = elem_non_const.Frontiere_points(num_noe_local,true);
                        // ramene le numero de la frontière passée en argument si elle existe actuellement au niveau de l'élément
                        // sinon ramène 0
                        // ramene également type_front: qui indique le type de frontière: POINT_G, LIGNE ou SURFACE
                        // c'est une méthode très longue, a utiliser avec précaution
                        Enum_type_geom type_front;
                        int num_front = el->Num_frontiere(*elfpoint,type_front);
                        // 4. on crée l'élément front ad hoc et on l'ajoute à la liste
                        int angle_mort = 1;
                        la_list_des_front.push_front(Front(*elfpoint,&elem_non_const,num_front,angle_mort));
                        LaLIST_io <Front>::iterator iik = la_list_des_front.begin(); // récup de l'iterator
                        // on met à jour les frontières mitoyennes
                        int num_noeud = ttn(1)->Num_noeud(); // le num global du noeud
                        // récup de la liste des frontières initiales contenant le noeud
                        list <Front*> & t_liFront_noeud_ijk = t_liFront_noeud(ilistfront)(1)(num_noeud);
                        list <Front*>::iterator km,kmfin=t_liFront_noeud_ijk.end();
                        for (km=t_liFront_noeud_ijk.begin();km != kmfin;km++)
                          {(*km)->AjoutMitoyen(&(*iik));// ajout frontière pour la frontière initiale
                           (*iik).AjoutMitoyen(*km); // ajout frontière pour la nouvelle frontière d'angle mort
                           };
                        // on sauvegarde le nouvelle front dans une liste spécifique
                        front_angle_mort.push_front(&(*iik));
                      #ifdef UTILISATION_MPI
                        if (ParaGlob::Monde()->rank() == 0)
                      #endif
                        if (niveau_commentaire_lescontacts >4 )
                         { cout << "\n ajout front point: noeud "<<el->Num_noeud(num_noe_local)
                                << " mail: " << jf //<< " zone "
                                << " element: "<<el->Num_elt_const() ;
                         };
                       };
                    };
                  };
              };
             int nb_front_fin = la_list_des_front.size(); // la taille finale
           #ifdef UTILISATION_MPI
             if (ParaGlob::Monde()->rank() == 0)
           #endif
             if (niveau_commentaire_lescontacts >2 )
                  cout << "\n ajout angle mort pour contact: mail. maitre "<< jf <<" : "<<(nb_front_fin-nb_front_init)
                       << " frontieres point  ";
                       
             #ifdef MISE_AU_POINT
             // petite vérif en passant
           #ifdef UTILISATION_MPI
             if (ParaGlob::Monde()->rank() == 0)
           #endif
             if ((nb_front_fin-nb_front_init) != front_angle_mort.size())
              {cout << "\n *** erreur dans la definition des elements point d'angle mort "
                    << " on a deux listes qui les representent mais qui n'ont pas la meme taille "
                    << (nb_front_fin-nb_front_init) << " et " << front_angle_mort.size()
                    << "\n LesContacts::ElementAngleMort(..";
               Sortie(1);
              };
             #endif

             // on utilise front_angle_mort pour définir les mitoyens angle mort des éléments angle morts
             list <Front *>::iterator ag,agfin=front_angle_mort.end();
             for (ag=front_angle_mort.begin();ag != agfin; ag++)
              {list <Front *>::iterator bg;
               bg = ag; bg++;
               // on utilise deux boucles imbriquées
               for ( ; bg!= agfin ; bg++)
                 {const Tableau <Noeud *>& tan = (*ag)->Eleme()->TabNoeud_const();
                  const Tableau <Noeud *>& tbn = (*bg)->Eleme()->TabNoeud_const();
                  // normalement c'est des FrontPoinF, donc il n'y a qu'un seul noeud frontière
                  if (tan(1)->Num_noeud() == tbn(1)->Num_noeud())
                   // les deux éléments FrontPoinF partage le même noeud,
                   // on met à jour les mitoyens
                   { (*ag)->AjoutMitoyen((*bg));
                     (*bg)->AjoutMitoyen((*ag));
                   };
                 };
              };
             
             }; // fin du test if (nb_front_init > 0) et if ((*la_list_des_front.begin()).NumMail() == jf )

        }; // fin de la boucle sur les maillages maitres de t_listFront
    }
   else
    { // cas d'une zone restreinte de contact
      // on va boucler sur les zones définies pour le contact
      
      int ilistfront = 1; // compteur pour le tableau t_listFront
      // on parcours les elements maitres: à cause d'un auto contact
      // éventuelle on peut avoir des maillages esclaves qui jouent le rôle également de
      // maillages maitres
      //on boucle sur les maillages maitres de t_listFront
      for (int jf=ntmail-nbmailMaitre+1;jf<=ntmail;jf++,ilistfront++)
       {  // ici on boucle sur les zones restreintes
          int nb_zone_et1 = 1+t_listFront(ilistfront).Taille();
          for (int i_zone=1;i_zone < nb_zone_et1;i_zone++)
           {// la liste des front de la zone
            LaLIST_io <Front> & la_list_des_front = t_listFront(ilistfront)(i_zone);
            int nb_front_init = la_list_des_front.size(); // la taille initiale
            // pour continuer il faut: 1) qu'il y a des front, 2) que ces fronts appartiennent au maillage
            if (nb_front_init > 0)
             if ((*la_list_des_front.begin()).NumMail() == jf )
              {
               // on va créer une liste de numéro des noeuds de la zone de frontière concernée
               // et en même temps
               // avant de balayer les noeuds, on fait une liste des éléments relatifs à la zone
               // qui contiennent des frontières déjà référencées
               list <int> num_noe_front;
               list <const Element* > li_Elem_avec_front;
               LaLIST_io <Front>::iterator il,ilfin = la_list_des_front.end();
               for (il = la_list_des_front.begin(); il != ilfin; il++)
                { const Tableau <Noeud *>& tnoe = (*il).Eleme()->TabNoeud_const();
                  int nb_noe_et1 = tnoe.Taille();
                  for (int m =1;m<nb_noe_et1;m++)
                    num_noe_front.push_back(tnoe(m)->Num_noeud());
                  li_Elem_avec_front.push_back((*il).PtEI());
                };
               // on ordonne et on supprime les doublons
               num_noe_front.sort(); // ordonne
               num_noe_front.unique(); // suppression des doublons
               li_Elem_avec_front.sort();  // ordonne
               li_Elem_avec_front.unique(); // supprime les doublons
               
               // on définit une liste temporaire des nouvelles front d'angle mort créé
               // c'est utilisé pour définir les mitoyens des éléments d'angle morts
               // qui sont apparus avec les angles morts (== les mitoyens angle mort des éléments angle morts !!)
               list <Front *> front_angle_mort;

               // maintenant on parcours les noeuds contenu dans num_noe_front
               // et on regarde s'il faut ajouter des éléments point d'angle mort
               list <int>::iterator ih,ih_fin=num_noe_front.end();
               for (ih = num_noe_front.begin();ih != ih_fin;ih++)
                 {int i_noe = (*ih); // le num du noeud maître que l'on va tester
                  Tableau <Noeud *> ttn(1);
                  ttn(1)= &(lesMail.Noeud_LesMaille(jf,i_noe)); // récup du pointeur de noeud
                     
                  // récup de la liste pour le noeud i_noe, des éléments qui contient ce noeud : construite avec Init_contact
                  const List_io < Element* >& liel = (*indice(jf))(i_noe);
                  // on boucle sur chaque élément qui contient le noeud
                  List_io < Element* >::const_iterator il,ilfin=liel.end();
                  for (il = liel.begin();il != ilfin;il++)
                    {const Element* el=(*il); // pour simplifier
                     // on regarde si cet élément ne fait pas partie de la liste d'élément
                     // qui contient déjà une frontière
                     bool trouver = false;
                     list <const Element* >::const_iterator ikl,iklfin=li_Elem_avec_front.end();
                     for (ikl = li_Elem_avec_front.begin();ikl != iklfin; ikl++)
                       {if (((*ikl)->Num_elt_const() == el->Num_elt_const()) && ((*ikl)->Num_maillage() == el->Num_maillage()))
                         {trouver= true; break;}
                       };
                     if (!trouver)
                      {// on n'a pas trouvé d'élément,
                       // 1. on récup le num local EF du noeud
                       int nb_noeu_el_et_1 = 1+el->Nombre_noeud(); // le nombre de noeud de l'élément
                       int num_noe_local = 0;
                       for (int k =1;k<nb_noeu_el_et_1;k++)
                         if (el->Num_noeud(k) == ttn(1)->Num_noeud())
                           {num_noe_local=k; break;};
                       // 2. on récupère la frontière noeud correspondante en forçant une création éventuelle
                       // on passe par l'intermédiaire de lesMail pour pouvoir modifier l'élément
                       // car c'est l'élément qui stocke et gère la frontière
                       Element& elem_non_const = lesMail.Element_LesMaille(el->Num_maillage(), el->Num_elt_const());
                       // on fait quelques tests pour écarter des cas non recevable
                       bool continuer = true; // par défaut
                       // a) si on est en 2D axi et que l'élément est un élément noeud ou un élément ligne, cela veut dire
                       // qu'il génére par rotation en 3D : une ligne ou une surface, or lignes ou surfaces n'intègrent pas
                       // d'élément de contact d'angle mort puisqu'il n'y à pas de volume mort associé
                       Enum_type_geom enu_type_geom_ele = Type_geom_generique(elem_non_const.Id_geometrie());
                       if (ParaGlob::AxiSymetrie() && ((enu_type_geom_ele == POINT_G) || (enu_type_geom_ele == LIGNE)))
                         {continuer = false;}
                       // b) si on est en axi et qu'il s'agit d'un élément surface, là c'est bon
                       else if (ParaGlob::AxiSymetrie() && (enu_type_geom_ele == SURFACE))
                         {continuer = true;}
                       // c) si on est en 3D et que l'élément est un élément noeud, ou ligne, ou surface, même sentence
                       else if ((ParaGlob::Dimension() == 3)
                           && ((enu_type_geom_ele == POINT_G) || (enu_type_geom_ele == LIGNE) || (enu_type_geom_ele == SURFACE))
                          )
                         {continuer = false;}
                       // on continue sauf cas particulier
                       if (continuer)
                         {// 3. on crée l'élément de frontière ad hoc
                          ElFrontiere* const elfpoint = elem_non_const.Frontiere_points(num_noe_local,true);
                          // ramene le numero de la frontière passée en argument si elle existe actuellement au niveau de l'élément
                          // sinon ramène 0
                          // ramene également type_front: qui indique le type de frontière: POINT_G, LIGNE ou SURFACE
                          // c'est une méthode très longue, a utiliser avec précaution
                          Enum_type_geom type_front;
                          int num_front = el->Num_frontiere(*elfpoint,type_front);
                          // 4. on crée l'élément front ad hoc et on l'ajoute à la liste
                          int angle_mort = 1;
                          la_list_des_front.push_front(Front(*elfpoint,&elem_non_const,num_front,angle_mort));
                          LaLIST_io <Front>::iterator iik = la_list_des_front.begin(); // récup de l'iterator
                          // on met à jour les frontières mitoyennes
      //                    // t_liFront_noeud(i)(j)(k) : la liste des frontières initiales qui contiennent le noeud k de la zone j du maillage i
      //                    Tableau < Tableau < Tableau < list <Front*> > > > t_liFront_noeud;
                          int num_noeud = ttn(1)->Num_noeud(); // le num global du noeud
                          // récup de la liste des frontières initiales contenant le noeud
                          list <Front*> & t_liFront_noeud_ijk = t_liFront_noeud(ilistfront)(i_zone)(num_noeud);
                          list <Front*>::iterator km,kmfin=t_liFront_noeud_ijk.end();
                          for (km=t_liFront_noeud_ijk.begin();km != kmfin;km++)
                            {(*km)->AjoutMitoyen(&(*iik));// ajout frontière pour la frontière initiale
                             (*iik).AjoutMitoyen(*km); // ajout frontière pour la nouvelle frontière d'angle mort
                            };
                          // on sauvegarde le nouvelle front dans une liste spécifique
                          front_angle_mort.push_front(&(*iik));

                        #ifdef UTILISATION_MPI
                          if (ParaGlob::Monde()->rank() == 0)
                        #endif
                          if (niveau_commentaire_lescontacts >4 )
                           { cout << "\n ajout front point: noeud "<<el->Num_noeud(num_noe_local)
                                  << " mail: " << jf << " zone "<< i_zone
                                  << " element: "<<el->Num_elt_const() ;
                           };
                         };
                      };
                    };
                 };
               int nb_front_fin = la_list_des_front.size(); // la taille finale
             #ifdef UTILISATION_MPI
               if (ParaGlob::Monde()->rank() == 0)
             #endif
               if (niveau_commentaire_lescontacts >2 )
                     cout << "\n ajout angle mort pour contact: mail. maitre "<<jf << " : zone de contact: " << i_zone
                          <<" : " <<(nb_front_fin-nb_front_init)
                          << " frontieres point ";
                           
               #ifdef MISE_AU_POINT
               // petite vérif en passant
             #ifdef UTILISATION_MPI
               if (ParaGlob::Monde()->rank() == 0)
             #endif
               if ((nb_front_fin-nb_front_init) != front_angle_mort.size())
                {cout << "\n *** erreur dans la definition des elements point d'angle mort "
                      << " on a deux listes qui les representent mais qui n'ont pas la meme taille "
                      << (nb_front_fin-nb_front_init) << " et " << front_angle_mort.size()
                      << "\n LesContacts::ElementAngleMort(..";
                 Sortie(1);
                };
               #endif

               // on utilise front_angle_mort pour définir les mitoyens angle mort des éléments angle morts
               list <Front *>::iterator ag,agfin=front_angle_mort.end();
               for (ag=front_angle_mort.begin();ag != agfin; ag++)
                 {list <Front *>::iterator bg;
                  bg = ag; bg++;
                  // on utilise deux boucles imbriquées
                  for ( ; bg!= agfin ; bg++)
                    {const Tableau <Noeud *>& tan = (*ag)->Eleme()->TabNoeud_const();
                     const Tableau <Noeud *>& tbn = (*bg)->Eleme()->TabNoeud_const();
                     // normalement c'est des FrontPoinF, donc il n'y a qu'un seul noeud frontière
                     if (tan(1)->Num_noeud() == tbn(1)->Num_noeud())
                      // les deux éléments FrontPoinF partage le même noeud,
                      // on met à jour les mitoyens
                      { (*ag)->AjoutMitoyen((*bg));
                        (*bg)->AjoutMitoyen((*ag));
                      };
                    };
                 };
            #ifdef UTILISATION_MPI
              if (ParaGlob::Monde()->rank() == 0)
            #endif
              if (niveau_commentaire_lescontacts >7 )
               { cout << "\n liste finale des elements angle mort "
                      << " mail: " << jf << " zone "<< i_zone;
                 for (ag=front_angle_mort.begin();ag != agfin; ag++)
                   (*ag)->Affiche();
               };

              }; // fin du test if (nb_front_init > 0) et if ((*la_list_des_front.begin()).NumMail() == jf )

           };// fin de la boucle sur les zones
        }; // fin de la boucle sur les maillages maitres de t_listFront
    };  // fin du test avec ou sans zone de contact


 };

// init éventuel du pilotage par fct nD du niveau de commentaire
void LesContacts::Init_fct_niveau_commentaire()
   {
  #ifdef UTILISATION_MPI
    int proc_en_cours = ParaGlob::Monde()->rank();
  #endif
    string nom_fct = ParaGlob::param->ParaAlgoControleActifs().Fct_niveau_commentaire_LesContact();
    if (nom_fct != "_")
      {// on va récupérer la fonction
       if (sauve_lesFonctionsnD->Existe(nom_fct))
        { Fonction_nD * pt_fonct = sauve_lesFonctionsnD->Trouve(nom_fct);
          // sauvegarde
          fct_niveau_commentaire = pt_fonct;
        }
       else
        {
         #ifdef UTILISATION_MPI
           cout << "\n proc " << proc_en_cours
         #else
           cout << "\n"
         #endif
                << " *** erreur dans la definition de la fonction nD de pilotage du  "
                << " niveau de commentaire en contact, le nom : " << nom_fct
                << " ne correspond pas a une fonction nD existante !! ";
           Sortie(1);
        };
      }
    else
      {fct_niveau_commentaire=NULL;};
    // initialisation des conteneurs de la  fonction nD
    if (fct_niveau_commentaire != NULL)
          Definition_conteneurs_fctnD_TypeQuelconque
                     (fct_niveau_commentaire
                      ,tqi_fct_nD_niveau_commentaire,tqi_const_fct_nD_niveau_commentaire
                      ,t_num_ordre_fct_nD_niveau_commentaire);
   };


  // définition des conteneurs static des TypeQuelconque
  void LesContacts::Definition_conteneurs_fctnD_TypeQuelconque
       (Fonction_nD * pt_fonct,Tableau < TypeQuelconque *  >& tqi,Tableau < const TypeQuelconque *  >& tqii
        ,Tableau <int>&  t_num_ordre )
   {// on commence par récupérer le conteneurs des grandeurs à fournir
    const Tableau <EnumTypeQuelconque>& tab_queconque = pt_fonct->Tab_enu_quelconque();
    //  on dimensionne
    int tail_tab_queconque = tab_queconque.Taille();
    tqi.Change_taille(tail_tab_queconque);
    t_num_ordre.Change_taille(tail_tab_queconque);
    Grandeur_scalaire_entier grand_courant_entier(0); // par défaut pour la création des conteneurs quelconques
    Grandeur_scalaire_double grand_courant_double(0.); // par défaut pour la création des conteneurs quelconques
    // grandeurs de travail
    int dim = ParaGlob::Dimension();
    Coordonnee  inter(dim);
    Grandeur_coordonnee   grand_coor_courant(inter);

    // on va balayer les grandeurs quelconques
    for (int i=1;i<= tail_tab_queconque;i++)
    { bool trouver = false;
      EnumTypeQuelconque enu = tab_queconque(i);

      tqi(i) = NULL; // init
      t_num_ordre(i) = 1; //***** pour l'instant uniquement la première coordonnée s'il s'agit d'un Coordonnee
       // *** à abonder par la suite !!!!!!!!!!
      
      // on regarde tout d'abord les grandeurs spécifiques
      switch (enu)
        { // il y a des grandeurs vectorielles qui pour l'instant ne sont pas prises en compte
          // cf. fct nD
          // NORMALE_CONTACT, GLISSEMENT_CONTACT ,PENETRATION_CONTACT,FORCE_CONTACT,
          case CONTACT_NB_DECOL:
            { tqi(i) = new TypeQuelconque(CONTACT_NB_DECOL,X1,grand_courant_entier);
              trouver = true;
              break;
            }
          case CONTACT_PENALISATION_N:
            { tqi(i) = new TypeQuelconque(CONTACT_PENALISATION_N,X1,grand_courant_double);
              trouver = true;
              break;
            }
          case CONTACT_PENALISATION_T:
            { tqi(i) = new TypeQuelconque(CONTACT_PENALISATION_T,X1,grand_courant_double);
              trouver = true;
              break;
            }
          case CONTACT_NB_PENET:
            { tqi(i) = new TypeQuelconque(CONTACT_NB_PENET,X1,grand_courant_entier);
              trouver = true;
              break;
            }
          case CONTACT_CAS_SOLIDE:
            { tqi(i) = new TypeQuelconque(CONTACT_CAS_SOLIDE,X1,grand_courant_entier);
              trouver = true;
              break;
            }
          case CONTACT_ENERG_GLISSE_ELAS:
            { tqi(i) = new TypeQuelconque(CONTACT_ENERG_GLISSE_ELAS,X1,grand_courant_double);
              trouver = true;
              break;
            }
          case CONTACT_ENERG_GLISSE_PLAS:
            { tqi(i) = new TypeQuelconque(CONTACT_ENERG_GLISSE_PLAS,X1,grand_courant_double);
              trouver = true;
              break;
            }
          case CONTACT_ENERG_GLISSE_VISQ:
            { tqi(i) = new TypeQuelconque(CONTACT_ENERG_GLISSE_VISQ,X1,grand_courant_double);
              trouver = true;
              break;
            }
          case CONTACT_ENERG_PENAL:
            { tqi(i) = new TypeQuelconque(CONTACT_ENERG_PENAL,X1,grand_courant_double);
              trouver = true;
              break;
            }
          case NOEUD_PROJECTILE_EN_CONTACT:
            { tqi(i) = new TypeQuelconque(NOEUD_PROJECTILE_EN_CONTACT,X1,grand_courant_double);
              trouver = true;
              break;
             }
          case NOEUD_FACETTE_EN_CONTACT:
            { tqi(i) = new TypeQuelconque(NOEUD_FACETTE_EN_CONTACT,X1,grand_courant_double);
//              Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*tqi(i)).Grandeur_pointee()));
              trouver = true;
              break;
            }
          case NUM_NOEUD:
            { tqi(i) = new TypeQuelconque(NUM_NOEUD,X1,grand_courant_entier);
              trouver = true;
              break;
            }
          case NUM_MAIL_NOEUD:
            { tqi(i) = new TypeQuelconque(NUM_MAIL_NOEUD,X1,grand_courant_entier);
              trouver = true;
              break;
            }
          case POSITION_GEOMETRIQUE:
            {tqi(i) = new TypeQuelconque(POSITION_GEOMETRIQUE,X1,grand_coor_courant);
             trouver = true;
              break;
             }
          case POSITION_GEOMETRIQUE_t:
            {tqi(i) = new TypeQuelconque(POSITION_GEOMETRIQUE_t,X1,grand_coor_courant);
             trouver = true;
              break;
             }
          case POSITION_GEOMETRIQUE_t0:
            {tqi(i) = new TypeQuelconque(POSITION_GEOMETRIQUE_t0,X1,grand_coor_courant);
             trouver = true;
              break;
             }
          // *** pour l'instant la suite n'est pas opérationelle car il s'agit de vecteur
          // dont les composantes n'ont pas d'équivalent en ddl_enum_etendu
          //  il faudrait les définir si on veut pouvoir s'en servir
          case PENETRATION_CONTACT:
             {tqi(i) = new TypeQuelconque(PENETRATION_CONTACT,X1,grand_coor_courant);
              trouver = true;
               break;
              }
          case PENETRATION_CONTACT_T:
             {tqi(i) = new TypeQuelconque(PENETRATION_CONTACT_T,X1,grand_coor_courant);
              trouver = true;
               break;
              }
          case GLISSEMENT_CONTACT:
             {tqi(i) = new TypeQuelconque(GLISSEMENT_CONTACT,X1,grand_coor_courant);
              trouver = true;
               break;
              }
          case GLISSEMENT_CONTACT_T:
             {tqi(i) = new TypeQuelconque(GLISSEMENT_CONTACT_T,X1,grand_coor_courant);
              trouver = true;
               break;
              }
          case NORMALE_CONTACT:
             {tqi(i) = new TypeQuelconque(NORMALE_CONTACT,X1,grand_coor_courant);
              trouver = true;
               break;
              }
          case FORCE_CONTACT:
             {tqi(i) = new TypeQuelconque(FORCE_CONTACT,X1,grand_coor_courant);
              trouver = true;
               break;
              }
          case FORCE_CONTACT_T:
           {tqi(i) = new TypeQuelconque(POSITION_GEOMETRIQUE,X1,grand_coor_courant);
            trouver = true;
             break;
            }
          case CONTACT_COLLANT:
           { tqi(i) = new TypeQuelconque(CONTACT_COLLANT,X1,grand_courant_entier);
             trouver = true;
             break;
           }
          case NUM_ZONE_CONTACT:
           { tqi(i) = new TypeQuelconque(NUM_ZONE_CONTACT,X1,grand_courant_entier);
             trouver = true;
             break;
           }
        case NUM_ELEMENT:
         { tqi(i) = new TypeQuelconque(NUM_ELEMENT,X1,grand_courant_entier);
           trouver = true;
           break;
         }
        case NUM_MAIL_ELEM:
         { tqi(i) = new TypeQuelconque(NUM_MAIL_ELEM,X1,grand_courant_entier);
           trouver = true;
           break;
         }
         //*** fin

          default:
            trouver = false;
            break;
       };

      // si on n'a rien trouvé
      if (!trouver)
        {cout << "\n erreur***: la grandeur  " << NomTypeQuelconque(enu)
              << " n'existe pas les elements de contact "
              << " la fonction nD " << pt_fonct->NomFonction()
              << " ne peut pas etre renseignee "
              << "\n ElContact::Fct_nD_contact::Definition_conteneurs_static_TypeQuelconque(.." << flush;
         pt_fonct->Affiche();
         Sortie(1);
        };

    };
    // tableau de const intermédiaire pour l'appel de la fonction
    tqii.Change_taille(tail_tab_queconque);
    for (int i=1;i<= tail_tab_queconque;i++)
      tqii(i) = tqi(i);
   };

// calcul d'une fonction nD relative à des données de LesContacts
double LesContacts::Valeur_fct_nD_LesContacts
        (Fonction_nD * pt_fonct,list<TypeQuelconque>* li
         ,Tableau < TypeQuelconque *  >& tqi
         ,Tableau < const TypeQuelconque *  >& tqii
         ,Tableau <int>&  t_num_ordre) const
 { // on commence par récupérer les conteneurs des grandeurs à fournir
   List_io <Ddl_enum_etendu>& li_enu_scal = pt_fonct->Li_enu_etendu_scalaire();
   List_io <TypeQuelconque >& li_quelc = pt_fonct->Li_equi_Quel_evolue();
   const Tableau <EnumTypeQuelconque>& tab_queconque = pt_fonct->Tab_enu_quelconque();


   // on passe en revue les grandeurs pour les renseigner
   
   // --- on balaie maintenant la liste des grandeurs à sortir
   // 1) tout d'abord les ddl étendues
   // pour l'instant cette liste doit-être vide
   if (li_enu_scal.size() != 0)
    { cout << "\n *** erreur : LesContacts::Valeur_fct_nD_LesContacts "
           << " la fonction nD associe a l'affichage pour LesContacts ne doit pas "
           << " inclure des Ddl_enum_etendu !! changer la fonction ";
      Sortie(1);
    }
   Tableau <double> val_ddl_enum (li_enu_scal.size()); // init par défaut


   // 2) puis les grandeurs quelconques évoluées
   {List_io<TypeQuelconque>::iterator ipq,ipqfin=li_quelc.end();
    for (ipq=li_quelc.begin();ipq!=ipqfin;ipq++)
    { bool trouver = false;
      TypeQuelconque& enuTQ = (*ipq); // pour simplifier
      const TypeQuelconque_enum_etendu& en_ET_TQ = enuTQ.EnuTypeQuelconque().EnumTQ();
      
      // si li est non nulle, li indique spécifiquement quelle grandeur on veut sortir
      if (li != NULL)
       { // on va balayer la liste et voir si une grandeur correspond
         list<TypeQuelconque>::iterator ili,ilifin =  li->end();
         for (ili = li->begin();ili != ilifin; ili++)
          { TypeQuelconque& enuTQ_li = (*ili); // pour simplifier
            const TypeQuelconque_enum_etendu& en_ET_TQ_li = enuTQ_li.EnuTypeQuelconque().EnumTQ();
            if (en_ET_TQ_li == en_ET_TQ ) // cas ou on a trouvé la grandeur demandée
              { // pour l'instant seules les grandeurs scalaires entières sont admises
                Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*ipq).Grandeur_pointee()));
                Grandeur_scalaire_entier& gr_li= *((Grandeur_scalaire_entier*) ((*ili).Grandeur_pointee()));
                (*gr.ConteneurEntier()) = (*gr_li.ConteneurEntier());
                trouver = true;
              };
          };
        };
      if (!trouver) // si non existant, init par défaut
        (*ipq).Grandeur_pointee()->InitParDefaut();
    };
   };

   // 3) et enfin les grandeurs quelconques:
   // >>> en fait même topo que pour la liste
   //        les conteneurs sont normalement déjà bien dimensionné
   int tail_tab_queconque = tab_queconque.Taille();

   // on va balayer les grandeurs quelconques
   for (int i=1;i<= tail_tab_queconque;i++)
   { bool trouver = false;
     EnumTypeQuelconque enu = tab_queconque(i);
          
     // si li est non nulle, li indique spécifiquement quelle grandeur on veut sortir
     if (li != NULL)
      { // on va balayer la liste et voir si une grandeur correspond
        list<TypeQuelconque>::iterator ili,ilifin =  li->end();
        for (ili = li->begin();ili != ilifin; ili++)
         { TypeQuelconque& enuTQ_li = (*ili); // pour simplifier
           EnumTypeQuelconque en_ET_TQ_li = enuTQ_li.EnuTypeQuelconque().EnumTQ();
           if (en_ET_TQ_li == enu ) // cas ou on a trouvé la grandeur demandée
             { // pour l'instant seules les grandeurs scalaires entières sont admises
               Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*tqi(i)).Grandeur_pointee()));
               Grandeur_scalaire_entier& gr_li= *((Grandeur_scalaire_entier*) ((*ili).Grandeur_pointee()));
               (*gr.ConteneurEntier()) = (*gr_li.ConteneurEntier());
               trouver = true;
             };
         };
       };
     if (!trouver) // si non existant, init par défaut
       (*tqi_fct_nD_niveau_commentaire(i)).Grandeur_pointee()->InitParDefaut();

   };
   // tqii pointe aux mêmes endroit que tqi mais est déclaré en const ...
   // calcul de la valeur et retour dans tab_ret
   Tableau <double> & tab_val = pt_fonct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,&(tqii),&t_num_ordre);
   
   #ifdef MISE_AU_POINT
   if (tab_val.Taille() != 1)
      { cout << "\nErreur : la fonction nD " << pt_fonct->NomFonction()
             << " doit calculer un scalaire or le tableau de retour est de taille "
             << tab_val.Taille() << " ce n'est pas normal !\n" << flush;
        if (Permet_affichage() > 2 )
          cout << "\n  LesContacts::Valeur_fct_nD_LesContacts(... \n";
        Sortie(1);
      };
   #endif
   // on récupère le premier élément du tableau uniquement
   return tab_val(1);
 };
 

// recherche dans les Front enregistrés de la zone , c-a-d dans t_listFront(i)(zone)
//  un Front de même origine , c-a-d sans les mitoyens
// si aucun résultat retourne NULL
// sinon ramène l'élément de t_listFront de même origine
Front* LesContacts::Front_de_meme_origine(Front* fro, int zone) const
{ // on récupère le num de maillage associé
  // récup du numero du maillage  rattache
  int numMail = fro->NumMail();
  int i =  numMail - (nb_mail_Esclave-nbmailautocontact);

  // on va balayer dans la  zone de contact
  LaLIST_io <Front> & list_fronta = (t_listFront(i)(zone)); // pour simplifier
  LaLIST_io <Front>::iterator il,ilfin = list_fronta.end();
  for (il = list_fronta.begin();il != ilfin; il++)
      if ((*il).MemeOrigine(*fro))
         return &((*il));
  // arrivée ici, cela veut dire que l'on n'a rien trouvé
  return NULL;




 // la liste des éléments frontières succeptibles d'entrer en contact
 // ces Front (qui contiennent que des pointeurs sauf une boite d'encombrement)
 // sont différents de ceux des maillages, et sont donc stocké en interne
// Tableau < Tableau < LaLIST_io <Front> > >  t_listFront;
      // t_listFront(i)(j)(k) : maillage maître (i)
      //  zone de contact (j)
      // (K) = kième frontière (dans l'ordre de la liste)
      // ** pour le tableaux t_listFront, le numéros dit de maillage, n'est pas le numéro
      // ** intrinsèque de maillage (telle que ceux associés aux noeuds et éléments)
      // ** mais uniquement un numéro locale d'ordre
      // ** mais on a: les éléments de frontière de t_listFront(i) font partie du maillage
      // i + (nb_mail_Esclave-nbmailautocontact)

};


#ifdef UTILISATION_MPI
  // mise à jour list contact pour proc 0:
  // il s'agit des infos minimales
  void LesContacts::Mise_a_jour_liste_contacts_actif_interProc()
    { temps_transfert_long.Mise_en_route_du_comptage(); // comptage cpu
      // on va se servir d'un  conteneur intermédiaire
      union double_int
        {  double  x;
           int     n[2];
        } ;
      double_int xinter;

      int proc_en_cours = ParaGlob::Monde()->rank();
      if (proc_en_cours != 0)  // seules les proc i>0 définissent des éléments de contact
        // on boucle sur les elements de contact du proc i pour les transmettres à proc 0
        { int nb_contact_actif = Calcul_Nb_contact_actif();
          // on a besoin de 8 entiers pour repérer un élément de contact, on va transmettre
          // un vecteur contenant ces infos
////---debug
//cout << "\n LesContacts::Mise_a_jour_liste_contacts_actif_interProc "
//     << " proc "<< proc_en_cours << ", nb_contact= "<< listContact.size();
////--fin debug
          v_interProc_contact_actif.Change_taille(5*nb_contact_actif);
          int rang=1; // init
          LaLIST <ElContact>::iterator il,ilfin = listContact.end();
          for (il = listContact.begin();il != ilfin; il++)
            if ((*il).Actif())
              {xinter.n[0]  = (*il).Actif(); xinter.n[1]  = (*il).Cas_solide();
               v_interProc_contact_actif(rang) = xinter.x;rang++;
               xinter.n[0]  = (*il).Esclave()->Num_Mail(); xinter.n[1]  = (*il).Esclave()->Num_noeud();
               v_interProc_contact_actif(rang) = xinter.x;rang++;
               xinter.n[0]  = (*il).Elfront()->NumUnique(); xinter.n[1]  = (*il).Const_Num_zone_contact();
//---debug
#ifdef MISE_AU_POINT
if (xinter.n[0] == 0)
   {cout << "\n *** debug erreur LesContacts::Mise_a_jour_liste_contacts_actif_interProc "
        << " proc "<< proc_en_cours << ", num unique nul !! ";
    (*il).Affiche(1);
   }
#endif
//--fin debug

               v_interProc_contact_actif(rang) = xinter.x;rang++;
               xinter.n[0]  = (*il).Lissage_normale(); xinter.n[1]  = (*il).Collant();
               v_interProc_contact_actif(rang) = xinter.x;rang++;
               xinter.n[0]  = (*il).Elfront_initiale()->NumUnique(); xinter.n[1]  = 0; //  xinter.n[1] ne sert pas ici
               v_interProc_contact_actif(rang) = xinter.x;rang++;
//---debug
#ifdef MISE_AU_POINT
if (xinter.n[0] == 0)
   {cout << "\n *** debug erreur LesContacts::Mise_a_jour_liste_contacts_actif_interProc "
        << " proc "<< proc_en_cours << ", num unique _zero est  nul !! ";
    (*il).Affiche(1);
   }
#endif
//--fin debug

              };
          // on transmet à proc 0
          // tout d'abord la taille
          mpi::request reqs1 = ParaGlob::Monde()->isend(0, 63, 5*nb_contact_actif);
          // puis le conteneur
          mpi::request reqs2 = v_interProc_contact_actif.Ienvoi_MPI(0,64);
        }
      else
        // cas du proc 0
        { int nb_proc_terminer = 0; // permettra de terminer
          // on supprime la liste existante car:
          // - si on teste l'existance d'un élément à chaque ajout => il faut parcourir la liste qui peut être longue
          // - il faut y adjoindre une méthode pour supprimer les éléments qui ont été supprimés par un proc
          // => c'est plus simple de repartir d'une liste vide,
          listContact.clear(); // init
          while (nb_proc_terminer < (ParaGlob::Monde()->size()-1))
            { // on récupère un résultat de cpu i
              int  taille_transfert;
              mpi::request reqs1 = ParaGlob::Monde()->irecv(mpi::any_source, 63, taille_transfert);
              mpi::status stat = reqs1.wait(); // on attend que le conteneur soit rempli
              v_interProc_contact_actif.Change_taille(taille_transfert); //  le conteneur
              int source = stat.source(); // récupération du numéro de la source
              //   on récupère
              mpi::request reqs2 = v_interProc_contact_actif.Irecup_MPI(source, 64);
              reqs2.wait(); // on attend que le conteneur soit rempli
              nb_proc_terminer++; //  on prend en compte que l'on a récupéré un conteneur
              // on va  remplir la liste des  contacts actifs
              int rang  = 1; // init
              while (rang <  taille_transfert+1)
                { // récup des infos pour le contact
                  xinter.x = v_interProc_contact_actif(rang);rang++;
                  int actif = xinter.n[0]; int casSolide = xinter.n[1];
                  xinter.x = v_interProc_contact_actif(rang);rang++;
                  int num_mail = xinter.n[0]; int num_noeud = xinter.n[1];
                  xinter.x = v_interProc_contact_actif(rang);rang++;
                  int numUnique = xinter.n[0]; int numZoneContact = xinter.n[1];
                  xinter.x = v_interProc_contact_actif(rang);rang++;
                  int lissageNormale = xinter.n[0]; int collant = xinter.n[1];
                  
                  xinter.x = v_interProc_contact_actif(rang);rang++;
                  int numUnique_0 = xinter.n[0];

                  
                  // on crée un élément de contact
                  ElContact elcont(pointe_t_listFront(numUnique)
                                   ,&(lesMaille->Noeud_LesMaille(num_mail,num_noeud))
                                   ,fct_nD_contact
                                   ,pointe_t_listFront(numUnique_0),collant);
                  elcont.Num_zone_contact()=numZoneContact; // affectation du numéro de zone
                  elcont.Change_lissage_normale(lissageNormale); // affectation du lissage
                  if (actif)  elcont.Met_actif();
                  else elcont.Met_Inactif();
                  listContact.push_front(elcont);
                };
            };
//          //---debug
//          cout << "\n LesContacts::Mise_a_jour_liste_contacts_actif_interProc "
//               << " proc "<< proc_en_cours << ", nb_contact= "<< listContact.size();
//          //--fin debug
          Calcul_Nb_contact_actif(); // mise à jour
        };
     temps_transfert_long.Arret_du_comptage();
    };
  
#endif


