// 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 "ElContact.h"
#include "Droite.h"
#include "ConstMath.h"
#include "MathUtil.h"
#include "ElemMeca.h"
#include "Util.h"
#include <cmath>
#include "TypeConsTens.h"
#include "TypeQuelconqueParticulier.h"


	// --- calcul des puissances virtuelles développées par les efforts de contact ----------
	//     et eventuellement calcul de la raideur associé 
	// -> explicite à tdt 
Vecteur* ElContact::SM_charge_contact()
	{
   if (Permet_affichage() > 5)
      {cout << "\n -- ElContact::SM_charge_contact: ";this->Affiche(1);}

   int contactType = ElContact::Recup_et_mise_a_jour_type_contact();
//cout << "\n -- debug ElContact::SM_charge_contact: contactType= " << contactType;

   // def de quelques dimensions pour simplifier
	  int dima = ParaGlob::Dimension();
	  int tab_taille = tabNoeud.Taille();
	  int nbddl = ddlElement_assemblage->NbDdl();
	  energie_penalisation = 0.;
	  energie_frottement.Inita(0.);
   // récup  d'une place pour le résidu local et mise à jour de list_SM éventuellement
   RecupPlaceResidu(nbddl);
   Vecteur& SM_res = *residu; // pour simplifier
   Mise_a_jour_ddlelement_cas_solide_assemblage(); // mise à jour de ddl element et de cas_solide

   // la suite de la méthode ne fonctionne que pour les type 2 et 4 de contact,
   //  dans le cas contraire on revient directement
	  if (!((contactType == 2) || (contactType == 4) || (contactType == 41)|| (contactType == 42)))
	      return residu;
	  // ---- sinon .... cas du contact par pénalisation -----
   //  dans le cas 2: on calcul un facteur de pénalisation
   //  dans le cas 4: on déduit le facteur de pénalisation à partir des forces internes
   //                 et de la condition cinématique de contact
	  // on considère ici que le point de contact est déterminé et on s'occupe de déterminer les
	  // efforts éventuels dus au contact ainsi que les énergies échangées sur le pas de temps
  
	  // a priori on suppose que le contact est collant --> calcul des efforts de contact
	  Coordonnee  Noe_atdt = noeud->Coord2(); // récup de la position actuelle du noeud projectile
   // recup des dernières différentes informations calculées au niveau de la cinématique de contact
   Coordonnee M_impact; // le point d'impact, la normale
   Vecteur phii;
   RecupInfo(N,M_impact,phii,false);
	  // en fait due aux différents algos de recherche de contact, normalement la position de contact est sauvegardée 
	  // dans l'élément, donc on utilise cette info, par contre on utilise RecupInfo pour calculer les autres infos : normale et phi

   // changement  éventuel par une moyenne glissante des positions successive du noeud esclave
   // Noe_atdt est modifié éventuellement 
   ChangeEnMoyGlissante(Noe_atdt);
	  
	  // calcul de la pénétration normale: deltaX=de la surface au point pénétré
	  Coordonnee deltaX(dima);
   switch (contactType)
     { case 2: // cas d'une pénalisation classique
       case 41: case 42:// idem après le basculement du cas 4
        { deltaX = Noe_atdt - M_impact;
          break;
        }
       case 4: // cas ou le noeud à été projeté sur la surface
        // normalement Noe_atdt est identique à M_impact
        { deltaX =  M_noeud_tdt_avant_projection - Noe_atdt;
          break;
        }
       default: cout << "\n erreur, le cas contactType = " << contactType
                     << " ne devrait pas exister ici !! "<< endl;
                Sortie(1);
        break;
     };
  
	  // le N sort de la surface maître, donc rentre dans la surface esclave
	  double gap= N * deltaX; // gap est négatif quand il y a pénétration
   if ((Permet_affichage() > 7) && (contactType==4))
     {double a = (deltaX - gap*N).Norme(); // on calcule pour vérifier
      cout << " diff_projX= "<< a;  // l'erreur de projection normale
     };
	  // récupération du facteur de pénalisation adaptée au cas de calcul et à la géométrie
   double d_beta_gap=0.; // init de la dérivée du facteur de pénalisation / au gap
   // calcul du facteur de pénalisation si le contact est de type 2
   if (contactType == 2)
    { penalisation = CalFactPenal(gap,d_beta_gap,contactType);}
   // sinon dans le cas 41 on utilise la valeur qui est en cours pondéré dans une moyenne glissante
   // avec le type 4
   else if (contactType == 41) // après basculement du 4 on utilise la pénalisation
    { double essai_penalisation = CalFactPenal(gap,d_beta_gap,contactType);
      // maintenant on va calculer la moyenne glissante, de manière à lisser les variations
      ElContact::Moyenne_glissante_penalisation(penalisation, essai_penalisation);
    };
   // si on est en 42, on conserve le facteur précédemment calculé
   gap_tdt = gap; // sauvegarde


/* // debug
//cout << "\n noeud: " << noeud->Num_noeud() <<" mailage:"<<noeud->Num_Mail()<< " : gap= " << gap <<" \n Noe_atdt ";Noe_atdt.Affiche(); cout << " Noe_a0:"; noeud->Coord0().Affiche();
//cout << " Noe_at:"; noeud->Coord1().Affiche();
//cout << "\n M_impact:"; M_impact.Affiche(); cout << "\n deltaX:"; deltaX.Affiche();
//cout << "\n N:"; N.Affiche(); cout << " debug ElContact::SM_charge_contact()"<<endl;
//// fin debug 
	  // limitation éventuelle au niveau de la pénétration maxi
//	  double max_pene = ParaGlob::param->ParaAlgoControleActifs().Penetration_contact_maxi(); */
	  int typ_cal= ParaGlob::param->ParaAlgoControleActifs().TypeCalculPenalisationPenetration();
   double borne_regularisation;
   if (fct_nD_contact.fct_nD_penetration_borne_regularisation != NULL)
        {borne_regularisation = Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_borne_regularisation
                                              ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_borne_regularisation
                                              ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_borne_regularisation
                                              ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_borne_regularisation);
        }
   else {borne_regularisation = ParaGlob::param->ParaAlgoControleActifs().Penetration_borne_regularisation();}


// pour l'instant je supprime le cas de la limitation du déplacement, car je pense qu'il faudrait y adjoindre les forces
// de réaction associées sinon l'équilibre est faux !! donc on commente pour l'instant
	  // méthode de limitation de la pénétration: opérationnelle si max_pene > 0
	  // si max_pene < 0 on intervient sur l'incrément de temps !!
/*	  if (max_pene > 0)
	   {if ((gap < (-  max_pene)) && (Dabs(gap) > ConstMath::trespetit))
	    // on modifie la position du noeud pour tenir compte de la pénalisation
//		 { Coordonnee nevez_posi(M_impact - 2 * max_pene * N);
		   { Coordonnee nevez_posi(M_impact + (max_pene / deltaX.Norme()) * deltaX);
		     noeud->Change_coord2(nevez_posi);
			  gap = - max_pene;
		    };
		 };*/
  
	  // --- calcul de la force normale	
	  // avec limitation éventuelle de l'intensité de la force de contact
	  double intens_force = 0. ; // init
	  bool force_limiter=false;
   Coordonnee F_N(dima); // init
  
   switch (contactType)
     { case 2: // cas d'une pénalisation classique
       case 41: case 42: // idem après basculement du cas 4
        { intens_force = gap * penalisation; // négatif quand on a pénétration
          double max_force_noeud;
          if (fct_nD_contact.fct_nD_force_contact_noeud_maxi != NULL)
               {max_force_noeud = Valeur_fct_nD(fct_nD_contact.fct_nD_force_contact_noeud_maxi
                                  ,ElContact::Fct_nD_contact::tqi_fct_nD_force_contact_noeud_maxi
                                  ,ElContact::Fct_nD_contact::tqi_const_fct_nD_force_contact_noeud_maxi
                                  ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_force_contact_noeud_maxi);
               }
          else {max_force_noeud = ParaGlob::param->ParaAlgoControleActifs().Force_contact_noeud_maxi();}
          if (Dabs(intens_force) > max_force_noeud)
            {intens_force = max_force_noeud * Signe(intens_force);
             force_limiter = true;
            }
          // on recalcul la pénalisation associée
          if ((Dabs(gap) > ConstMath::trespetit) && force_limiter)
            {penalisation = intens_force / gap;
             if (Permet_affichage() > 5)
               {cout << " ** limitation penalisation= " << penalisation
                     << " intensite force = " << intens_force << flush;};
            };
          // F_N c'est la force qui appuie sur le noeud esclave d'où le - car intens_force est négatif
          F_N = - N * intens_force; //(-gap * penalisation);
          break;
        }
       case 4: // cas ou le noeud à été projeté sur la surface
        // on récupère la force de contact via la puissance interne des éléments contenants
        // le noeud exclave (le calcul des forces internes doit déjà avoir été effectué)
        {  TypeQuelconque_enum_etendu enu(FORCE_GENE_INT);
            // récupération du conteneur pour lecture uniquement
            const TypeQuelconque& typquel = noeud->Grandeur_quelconque(enu);
            const  Grandeur_coordonnee& cofo = *((Grandeur_coordonnee*) typquel.Const_Grandeur_pointee());
            // signe - car le contact doit s'opposer exactement à l'effort interne
            F_N = - cofo.ConteneurCoordonnee_const(); // récup de - la réaction
            if (Permet_affichage() > 6)  cout << " F_N(force_interne)= " << F_N << " ";
            // en fait on ne va garder que la partie normale à la facette, ce qui laissera le glissement possible
            intens_force = - F_N * N; // 1) projection de la réaction
            F_N = - N * intens_force; // 2) recalcul uniquement de la partie normale
            // on va également tenter un calcul d'une pénalisation équivalente
//            penalisation = Dabs(intens_force / (Dabs(gap)));// + ConstMath::unpeupetit)) ;
            double max_pene;
            if (fct_nD_contact.fct_nD_penetration_contact_maxi != NULL)
                 {max_pene = Dabs(Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_contact_maxi
                                 ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_contact_maxi
                                 ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_contact_maxi
                                 ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_contact_maxi));
                 }
            else {max_pene = Dabs(ParaGlob::param->ParaAlgoControleActifs().Penetration_contact_maxi());}



//            double essai_penalisation; // variable intermédiaire
//            if (Dabs(gap) > max_pene)
//              {essai_penalisation = Dabs(intens_force) / Dabs(gap);} //(Dabs(gap) + ConstMath::unpeupetit)) ;}
//            else // sinon la pénétration est vraiment petite, on va cibler la valeur de max_pene
//              {essai_penalisation = Dabs(intens_force) / max_pene;
//               if (Permet_affichage() > 5) cout << "\n (penalisation limite) ";
//              };
//            // on tient compte d'un facteur multiplicatif éventuelle
         
            // a priori c'est cette méthode qui est la plus performante (idem implicite)
            double essai_penalisation = Dabs(intens_force) / max_pene;

            // maintenant on va calculer la moyenne glissante
            ElContact::Moyenne_glissante_penalisation(penalisation, essai_penalisation);
         
         
//            if (Dabs(gap) > max_pene)
//              {penalisation = Dabs(intens_force) / Dabs(gap);} //(Dabs(gap) + ConstMath::unpeupetit)) ;}
//            else // sinon la pénétration est vraiment petite, on va cibler la valeur de max_pene
//              {penalisation = Dabs(intens_force) / max_pene;
//               if (Permet_affichage() > 5) cout << "\n (penalisation limite) ";
//              };
            if (Permet_affichage() > 6)  cout << " F_N(pur)= " << F_N << " ";
          break;
        }
       default: break; // déjà vu au dessus
     };
    F_N_max = (intens_force); // sauvegarde
    if (Permet_affichage() > 5)
      {cout << " noeud: " << noeud->Num_noeud();
       cout << "\n deltaX " << deltaX
            <<"\n Noe_atdt= " << Noe_atdt << " M_impact= " << M_impact
            <<"\n gap= " << gap << " N= " << N << " penalisation_normale= " << penalisation
            << " intensite force = " << intens_force << flush;
      };
//cout << "\n SM_charge_contact: penalisation= "<< penalisation;
    // --  ici on a un traitement différent suivant typ_cal ---
   // Dans le cas du contact 4 on ne s'occupe pas du type de calcul de pénalisation
   if (contactType != 4)
    { switch (typ_cal)
       {case 1: case 2: case 3: // cas le plus simple : pas de calcul particulier
         if (gap > 0) // non ce n'est pas bizarre c'est le cas où le noeud décolle
           // c'est aussi le cas où on a un type de calcul de la pénalisation = 4 (il y aura une très légère force de collage pour
           // gap > 0 mais < borne_regularisation
           // par contre on annulle effectivement le résidu
          { //cout << "\n *** bizarre la penetration devrait etre negative ?? , on annule les forces de contact "
            //     << "\n ElContact::SM_charge_contact(...";
            force_contact=F_N;  // on sauvegarde pour le traitement du décollement éventuel
            // au cas où on met à 0 les forces de réaction sur la facette
            int nb_n_facette = tabForce_cont.Taille();
            for (int i=1; i<= nb_n_facette; i++) tabForce_cont(i).Zero();
            if (Permet_affichage() > 6)
              { cout << "\n noeud: " << noeud->Num_noeud() << ": force collage non prise en compte " << force_contact
                     << " gap(positif)= " << gap << ", Normale= " << N;
                cout << "\n deltaX " << deltaX
                     <<"\n Noe_atdt= " << Noe_atdt << " M_impact= " << M_impact
                     << " penalisation_normale= " << penalisation
                     << " intensite force = " << intens_force ;
              };
            // on ne contribue pas aux réactions aux niveaux des noeuds car la valeur que l'on devrait ajoutée est nulle
            return residu;  // les résidu + raideur sont nul à ce niveau de la méthode
          };
         break;
        case 4: case 5:case 6:
         if (gap > Dabs(borne_regularisation))
           // on est sortie de la zone d'accostage donc on neutralise le contact
          { force_contact=F_N;  // on sauvegarde pour le traitement du décollement éventuel
            // au cas où on met à 0 les forces de réaction sur la facette
            int nb_n_facette = tabForce_cont.Taille();
            for (int i=1; i<= nb_n_facette; i++) tabForce_cont(i).Zero();
            if (Permet_affichage() >= 6)
                { cout << "\n noeud: " << noeud->Num_noeud()
                       << ": force collage non prise en compte " << force_contact
                       << " gap(positif et > borne regularisation)= " << gap << ", Normale= " << N;
                  cout << "\n deltaX " << deltaX
                       <<"\n Noe_atdt= " << Noe_atdt << " M_impact= " << M_impact
                       << " penalisation_normale= " << penalisation
                       << " intensite force = " << intens_force ;
                };
            // on ne contribue pas aux réactions aux niveaux des noeuds car la valeur que l'on devrait ajoutée est nulle
            return residu;  // les résidu + raideur sont nul à ce niveau de la méthode
          };
         break;
        case 7: case 8:
         // par rapport aux autres cas, on neutralise rien
         break;
        default :
          cout << "\n *** cas pas pris en compte !! \n  SM_charge_contact() " << endl;
          Sortie(1);
       };
    };
    // --  fin du traitement différent suivant typ_cal ---

/*
////////debug
//   if (ParaGlob::NiveauImpression() >= 6)
//// if (noeud->Num_noeud()==113)
//     {
////      cout << "\n debug : ElContact::SM_charge_contact(): noeud 113";
//      cout << "\n noeud: " << noeud->Num_noeud();
//      cout << "\n deltaX " << deltaX
//           <<"\n Noe_atdt= " << Noe_atdt << " M_impact= " << M_impact
//           <<"\n gap= " << gap << " N= " << N << " penalisation= " << penalisation << endl
//           ;
//     };
////// fin debug
//////debug
// if (noeud->Num_noeud()==113)
//  {
//	  cout << "\n debug : ElContact::SM_charge_contact(): noeud 113"
//	       << "\n force " << F_N << " force_limiter "<<force_limiter <<endl
//			 ;
//  };
//// fin debug

// --  debug
//cout << ",  F_N= " << (F_N * N) << " (ElContact::SM_charge_contact())" << endl;	  
// -- fin debug */
  
   // énergie élastique de pénalisation: énergie élastique = 1/2 k x^2 = 1/2 F x
   energie_penalisation = 0.5 * intens_force * gap; // la partie déplacement normal
   // on initialise la partie frottement avec au moins la partie pénalisation, ensuite
   // ce sera éventuellement abondé par le glissement ? ou la force de collage .... en devenir
   energie_frottement.ChangeEnergieElastique(energie_penalisation);
	  // -- maintenant on regarde s'il faut s'occuper du frottement
	  Coordonnee F_T(dima);
	  Element * elemf = elfront->PtEI(); // récup de l'élément fini
	  CompFrotAbstraite* loiFrot = ((ElemMeca*) elemf)->LoiDeFrottement();
   penalisation_tangentielle = 0.; // init
   bool force_T_limiter = false; // init
	  if (loiFrot != NULL)
	  	{ // --- cas de l'existance d'une loi de frottement
	     // calcul du déplacement tangentiel
      ElFrontiere & elfro_initiale = *(front_initiale->Eleme()); // pour commodite
      const Coordonnee * M_C0 = NULL; // init
      if (elfro_initiale.Type_geom_front() != POINT_G)
       {M_C0 = &(elfro_initiale.Metrique()->PointM_tdt(elfro_initiale.TabNoeud(),phi_theta_0));// ( stockage a tdt)
       }
      else
       {// Dans le cas d'une frontière point, le point d'intersection est le point frontière
        // et initialement ce sont ses coordonnées initiales
        M_C0 = &(tabNoeud(2)->Coord0());
       };
      Coordonnee M_0atdt = (Noe_atdt-(*M_C0)); // déplacement total à partir du point d'impact mis à jour
         // transportée via l'interpolation: c-a-d les phi_theta_0 qui restent constant
         // ici du au frottement, le point d'impact est mis à jour en fonction du glissement éventuel
      dep_tangentiel = M_0atdt- N * (M_0atdt*N); // on retire le deplacement normal éventuel
      // calcul de la force tangentielle en supposant tout d'abord un contact collant
      double penalisation_tangentielle;
      if (fct_nD_contact.fct_nD_penalisationTangentielle != NULL)
           {penalisation_tangentielle = Valeur_fct_nD(fct_nD_contact.fct_nD_penalisationTangentielle
                           ,ElContact::Fct_nD_contact::tqi_fct_nD_penalisationTangentielle
                           ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penalisationTangentielle
                           ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penalisationTangentielle);
           }
      else {penalisation_tangentielle = ParaGlob::param->ParaAlgoControleActifs().PenalisationTangentielleContact();}
     //      F_T =dep_tangentiel*(-ParaGlob::param->ParaAlgoControleActifs().PenalisationTangentielleContact());
      F_T =dep_tangentiel*(-penalisation_tangentielle);

	     // -- vérification en appelant  le comportement en frottement ----
	     //  tout d'abord on récupère le delta t pour le calcul de la vitesse
	     double deltat=ParaGlob::Variables_de_temps().IncreTempsCourant();
      double unSurDeltat=0;    
	     if (Abs(deltat) >= ConstMath::trespetit)
         {unSurDeltat = 1./deltat;}
      else
         // si l'incrément de temps est tres petit on remplace 1/deltat par un nombre tres grand
         { // non un pas de temps doit être positif !! or certaine fois il peut y avoir des pb
           if (unSurDeltat < 0)
             { cout << "\n le pas de temps est négatif !! "
                    << "\n ElContact::SM_charge_contact(...";
             };
           unSurDeltat = ConstMath::tresgrand;
         }; 
      // calcul des vitesses
      Coordonnee vit_T;
      if (Mt.Dimension() != 0)
        {vit_T = (Mtdt-Mt)* unSurDeltat;}
      else
        // sinon Mt n'est pas encore définit donc on ne peut pas calculer le déplacement
        // il est donc laissé à 0.
        {vit_T = (Mtdt)* unSurDeltat;}
	     // appel de la loi de comportement: calcul de la force de frottement et des énergies échangées
	     Coordonnee nevez_force_frot;EnergieMeca energie;
      bool glisse = loiFrot->Cal_explicit_contact_tdt(vit_T,N,F_N,F_T,energie,deltat,nevez_force_frot);
          
     //     if (vit_T * nevez_force_frot > 0)
     //       cout << "\n bizarre puissance de frottement positive !";
          
          
      if (glisse)  // on remplace la force de frottement si ça glisse sinon on conserve F_T
          F_T = nevez_force_frot;
	     // --- calcul des énergies échangées 
	     energie_frottement += energie;
      if (Permet_affichage() > 6)
       { cout << "\n deltaX_T: " << dep_tangentiel
              <<"\n vit_T= " << vit_T << " glisse= " << glisse
              << " penalisation_tangentielle= " << penalisation_tangentielle;
       };
	  	}
   else if ( cas_collant)
    { // --- cas d'un contact collant sans loi de frottement explicite
      // calcul du déplacement tangentiel par rapport au premier point de contact
      ElFrontiere & elfro_initiale = *(front_initiale->Eleme()); // pour commodite
      const Coordonnee * M_C0 = NULL; // init
      if (elfro_initiale.Type_geom_front() != POINT_G)
       {M_C0 = &(elfro_initiale.Metrique()->PointM_tdt(elfro_initiale.TabNoeud(),phi_theta_0));// ( stockage a tdt)
       }
      else
       {// Dans le cas d'une frontière point, le point d'intersection est le point frontière
        // et transporté, ce sont ses coordonnées actuelles
        M_C0 = &(tabNoeud(2)->Coord2());
       };
                    
      Coordonnee M_0atdt = (Noe_atdt-(*M_C0)); // déplacement total à partir du point d'impact initial
         // transportée via l'interpolation: c-a-d les phi_theta_0 qui restent constant
      dep_tangentiel = M_0atdt- N * (M_0atdt*N); // on retire le deplacement normal éventuel
      double dep_T = dep_tangentiel.Norme();
      // calcul de la pénalisation
      double d_beta_dep_T=0.; // init de la dérivée du facteur de pénalisation / au déplacement
        // pour l'instant ne sert pas
      penalisation_tangentielle = CalFactPenalTangentiel(dep_T,d_beta_dep_T);
      dep_T_tdt = dep_T; // sauvegarde

      // calcul de la force tangentielle en supposant un contact collant
      F_T = -dep_tangentiel * penalisation_tangentielle;
      // limitation éventuelle de la force
      double max_force_T_noeud;
      if (fct_nD_contact.fct_nD_force_tangentielle_noeud_maxi != NULL)
           {max_force_T_noeud = Valeur_fct_nD(fct_nD_contact.fct_nD_force_tangentielle_noeud_maxi
                                   ,ElContact::Fct_nD_contact::tqi_fct_nD_force_tangentielle_noeud_maxi
                                   ,ElContact::Fct_nD_contact::tqi_const_fct_nD_force_tangentielle_noeud_maxi
                                   ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_force_tangentielle_noeud_maxi);
           }
      else {max_force_T_noeud = ParaGlob::param->ParaAlgoControleActifs().Force_tangentielle_noeud_maxi();}
      double n_F_T = Dabs(penalisation_tangentielle * dep_T);
      if (n_F_T > max_force_T_noeud)
        {F_T *= max_force_T_noeud/n_F_T;
         force_T_limiter = true;
        };
      if (Permet_affichage() > 6)
       { cout << "\n deltaX_T: " << dep_tangentiel
              << " penalisation_tangentielle= " << penalisation_tangentielle;
       };
      // --- calcul des énergies échangées
      // ici il s'agit d'une énergie élastique, que l'on ajoute à la pénalisation normale
      // énergie élastique de pénalisation: énergie élastique = 1/2 k x^2 = 1/2 F x
      energie_penalisation += Dabs(0.5 * F_T * dep_tangentiel); // pour que le résultat soit > 0
      energie_frottement.ChangeEnergieElastique(energie_penalisation);
    };	  // --- calcul des puissances virtuelles
    
   F_T_max = F_T.Norme(); // sauvegarde

	  force_contact = F_T + F_N;  
	  if (Permet_affichage() >= 5)
			  {	cout << "\n noeud: " << noeud->Num_noeud() << ": force contact " << force_contact ;
       cout << " ||F_N||= "<< F_N.Norme() << " ||F_T||= "<< F_T_max;
				   if (force_limiter) cout << " force normale en limite " ;
       if (force_T_limiter) cout << " force tangentielle en limite " ;
			  };
   if (Permet_affichage() > 6)
     cout << "\n ==> force contact imposee au residu (noeud:" << noeud->Num_noeud()
          << " maillage:"<< noeud->Num_Mail() << ") " << force_contact;
/* ////debug
// if (noeud->Num_noeud()==207)
//  {
//	  cout << "\n debug : ElContact::SM_charge_contact(): noeud 113";
//   cout << "\n noeud: " << noeud->Num_noeud() << ": force contact " << force_contact ;
//   if (force_limiter) cout << " force en limite " ;
//  };
//// fin debug
// -- debug
//double inten = 	force_contact.Norme();
//if (inten > 0) 
//  cout << "\n force_contact = " << force_contact << " (ElContact::SM_charge_contact())" << endl;   
// -- fin debug */
	  
	  int posi = Id_nom_ddl("R_X1") -1; // préparation pour les réactions
	  Ddl_enum_etendu& ddl_reaction_normale=Ddl_enum_etendu::Tab_FN_FT()(1);
	  Ddl_enum_etendu&  ddl_reaction_tangentielle=Ddl_enum_etendu::Tab_FN_FT()(2);
   // une petite manip pour le cas axisymétrique: dans ce cas les ddl à considérer pour le résidu et la raideur
   // doivent être uniquement x et y donc 2 ddl au lieu de 3. (rien ne dépend de z)
   int nb_ddl_en_var =  dima; // init
   bool axi = false;
   if (ParaGlob::AxiSymetrie())
      { nb_ddl_en_var -= 1; axi = true;};
	  //  a- cas du noeud projectile
	  int ism=1;
	  if ((cas_solide == 0 ) || (cas_solide == 1))
	    // cas où le noeud est libre
		   {for (int ix=1;ix<=nb_ddl_en_var;ix++,ism++)   // pour le noeud projectile la vitesse virtuelle associé est
       { SM_res(ism)= force_contact(ix);      // directement la vitesse du noeud
		       // on abonde au niveau de la réaction au noeud
		       noeud->Ajout_val_tdt(Enum_ddl(ix+posi),force_contact(ix)); 
		     };
		   }
	  else 
	    // on s'occupe quand même des réactions, ce qui nous permettra de les récupérer même sur un solide
		   {for (int ix=1;ix<=dima;ix++)   // pour le noeud projectile la vitesse virtuelle associé est
	      // on abonde au niveau de la réaction au noeud
		     noeud->Ajout_val_tdt(Enum_ddl(ix+posi),force_contact(ix)); 
		   };
   // dans le cas de l'axi il faut décaler ism pour la suite, ce qui correspond au z
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
// if (axi) ism++; // qui n'est pas pris en compte	  // on abonde au niveau de la réaction normale et tangentielle pour le noeud esclave
	  noeud->ModifDdl_etendu(ddl_reaction_normale).Valeur() += -intens_force;
	  double intensite_tangentielle = F_T.Norme();
	  noeud->ModifDdl_etendu(ddl_reaction_tangentielle).Valeur() += intensite_tangentielle;
			  	  
	  //  b- cas de la surface cible
	  if ((cas_solide == 0 ) || (cas_solide == 2))
	    // cas où la facette est libre
	   {for (int ir=1;ir<=tab_taille-1;ir++) 
	    { Noeud* noe = tabNoeud(ir+1); // pour simplifier
       tabForce_cont(ir) = -force_contact*phii(ir);
			    for (int ix=1;ix<=nb_ddl_en_var;ix++,ism++)
         { double force_imp = -force_contact(ix)*phii(ir);
				       SM_res(ism)= force_imp;
					      // on abonde au niveau de la réaction au noeud
					      noe->Ajout_val_tdt(Enum_ddl(ix+posi),force_imp); 
				     };
       // dans le cas de l'axi il faut décaler ism pour la suite, ce qui correspond au z
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
//      if (axi) ism++; // qui n'est pas pris en compte
			    // on abonde au niveau de la réaction normale et tangentielle pour les noeuds maîtres
			    noe->ModifDdl_etendu(ddl_reaction_normale).Valeur() += -intens_force*phii(ir);
			    noe->ModifDdl_etendu(ddl_reaction_tangentielle).Valeur() += intensite_tangentielle*phii(ir);
		   };		 
		  }
	  else
	   // on s'occupe quand même des réactions, ce qui nous permettra de les récupérer même sur un solide
	   {for (int ir=1;ir<=tab_taille-1;ir++) 
	     { Noeud* noe = tabNoeud(ir+1); // pour simplifier
        tabForce_cont(ir) = -force_contact*phii(ir);
			     for (int ix=1;ix<=dima;ix++)
	        { double force_imp = -force_contact(ix)*phii(ir);
				       // on abonde au niveau de la réaction au noeud
				       noe->Ajout_val_tdt(Enum_ddl(ix+posi),force_imp); 
			      };
			     // on abonde au niveau de la réaction normale et tangentielle pour les noeuds maîtres
			     noe->ModifDdl_etendu(ddl_reaction_normale).Valeur() += -intens_force*phii(ir);
			     noe->ModifDdl_etendu(ddl_reaction_tangentielle).Valeur() += intensite_tangentielle*phii(ir);
		    };
		  };
   if (Permet_affichage() >= 7)
     { cout << "\n =-=-=- bilan sur le calcul de l'element de contact:  =-=-=-  ";
       this->Affiche();
     };

/*	  // retour
////debug
//	  cout << "\n debug : ElContact::SM_charge_contact()";
//noeud->Affiche();
//for (int ir=1;ir<=tab_taille-1;ir++) 
//   tabNoeud(ir+1)->Affiche();
//
//   cout << "\n residu " << *residu <<endl;
// fin debug */
	  return residu;
	};
	
	// -> implicite,
Element::ResRaid ElContact::SM_K_charge_contact()
	{ if (Permet_affichage() > 5)
     {cout << "\n --  SM_K_charge_contact: ";this->Affiche(1);};
   // le contact 4 passe en 41 ou 42 quand on dépasse un nombre donné d'itérations par exemple
   int contactType = ElContact::Recup_et_mise_a_jour_type_contact();
   // préparation du retour
	  Element::ResRaid el;el.res=NULL;el.raid=NULL;
	  energie_penalisation = 0.;
	  energie_frottement.Inita(0.);
	  Mise_a_jour_ddlelement_cas_solide_assemblage(); // mise à jour de ddl element et de cas_solide
	  // dans le cas du modèle de contact cinématique, on n'a pas de contribution (pour l'instant) au second membre et à la raideur
	  // cela pourrait changer si l'on considérait le frottement, mais pour l'instant ce n'est pas le cas
	  // on retourne un pointeur null
	  if (contactType == 1)
	      return el;
	  // def de quelques dimensions pour simplifier 
	  int dima = ParaGlob::Dimension();
	  int tab_taille = tabNoeud.Taille();
	  int nbddl = ddlElement_assemblage->NbDdl();
   // récup  d'une place pour le résidu local et mise à jour de list_SM éventuellement
   RecupPlaceResidu(nbddl);
   Vecteur& SM_res = *residu; // pour simplifier
   // idem pour la raideur
   RecupPlaceRaideur(nbddl); 
	  // ---- initialisation de SM et de la raideur
	  SM_res.Zero();raideur->Initialise(0.0);
   el.res = residu; 
   el.raid = raideur;       
   // la suite de la méthode ne fonctionne que pour les type 2 et 4 de contact,
   //  dans le cas contraire on revient directement
   if (!((contactType == 2) || (contactType == 4) || (contactType == 41) || (contactType == 42)))
      return el;
			
	  // ---- sinon .... cas du contact par pénalisation -----
   //  dans le cas 2: on calcul un facteur de pénalisation
   //  dans le cas 4: on déduit le facteur de pénalisation à partir des forces internes
   //                 et de la condition cinématique de contact
   // on considère ici que le point de contact est déterminé et on s'occupe de déterminer les
   // efforts éventuels dus au contact ainsi que les énergies échangées sur le pas de temps

	  // a priori on suppose que le contact est collant --> calcul des efforts de contact
	  Coordonnee  Noe_atdt = noeud->Coord2(); // récup de la position actuelle du noeud projectile
	  // recup du dernier des différentes informations
	  Coordonnee M_impact; // le point d'impact, la normale
	  Vecteur phii;

   // d_T_pt: représente la variation du vecteur normale
	  Tableau <Coordonnee >* d_T_pt = (RecupInfo(N,M_impact,phii,true));
	  // en fait due aux différents algos de recherche de contact, normalement la position de contact est sauvegardée 
	  // dans l'élément, donc on utilise cette info, par contre on utilise RecupInfo pour calculer les autres infos : normale et phi
	  // M_impact et Mtdt sont différents uniquement dans le cas d'un restart 
   // Mtdt est mis à jour pendant la recherche du contact
	  M_impact = Mtdt;
   // changement  éventuel par une moyenne glissante des positions successive du noeud esclave
   // Noe_atdt est modifié éventuellement 
   ChangeEnMoyGlissante(Noe_atdt);
	  
	  // calcul de la pénétration normale
   Coordonnee deltaX(dima);
   switch (contactType)
     { case 2: // cas d'une pénalisation classique
       case 41 : case 42: // idem après basculement du 4
        { deltaX = Noe_atdt - M_impact;
          break;
        }
       case 4: // cas ou le noeud à été projeté sur la surface
        // normalement Noe_atdt est identique à M_impact
        { deltaX =  M_noeud_tdt_avant_projection - Noe_atdt;
          break;
        }
       default: cout << "\n *** erreur, le cas contactType = " << contactType
                     << " ne devrait pas exister ici !! "<< endl;
                Sortie(1);
       break;
     };


	  double gap= N * deltaX;  // ce qui constitue la fonction g(ddl) à annuler voir à minimiser
   if ((Permet_affichage() > 7) && (contactType==4))
     {double a = (deltaX - gap*N).Norme(); // on calcule pour vérifier
      cout << " diff_projX= "<< a;  // l'erreur de projection normale
     };
////debug
// if (noeud->Num_noeud()==56)
//  {
//	  cout << "\n debug : ElContact::SM_K_charge_contact(): noeud 409"
//	       << "\n deltaX " << deltaX 
//			 <<"\n Noe_atdt= " << Noe_atdt << " M_impact= " << M_impact
//			 <<"\n gap= " << gap << " N= " << N << endl
//			 ;
//  };
//// fin debug
	  // récupération du facteur de pénalisation adaptée au cas de calcul et à la géométrie
   double d_beta_gap=0.; // init de la dérivée du facteur de pénalisation / au gap
   // calcul du facteur de pénalisation si le contact est de type 2
   if (contactType == 2)
      {penalisation = CalFactPenal(gap,d_beta_gap,contactType);}
   // sinon dans le cas 41 ou 42 on utilise la valeur qui est en cours
   // il n'y a pas de différence entre 41 et 42 en implicite
   gap_tdt = gap; // sauvegarde
	  // limitation au niveau du gap à 2 fois la pénétration maxi
//	  double max_pene = ParaGlob::param->ParaAlgoControleActifs().Penetration_contact_maxi();
   double borne_regularisation;
   if (fct_nD_contact.fct_nD_penetration_borne_regularisation != NULL)
        {borne_regularisation = Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_borne_regularisation
                                              ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_borne_regularisation
                                              ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_borne_regularisation
                                              ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_borne_regularisation);
        }
   else {borne_regularisation = ParaGlob::param->ParaAlgoControleActifs().Penetration_borne_regularisation();}
	  int typ_cal= ParaGlob::param->ParaAlgoControleActifs().TypeCalculPenalisationPenetration();

//debug
// if (noeud->Num_noeud()==7)
//  {double max_pene = ParaGlob::param->ParaAlgoControleActifs().Penetration_contact_maxi();
//   if ((gap_t > max_pene) && (gap_tdt > max_pene))
//    {cout << "\n debug : ElContact::SM_K_charge_contact(): noeud 7"
//          << "\n gap_t " << gap_t << " max_pene= " << max_pene
//          <<"\n gap_tdt= " << gap_tdt << " M_impact= " << M_impact
//          <<"\n gap= " << gap << " N= " << N << endl;
//    };
//  };
// fin debug
// pour l'instant je supprime le cas de la limitation du déplacement, car je pense qu'il faudrait y adjoindre les forces
// de réaction associées sinon l'équilibre est faux !! donc on commente pour l'instant
/*	  // méthode de limitation de la pénétration: opérationnelle si max_pene > 0
	  // si max_pene < 0 on intervient sur l'incrément de temps !!
	  if (max_pene > 0)
	   {if ((typ_cal == 5) && (gap < (- 2 * max_pene)))
	     // on modifie la position du noeud pour tenir compte de la pénalisation
//		  { Coordonnee nevez_posi(M_impact - 2 * max_pene * N);
		  { Coordonnee nevez_posi(M_impact + (2. * max_pene / deltaX.Norme()) * deltaX);
		    noeud->Change_coord2(nevez_posi);
		   };
		 };*/ 
		
	  // --- calcul de la force normale	
	  // limitation éventuelle de l'intensité de la force de contact
   double intens_force = 0. ; // init
   bool force_limiter=false;
   Coordonnee F_N(dima); // init
  
   double max_force_noeud;
   if (fct_nD_contact.fct_nD_force_contact_noeud_maxi != NULL)
        {max_force_noeud = Valeur_fct_nD(fct_nD_contact.fct_nD_force_contact_noeud_maxi
                             ,ElContact::Fct_nD_contact::tqi_fct_nD_force_contact_noeud_maxi
                             ,ElContact::Fct_nD_contact::tqi_const_fct_nD_force_contact_noeud_maxi
                             ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_force_contact_noeud_maxi);
        }
   else {max_force_noeud = ParaGlob::param->ParaAlgoControleActifs().Force_contact_noeud_maxi();}
   switch (contactType)
     { case 2: // cas d'une pénalisation classique
       case 41: case 42: // idem après basculement du cas 4
        { intens_force = gap * penalisation; // négatif quand on a pénétration
          if (Dabs(intens_force) > max_force_noeud)
            {intens_force = max_force_noeud * Signe(intens_force);
             force_limiter = true;
            };
          // on recalcul la pénalisation associée
          if ((Dabs(gap) > ConstMath::trespetit) && force_limiter)
            {penalisation = intens_force / gap;
             if (Permet_affichage() > 5)
               {cout << " ** limitation penalisation= " << penalisation
                     << " intensite force = " << intens_force << flush;};
            };
          // F_N c'est la force qui appuie sur le noeud esclave d'où le - car intens_force est négatif
          F_N = - N * intens_force; //(-gap * penalisation);
          break;
        }
       case 4: // cas ou le noeud à été projeté sur la surface
        // on récupère la force de contact via la puissance interne des éléments contenants
        // le noeud exclave (le calcul des forces internes doit déjà avoir été effectué)
        {  TypeQuelconque_enum_etendu enu(FORCE_GENE_INT);
            // récupération du conteneur pour lecture uniquement
            const TypeQuelconque& typquel = noeud->Grandeur_quelconque(enu);
            const  Grandeur_coordonnee& cofo = *((Grandeur_coordonnee*) typquel.Const_Grandeur_pointee());
            // signe - car le contact doit s'opposer exactement à l'effort interne
            F_N = - cofo.ConteneurCoordonnee_const(); // récup de - la réaction
            if (Permet_affichage() > 6)  cout << " F_N(force_interne)= " << F_N << " ";
            // en fait on ne va garder que la partie normale à la facette, ce qui laissera le glissement possible
            intens_force = - F_N * N; // 1) projection de la réaction
            F_N = - N * intens_force; // 2) recalcul uniquement de la partie normale
            // on va également tenter un calcul d'une pénalisation équivalente
            double max_pene;
            if (fct_nD_contact.fct_nD_penetration_contact_maxi != NULL)
                 {max_pene = Dabs(Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_contact_maxi
                                   ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_contact_maxi
                                   ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_contact_maxi
                                   ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_contact_maxi));
                  }
            else {max_pene = Dabs(ParaGlob::param->ParaAlgoControleActifs().Penetration_contact_maxi());}
            // l'idée est de viser la pénétration max_pene, car en pénalisation, le fait
            // de conserver la pénétration et la force constatées, ne va pas diminuer la pénétration
            // or on aimerait une pénétration petite !! a priori celle = max_pene
//            // si cette grandeur est plus petite que gap
//            // si par contre max_pene est grand, (ou bien plus grand que gap) on utilise gap
//            penalisation = Dabs(intens_force) / (MiN(max_pene, Dabs(gap)));
//            if (Dabs(gap) > max_pene)
//              {penalisation = Dabs(intens_force) / max_pene; }
//            else
//              {penalisation = Dabs(intens_force) / Dabs(gap);}

            // a priori c'est cette méthode qui est la plus performante
            penalisation = Dabs(intens_force) / max_pene;
         
//            if (Dabs(gap) > max_pene)
//              {penalisation = Dabs(intens_force) / Dabs(gap);} //(Dabs(gap) + ConstMath::unpeupetit)) ;}
//            else // sinon la pénétration est vraiment petite, on va cibler la valeur de max_pene
//              {penalisation = Dabs(intens_force) / max_pene;
//               if (Permet_affichage() > 5) cout << "\n (penalisation limite) ";
//              };
            if (Permet_affichage() > 6)  cout << " F_N(pur)= " << F_N << " ";
          break;
        }
       default: break; // déjà vu au dessus
     };
    F_N_max = (intens_force); // sauvegarde

   if (Permet_affichage() > 5)
     {cout << " noeud: " << noeud->Num_noeud();
      cout << "\n deltaX " << deltaX
           <<"\n Noe_atdt= " << Noe_atdt << " M_impact= " << M_impact
           <<"\n gap= " << gap << " N= " << N << " penalisation_normale= " << penalisation
           << " intensite force = " << intens_force << flush;
     };
//cout << "\n SM_K_charge_contact: penalisation= "<< penalisation << " gap= " << gap << " FN= " << F_N ;


//debug
// if ((F_N_max > 5000000) || (noeud->Num_noeud()==2))
// if (penalisation == 0.)
// if (noeud->Num_noeud()==2)
//  {
//   cout << "\n debug : ElContact::SM_K_charge_contact(): noeud " << noeud->Num_noeud()
//        << " zone: " << num_zone_contact
//        << "\n force " << F_N << " force_limiter "<<force_limiter <<endl
//        <<"\n Noe_atdt= " << Noe_atdt << " M_impact= " << M_impact
//        <<"\n gap= " << gap << " N= " << N
//        << " penalisation= " << penalisation << endl
//    ;
////  CalFactPenal(gap,d_beta_gap,contactType);
//  };
// fin debug

	  // ici on a un traitement différent suivant les types de contact
   // Dans le cas du contact 4 on ne s'occupe pas du type de calcul de pénalisation
   if (contactType != 4)
    {switch (typ_cal)
     {case 1: case 2: case 3: // cas le plus simple : pas de calcul particulier
	      if (gap > 0) // non ce n'est pas bizarre c'est le cas où le noeud décolle
	        // c'est aussi le cas où on a un type de calcul de la pénalisation = 4 (il y aura une très légère force de collage pour
		       // gap > 0 mais < borne_regularisation
	        // par contre on annulle effectivement le résidu 
         { //cout << "\n *** bizarre la penetration devrait etre negative ?? , on annule les forces de contact "
           //     << "\n ElContact::SM_charge_contact(...";
           force_contact=F_N;  // on sauvegarde pour le traitement du décollement éventuel
           // au cas où on met à 0 les forces de réaction sur la facette
           int nb_n_facette = tabForce_cont.Taille();
           for (int i=1; i<= nb_n_facette; i++) tabForce_cont(i).Zero();
           if (Permet_affichage() > 6)
             {	cout << "\n noeud: " << noeud->Num_noeud()
                    << ": force collage non prise en compte " << force_contact
                    << " gap(positif)= " << gap << ", Normale= " << N;
               cout << "\n deltaX " << deltaX
                    <<"\n Noe_atdt= " << Noe_atdt << " M_impact= " << M_impact
                    << " penalisation_normale= " << penalisation
                    << " intensite force = " << intens_force ;
             };
           // on ne contribue pas aux réactions aux niveaux des noeuds car la valeur que l'on devrait ajoutée est nulle	
           return el;  // les résidu + raideur sont nul à ce niveau de la méthode
         };
			    break;
		    case 4: case 5: case 6:
		     if (gap > Dabs(borne_regularisation)) 
			      // on est sortie de la zone d'accostage donc on neutralise le contact
         { force_contact=F_N;  // on sauvegarde pour le traitement du décollement éventuel
           // au cas où on met à 0 les forces de réaction sur la facette
           int nb_n_facette = tabForce_cont.Taille();
           for (int i=1; i<= nb_n_facette; i++) tabForce_cont(i).Zero();
           if (Permet_affichage() >= 6)
               {	cout << "\n noeud: " << noeud->Num_noeud()
                      << ": force collage non prise en compte " << force_contact
                      << " gap(positif et > borne regularisation)= " << gap << ", Normale= " << N;
                 cout << "\n deltaX " << deltaX
                      <<"\n Noe_atdt= " << Noe_atdt << " M_impact= " << M_impact
                      << " penalisation_normale= " << penalisation
                      << " intensite force = " << intens_force ;
               };
           // on ne contribue pas aux réactions aux niveaux des noeuds
           //  car la valeur que l'on devrait ajoutée est nulle
           return el;  // les résidu + raideur sont nul à ce niveau de la méthode
         };
			    break;
		    case 7: case 8:
       // par rapport aux autres cas, on neutralise rien
			    break;
		    default :
		      cout << "\n *** cas pas pris en compte !! \n  SM_K_charge_contact() " << endl;
			     Sortie(1);
		   };
    };
  
  
   // énergie élastique de pénalisation: énergie élastique = 1/2 k x^2 = 1/2 F x
   energie_penalisation = 0.5 * F_N_max * gap; // la partie déplacement normal
   // on initialise la partie frottement avec au moins la partie pénalisation, ensuite
   // ce sera éventuellement abondé par le glissement ? ou la force de collage .... en devenir
   energie_frottement.ChangeEnergieElastique(energie_penalisation);
	  // -- maintenant on regarde s'il faut s'occuper du frottement
	  Coordonnee F_T(dima);
   Element * elemf = elfront->PtEI(); // récup de l'élément fini
	  CompFrotAbstraite* loiFrot = ((ElemMeca*) elemf)->LoiDeFrottement();
   penalisation_tangentielle = 0.; // init
   bool force_T_limiter = false; // init
   Coordonnee M_0atdt; // init,
	  if (loiFrot != NULL)
	  	{ // --- cas de l'existance d'une loi de frottement
//il faut revoir la notion de déplacement incrémental ou total en fonction de la théorie (voir peut-être théorie écrite )
      // calcul du déplacement tangentiel par rapport au premier point de contact
      // mis à jour en fonction du glissement éventuelle
      ElFrontiere & elfro_initiale = *(front_initiale->Eleme()); // pour commodite
      const Coordonnee * M_C0 = NULL; // init
      if (elfro_initiale.Type_geom_front() != POINT_G)
       {M_C0 = &(elfro_initiale.Metrique()->PointM_tdt(elfro_initiale.TabNoeud(),phi_theta_0));// ( stockage a tdt)
       }
      else
       {// Dans le cas d'une frontière point, le point d'intersection est le point frontière
        // et initialement ce sont ses coordonnées initiales
        M_C0 = &(tabNoeud(2)->Coord0());
       };
//      const Coordonnee & M_C0 = elfro.Metrique()->PointM_tdt // ( stockage a tdt)
//                          (elfro.TabNoeud(),phi_theta_0);
      ////---- debug ----
      //{const Coordonnee & M_a0 = elfro.Metrique()->PointM_0 // ( stockage a tdt)
      //                    (elfro.TabNoeud(),phi_theta_0);
      // cout << "\n debug smK contact: M_a0: ";M_a0.Affiche_1(cout);
      // cout << " a tdt: ";M_C0.Affiche_1(cout);
      //}
      ////---- fin debug ---
      M_0atdt = (Noe_atdt-(*M_C0)); // déplacement total à partir du point d'impact mis à jour
         // transportée via l'interpolation: c-a-d les phi_theta_0 qui restent constant
         // ici du au frottement, le point d'impact est mis à jour en fonction du glissement éventuel
      dep_tangentiel = M_0atdt- N * (M_0atdt*N); // on retire le deplacement normal éventuel
      ////---- debug ----
      //{ cout << "\n dep_tangentiel: ";dep_tangentiel.Affiche_1(cout);
      //  cout << "\n Noe_atdt: "; Noe_atdt.Affiche_1(cout);
      //}
      ////---- fin debug ---
      double dep_T = dep_tangentiel.Norme();
      dep_T_tdt = dep_T; // sauvegarde

    
//	     // calcul du déplacement tangentiel: là il s'agit du déplacement relatif entre l'ancien point projeté et le nouveau
//
//      Coordonnee M_tatdt(dima);
//      if (Mt.Dimension() != 0)
//        {M_tatdt = (Mtdt-Mt);} // déplacement total
//      ; // sinon Mt n'est pas encore définit donc on ne peut pas calculer le déplacement
//        // il est donc laissé à 0.
//	     dep_tangentiel = M_tatdt- N * (M_tatdt*N); // on retire le deplacement normal éventuel

	     // calcul de la force tangentielle en supposant tout d'abord un contact collant
      double penalisation_tangentielle;
      if (fct_nD_contact.fct_nD_penalisationTangentielle != NULL)
           {penalisation_tangentielle = Valeur_fct_nD(fct_nD_contact.fct_nD_penalisationTangentielle
                           ,ElContact::Fct_nD_contact::tqi_fct_nD_penalisationTangentielle
                           ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penalisationTangentielle
                           ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penalisationTangentielle);
           }
      else {penalisation_tangentielle = ParaGlob::param->ParaAlgoControleActifs().PenalisationTangentielleContact();}
//	     F_T =dep_tangentiel*(-ParaGlob::param->ParaAlgoControleActifs().PenalisationTangentielleContact());
      F_T =dep_tangentiel*(-penalisation_tangentielle);
	     // -- verification en appelant  le comportement en frottement ----
	     //  tout d'abord on récupère le delta t pour le calcul de la vitesse
	     double deltat=ParaGlob::Variables_de_temps().IncreTempsCourant();
      double unSurDeltat=0;    
	     if (Abs(deltat) >= ConstMath::trespetit)
        {unSurDeltat = 1./deltat;}
      else
        // si l'incrément de temps est tres petit on remplace 1/deltat par un nombre tres grand
        { // non un pas de temps doit être positif !! or certaine fois il peut y avoir des pb
          if (unSurDeltat < 0)
             { cout << "\n le pas de temps est négatif !! "
                    << "\n ElContact::SM_charge_contact(...";
             };
          unSurDeltat = ConstMath::tresgrand;
        }; 
	     // calcul des vitesses
	     Coordonnee vit_T;
      if (Mt.Dimension() != 0)
        {vit_T = (Mtdt-Mt)* unSurDeltat;}
      else
        // sinon Mt n'est pas encore définit donc on ne peut pas calculer le déplacement
        // il est donc laissé à 0.
        {vit_T = (Mtdt)* unSurDeltat;}

	     // appel de la loi de comportement: calcul de la force de frottement et des énergies échangées
	     Coordonnee nevez_force_frot;EnergieMeca energie;
      bool glisse = loiFrot->Cal_explicit_contact_tdt(vit_T,N,F_N,F_T,energie,deltat,nevez_force_frot);
      
      if (Permet_affichage() >= 6)
       { cout << "\n deltaX_T: " << dep_tangentiel
              <<"\n vit_T= " << vit_T << " glisse= " << glisse
              << " penalisation_tangentielle= " << penalisation_tangentielle;
       };
// *** à faire
//  mise à jour du point d'impact en fct du glissement , ou d'un point de référence qui tient compte
//  du glissement


     //     if (vit_T * nevez_force_frot > 0)
     //       cout << "\n bizarre puissance de frottement positive !";
          
          
      if (glisse)  // on remplace la force de frottement si ça glisse sinon on conserve F_T
             F_T = nevez_force_frot;
             
	     // --- calcul des énergies échangées 
	     energie_frottement += energie;
    }
   else if ( cas_collant)
    { // --- cas d'un contact collant sans loi de frottement explicite
      // calcul du déplacement tangentiel par rapport au premier point de contact
      ElFrontiere & elfro_initiale = *(front_initiale->Eleme()); // pour commodite
      const Coordonnee * M_C0 = NULL; // init
      if (elfro_initiale.Type_geom_front() != POINT_G)
       {M_C0 = &(elfro_initiale.Metrique()->PointM_tdt(elfro_initiale.TabNoeud(),phi_theta_0));// ( stockage a tdt)
       }
      else
       {// Dans le cas d'une frontière point, le point d'intersection est le point frontière
        // et transporté, ce sont ses coordonnées actuelles
        M_C0 = &(tabNoeud(2)->Coord2());
       };
                    
////---- debug ----
//{const Coordonnee & M_a0 = elfro.Metrique()->PointM_0 // ( stockage a tdt)
//                    (elfro.TabNoeud(),phi_theta_0);
// cout << "\n debug smK contact: M_a0: ";M_a0.Affiche_1(cout);
// cout << " a tdt: ";M_C0.Affiche_1(cout);
//}
////---- debug ---
      M_0atdt = (Noe_atdt-(*M_C0)); // déplacement total à partir du point d'impact initial
         // transportée via l'interpolation: c-a-d les phi_theta_0 qui restent constant
      dep_tangentiel = M_0atdt- N * (M_0atdt*N); // on retire le deplacement normal éventuel
      
////---- debug ----
//{ dep_tangentiel = M_impact - (*M_C0);
//}
////---- debug ---

////---- debug ----
//{ cout << "\n dep_tangentiel: ";dep_tangentiel.Affiche_1(cout);
//  cout << "\n Noe_atdt: "; Noe_atdt.Affiche_1(cout);
//}
////---- debug ---
      double dep_T = dep_tangentiel.Norme();
      // calcul de la pénalisation
      double d_beta_dep_T=0.; // init de la dérivée du facteur de pénalisation / au déplacement
        // pour l'instant ne sert pas
      penalisation_tangentielle = CalFactPenalTangentiel(dep_T,d_beta_dep_T);
      dep_T_tdt = dep_T; // sauvegarde
      // calcul de la force tangentielle en supposant un contact collant
      F_T = -dep_tangentiel * penalisation_tangentielle;
      // limitation éventuelle de la force
      double max_force_T_noeud;
      if (fct_nD_contact.fct_nD_force_tangentielle_noeud_maxi != NULL)
           {max_force_T_noeud = Dabs(Valeur_fct_nD(fct_nD_contact.fct_nD_force_tangentielle_noeud_maxi
                                       ,ElContact::Fct_nD_contact::tqi_fct_nD_force_tangentielle_noeud_maxi
                                       ,ElContact::Fct_nD_contact::tqi_const_fct_nD_force_tangentielle_noeud_maxi
                                       ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_force_tangentielle_noeud_maxi));
           }
      else {max_force_T_noeud = Dabs(ParaGlob::param->ParaAlgoControleActifs().Force_tangentielle_noeud_maxi());}
      double n_F_T = Dabs(penalisation_tangentielle * dep_T);
////---- debug ----
//{ cout << "\n  n_F_T= " << n_F_T ;
// cout << " F_T: ";F_T.Affiche_1(cout);
//}
////---- debug ---
      if (Permet_affichage() >= 6)
       { cout << "\n deltaX_T: " << dep_tangentiel
              << " penalisation_tangentielle= " << penalisation_tangentielle;
       };
      if ((n_F_T > max_force_T_noeud) && (max_force_T_noeud > ConstMath::petit))
        {F_T *= max_force_T_noeud/n_F_T;
         force_T_limiter = true;
         // on recalcul la pénalisation associée
         if (Dabs(dep_T) > ConstMath::trespetit)
           {penalisation_tangentielle = max_force_T_noeud / dep_T;}
         if (Permet_affichage() >= 6)
           {cout << " ** limitation penalisation tangentielle= " << penalisation_tangentielle
                 << " intensite force = " << max_force_T_noeud << flush;};
        };
////---- debug ----
//{ cout << "\n  F_T_max= " << n_F_T ;
// cout << " F_T: ";F_T.Affiche_1(cout);
//}
////---- debug ---
      // --- calcul des énergies échangées
      // ici il s'agit d'une énergie élastique, que l'on ajoute à la pénalisation normale
      // énergie élastique de pénalisation: énergie élastique = 1/2 k x^2 = 1/2 F x
      energie_penalisation -= 0.5 * F_T * dep_tangentiel; // - pour que le résultat soit > 0
      energie_frottement.ChangeEnergieElastique(energie_penalisation);
    };
   F_T_max = F_T.Norme(); // sauvegarde
	  // --- calcul des puissances virtuelles
	  force_contact = F_T + F_N;	  
   if (Permet_affichage() > 5)
     { cout << " noeud: " << noeud->Num_noeud() << ": force contact " << force_contact ;
       cout << " ||F_N||= "<< F_N.Norme() << " ||F_T||= "<< F_T_max;
       if (force_limiter) cout << " force normale en limite " ;
       if (force_T_limiter) cout << " force tangentielle en limite " ;
     };
   if (Permet_affichage() > 6)
     cout << "\n ==> force contact imposee au residu (noeud:" << noeud->Num_noeud() 
				      << " maillage:"<< noeud->Num_Mail() << ") " << force_contact;
	  int posi = Id_nom_ddl("R_X1") -1; // préparation pour les réactions
	  Ddl_enum_etendu& ddl_reaction_normale=Ddl_enum_etendu::Tab_FN_FT()(1);
	  Ddl_enum_etendu&  ddl_reaction_tangentielle=Ddl_enum_etendu::Tab_FN_FT()(2);
   // une petite manip pour le cas axisymétrique: dans ce cas les ddl à considérer pour le résidu et la raideur
   // doivent être uniquement x et y donc 2 ddl au lieu de 3. (rien ne dépend de z)
   int nb_ddl_en_var =  dima; // init
   bool axi = false;
   if (ParaGlob::AxiSymetrie())
      { nb_ddl_en_var -= 1; axi = true;};
   // récup du kronecker qui va bien
   TenseurHB& IdHB = *Id_dim_HB(ParaGlob::Dimension());
	  //  a- cas du noeud projectile
	  int ism=1; // init cas normale
	  if ((cas_solide == 0 ) || (cas_solide == 1))
	    // cas où le noeud est libre
		   {for (int ix=1;ix<=nb_ddl_en_var;ix++,ism++)   // pour le noeud projectile la vitesse virtuelle associé est
	      {SM_res(ism) += force_contact(ix);  // = F_T + F_N  , vitesse virtuelle: directement la vitesse du noeud
//	   F_N = - N * intens_force = - N * (gap * penalisation)
//        = - N * ( (N * deltaX) * penalisation );
// donc force_contact(ix) = - N(ix) * ( (N * deltaX) * penalisation )
// et d_force_contact(ix) / d ddl_iy = - N(ix) * ( (N * d_deltaX_d_ddl_iy ) * penalisation )
//        + d(- N(ix))_d_ddl_iy * ( (N * deltaX) * penalisation )
//        + - N(ix) * ( (d_N_d_ddl_iy * deltaX) * penalisation )
//        + - N(ix) * ( (N * deltaX) * d_penalisation_d_ddl_iy )
// avec d_penalisation_d_ddl_iy = d_penalisation_d_gap * d_gap_d_ddl_iy
// et sachant que d_gap_d_ddl_iy = N(iy) pour le noeud central

// F_T = -dep_tangentiel * penalisation_tangentielle;

		      // on abonde au niveau de la réaction au noeud
		      noeud->Ajout_val_tdt(Enum_ddl(ix+posi),force_contact(ix)); 
		      // tout d'abord les termes strictement relatifs au noeud esclave
		      int jsm=1;
        if ( cas_collant)
	        {for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++)
           { (*raideur)(ism,jsm) += penalisation * N(ix)*N(iy) // partie classique
                 //+ N(ix) * ( gap * d_beta_gap * N(iy) ) // partie intégrant la variation de la pénalisation
                          + penalisation_tangentielle *(IdHB(ix,iy) - N(iy)*N(ix)) // partie collant
//                          + penalisation_tangentielle *(-IdHB(ix,iy) + N(iy)*N(ix)) // partie collant

// pour le noeud en contact
// M_0atdt = (Noe_atdt-(*M_C0));  dep_tangentiel = M_0atdt- N * (M_0atdt*N); F_T = -dep_tangentiel * penalisation_tangentielle;
// d_F_T = - penalisation_tangentielle * (d_(M_0atdt*N) - d_N * (M_0atdt*N) - N * d_(M_0atdt*N))
// avec d_(M_0atdt) = d_(Noe_atdt) - d_(*M_C0)
// 1) par rapport aux ddl du noeud esclave:
// d_(Noe_atdt)(ix,iy) = IdHB(ix,iy); d_(*M_C0)(ix,iy) = 0;d_N(ix,iy) = 0;
// d'où: d_(M_0atdt)/d_ddl_iy = IdHB(ix,iy) * I_x
// et d_(M_0atdt)/d_ddl_iy * N = somme (IdHB(ix,iy) * N(ix)); et M_0atdt * d_N = 0
// --> d_(M_0atdt*N)(ix,iy) = IdHB(ix,iy) * N(ix) et
// d_F_T(ix,iy) = - penalisation_tangentielle * (IdHB(ix,iy) - N(ix) * N(iy);
                                    ;
           };
         }
        else // sans frottement
          {for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++)
           { (*raideur)(ism,jsm) += penalisation * N(ix)*N(iy) // partie classique
                                    //+ N(ix) * ( gap * d_beta_gap * N(iy) )
                                    ; // partie intégrant la variation de la pénalisation
           };
         };
        //--- fin raideur : noeud esclave - noeud esclave

        // dans le cas de l'axi il faut décaler jsm, ce qui correspond au z
//        if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
        // --- ensuite les termes de couplage noeud esclave - noeuds de la facette
        if (cas_solide == 0) // cas du bi_déformable, sinon pas de terme de couplage
         {int iddl_facette = 1;
          if (d_T_pt != NULL) // car même si on l'a demandé, il peut être null s'il n'existe pas (ex cas 1D )
            {Tableau <Coordonnee >& d_T = *d_T_pt; // variation du vecteur normale: pour simplifier
             if ( cas_collant)
              {if (actif==1) // on doit prendre en compte que deltax n'est pas normal à la facette
                {for (int jr=1;jr<=tab_taille-1;jr++) // boucle sur les noeuds de la facette
                  { for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++) // boucle sur les ddl
                     {double dN_deltaX = (d_T(iddl_facette) * deltaX);
                     (*raideur)(ism,jsm) +=  penalisation * ( - phii(jr) * N(ix)*N(iy)
                                                               + gap * d_T(iddl_facette)(ix)
//                                                               + N(ix) * (d_T(iddl_facette) * deltaX)
                                                               + N(ix) * dN_deltaX
                                                             )
                            + penalisation_tangentielle * (
//                                        (-phi_theta_0(jr)) * (IdHB(ix,iy)+N(ix)*N(iy))
//                                        - phii(jr) * N(ix)*N(iy) +(phi_theta_0(jr)) * IdHB(ix,iy)
                                        -(- phii(jr) * N(ix)*N(iy) +(phi_theta_0(jr)) * IdHB(ix,iy))
//                                        + N(ix) * (d_T(iddl_facette) * M_0atdt)
                                        + N(ix) * dN_deltaX
//                                        + N(ix) * d_T(iddl_facette)(ix) * (N * M_0atdt)
                                        + gap * d_T(iddl_facette)(ix)
                                                           ); // partie collant
                                                              
                    }
                   // dans le cas de l'axi il faut décaler jsm, ce qui correspond au z
//                   if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
                  }
                }
               else // sinon deltax est normal à la facette et d_N * deltaX = 0
                {for (int jr=1;jr<=tab_taille-1;jr++) // boucle sur les noeuds de la facette
                  { for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++) // boucle sur les ddl
                     (*raideur)(ism,jsm) +=  penalisation * ( - phii(jr) * N(ix)*N(iy)
                                                               + gap * d_T(iddl_facette)(ix)
                                                             )
                                   + penalisation_tangentielle * (
                            //                                        (-phi_theta_0(jr)) * (IdHB(ix,iy)+N(ix)*N(iy))
//                                        - phii(jr) * N(ix)*N(iy) +(phi_theta_0(jr)) * IdHB(ix,iy)
                                          -(- phii(jr) * N(ix)*N(iy) +(phi_theta_0(jr)) * IdHB(ix,iy))
                                                                  );
                                                              ;
                   // dans le cas de l'axi il faut décaler jsm, ce qui correspond au z
 //                  if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
                  }
                };
              }
             else // sinon pas collant avec (d_T_pt != NULL)
              {if (actif==1) // on doit prendre en compte que deltax n'est pas normal à la facette
                 {for (int jr=1;jr<=tab_taille-1;jr++) // boucle sur les noeuds de la facette
                   { for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++) // boucle sur les ddl
                      (*raideur)(ism,jsm) +=  penalisation * ( - phii(jr) * N(ix)*N(iy)
                                                                + gap * d_T(iddl_facette)(ix)
                                                                + N(ix) * (d_T(iddl_facette) * deltaX)
                                                               );
                    // dans le cas de l'axi il faut décaler jsm, ce qui correspond au z
//                    if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
                   }
                 }
               else // sinon deltax est normal à la facette et d_N * deltaX = 0
                 {for (int jr=1;jr<=tab_taille-1;jr++) // boucle sur les noeuds de la facette
                   { for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++) // boucle sur les ddl
                      (*raideur)(ism,jsm) +=  penalisation * ( - phii(jr) * N(ix)*N(iy)
                                                                + gap * d_T(iddl_facette)(ix)
                                                               );
                    // dans le cas de l'axi il faut décaler jsm, ce qui correspond au z
//                    if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
                   }
                 };
              };
            }
          else // sinon cas d_T_pt == NULL et (cas_solide == 0)
            {if ( cas_collant)
               {for (int jr=1;jr<=tab_taille-1;jr++) // boucle sur les noeuds de la facette
                 { for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++) // boucle sur les ddl
                     (*raideur)(ism,jsm) +=  penalisation * ( - phii(jr) * N(ix)*N(iy))
                                   + penalisation_tangentielle * (
                            //                                        (-phi_theta_0(jr)) * (IdHB(ix,iy)+N(ix)*N(iy))
//                                        - phii(jr) * N(ix)*N(iy) +(phi_theta_0(jr)) * IdHB(ix,iy)
                                         -(- phii(jr) * N(ix)*N(iy) +(phi_theta_0(jr)) * IdHB(ix,iy))
                                                                  );
                   // dans le cas de l'axi il faut décaler jsm, ce qui correspond au z
//                   if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
                 };
               }
             else // sinon cas non collant
               {for (int jr=1;jr<=tab_taille-1;jr++) // boucle sur les noeuds de la facette
                 { for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++) // boucle sur les ddl
                     (*raideur)(ism,jsm) +=  penalisation * ( - phii(jr) * N(ix)*N(iy));
                   // dans le cas de l'axi il faut décaler jsm, ce qui correspond au z
 //                  if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
                 };
               }; // fin du if (cas_collant)
            }; // fin cas d_T_pt == NULL et (cas_solide == 0)
         }; // fin du if (cas_solide == 0)
       }; // fin de la boucle ix sur le noeud esclave
		   } // fin de la première partie de: if ((cas_solide == 0 ) || (cas_solide == 1))
	  else // sinon c-a-d: cas_solide différent de 0 et 1 c-a-d le noeud est bloqué
	   // on s'occupe quand même des réactions, ce qui nous permettra de les récupérer même sur un solide
		   {for (int ix=1;ix<=dima;ix++)   // pour le noeud projectile la vitesse virtuelle associé est
	      // on abonde au niveau de la réaction au noeud
		     noeud->Ajout_val_tdt(Enum_ddl(ix+posi),force_contact(ix)); 
		   };
   // dans le cas de l'axi il faut décaler ism pour la suite, ce qui correspond au z
//   if (axi) ism++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus

	  // on abonde au niveau de la réaction normale et tangentielle pour le noeud esclave
	  noeud->ModifDdl_etendu(ddl_reaction_normale).Valeur() += -intens_force;
	  double intensite_tangentielle = F_T.Norme();
	  noeud->ModifDdl_etendu(ddl_reaction_tangentielle).Valeur() += intensite_tangentielle;

	  //  b- cas de la surface cible 
	  if ((cas_solide == 0 ) || (cas_solide == 2))
	    // cas où la facette est libre
// pour les noeud de la facette maître, sachant que l'on a pour la force sur le noeud esclave
//	   F_N = - N * intens_force = - N * (gap * penalisation) = - N * ( (N * deltaX) * penalisation )
//        = force_contact
// donc sur les noeuds "s" de la facette on a :
//    F_N^s = phi_s * N * ( (N * deltaX) * penalisation )
// d'autre part on a gap = N * (X(t+dt)-M_impact) = N * (X(t+dt)- phi_r * X(tdt)^r)
// avec X(tdt)^r les coordonnées du noeud r de la facette
// d'où la forme finale pour le noeud "s" de la facette :
//   F_N^s = phi_s * N * ( (N * (X(t+dt)- phi_r * X(tdt)^r)) * penalisation )
//         = - phi_s * force_contact
//
// maintenant au niveau des raideurs:
// les raideurs relativements au noeud esclave -> dF_N^s/dX(t+dt)(iy)
//  dF_N^s/dX(t+dt)(iy) = phi_s * N * (N(iy)) * penalisation
//
// les raideurs relativements entre noeuds de la facette
// dF_N^s/dX(t+dt)(iy) =  phi_s * N * ( (N(iy) * (- phi_r ) * penalisation )
//                     + phi_s * d_N/dX(t+dt)(iy) * ( (N * (X(t+dt)- phi_r * X(tdt)^r)) * penalisation )
//                     + phi_s * N * (d_N/dX(t+dt)(iy) * (X(t+dt)- phi_r * X(tdt)^r)) * penalisation )


// donc force_contact(ix) = - N(ix) * ( (N * deltaX) * penalisation )
// et d_force_contact(ix) / d ddl_iy = - N(ix) * ( (N * d_deltaX_d_ddl_iy ) * penalisation )
//        + d(- N(ix))_d_ddl_iy * ( (N * deltaX) * penalisation )
//        + - N(ix) * ( (d_N_d_ddl_iy * deltaX) * penalisation )
//        + - N(ix) * ( (N * deltaX) * d_penalisation_d_ddl_iy )
// avec d_penalisation_d_ddl_iy = d_penalisation_d_gap * d_gap_d_ddl_iy
// et sachant que d_gap_d_ddl_iy = N(iy) pour le noeud central
	   {for (int is=1;is<=tab_taille-1;is++)
	    { Noeud* noe = tabNoeud(is+1); // pour simplifier
       if (Permet_affichage() >= 7)
         cout << "\n reaction facette imposee au residu (noeud:" << noe->Num_noeud()
				          << " maillage:"<< noe->Num_Mail() << ") " ;
       tabForce_cont(is) = -force_contact*phii(is);
       for (int ix=1;ix<=nb_ddl_en_var;ix++,ism++)
	       { double force_imp = -force_contact(ix)*phii(is);
          SM_res(ism) += force_imp;
          if (Permet_affichage() > 6)
            cout << force_imp << " ";
          // on abonde au niveau de la réaction au noeud
          noe->Ajout_val_tdt(Enum_ddl(ix+posi),force_imp); 
          int jsm = 1;
          // tout d'abord le terme de couplage
          if (cas_solide == 0) // on intervient que si c'est déformable-déformable
           { if ( cas_collant)
              {for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++)
                { (*raideur)(ism,jsm)
                     += - phii(is)
                           * (penalisation * (N(ix)*N(iy))
                              //                          + penalisation_tangentielle *(IdHB(ix,iy) - N(iy)*N(ix)) // partie collant
                              + penalisation_tangentielle *(IdHB(ix,iy) - N(iy)*N(ix)) // partie collant
                              // partie collant
                             );
                }
              }
             else // cas non collant
              {for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++)
                { (*raideur)(ism,jsm) += - phii(is) * penalisation * N(ix)*N(iy);}
              }
           };
         
          // dans le cas de l'axi il faut décaler jsm pour la suite, ce qui correspond au z
//          if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus

          // puis les termes facette - facette
          int iddl_facette = 1; 
          if (d_T_pt != NULL) // car même si on l'a demandé, il peut être null s'il n'existe pas (ex cas 1D )
            {Tableau <Coordonnee >& d_T = *d_T_pt; // pour simplifier
             if (cas_collant)
              {if (actif==1) // on doit prendre en compte que deltax n'est pas normal à la facette
                {for (int js=1;js<=tab_taille-1;js++)
                 {for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++)
                   {double dN_deltaX = (d_T(iddl_facette) * deltaX);
                    (*raideur)(ism,jsm)
                       +=  - phii(is) * ( penalisation
                                             *(N(ix) * ( - phii(js) * N(iy))
                                                 + d_T(iddl_facette)(ix) * gap
                                                 + N(ix) * (d_T(iddl_facette) * deltaX)
                                              )
                             + penalisation_tangentielle * (
 //                                        (-phi_theta_0(jr)) * (IdHB(ix,iy)+N(ix)*N(iy))
 //                                        - phii(js) * N(ix)*N(iy) +(phi_theta_0(js)) * IdHB(ix,iy)
                                         -(- phii(js) * N(ix)*N(iy) +(phi_theta_0(js)) * IdHB(ix,iy))
 //                                        + N(ix) * (d_T(iddl_facette) * M_0atdt)
                                         + N(ix) * dN_deltaX
 //                                        + N(ix) * d_T(iddl_facette)(ix) * (N * M_0atdt)
                                         + gap * d_T(iddl_facette)(ix)
                                                            ) // partie collant
                                        );
                 };

                  // dans le cas de l'axi il faut décaler jsm pour la suite, ce qui correspond au z
//                  if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
                 }
                } //fin cas actif == 1
               else // sinon: actif diff de 1,  deltax est normal à la facette et d_N * deltaX = 0
                {for (int js=1;js<=tab_taille-1;js++)
                  {for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++)
                    (*raideur)(ism,jsm)
                       +=  - phii(is) * ( penalisation
                                            * (N(ix) * ( - phii(js) * N(iy))
                                               + d_T(iddl_facette)(ix) * gap
                                              )
                                          + penalisation_tangentielle * (
                                        //                                        (-phi_theta_0(jr)) * (IdHB(ix,iy)+N(ix)*N(iy))
//                                                    - phii(js) * N(ix)*N(iy) +(phi_theta_0(js)) * IdHB(ix,iy)
                                          -(- phii(js) * N(ix)*N(iy) +(phi_theta_0(js)) * IdHB(ix,iy))
                                                                         )
                                        );
                   // dans le cas de l'axi il faut décaler jsm pour la suite, ce qui correspond au z
//                   if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
                  }
                 }; // fin cas actif != 1
              }
             else // sinon cas non collant
              {if (actif==1) // on doit prendre en compte que deltax n'est pas normal à la facette
                {for (int js=1;js<=tab_taille-1;js++)
                 {for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++)
                   (*raideur)(ism,jsm) +=  - phii(is) * penalisation * (
                                                   N(ix) * ( - phii(js) * N(iy))
                                                 + d_T(iddl_facette)(ix) * gap
                                                 + N(ix) * (d_T(iddl_facette) * deltaX)
                                                                       );

                  // dans le cas de l'axi il faut décaler jsm pour la suite, ce qui correspond au z
//                  if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
                 }
                } //fin cas actif == 1
               else // sinon: actif diff de 1,  deltax est normal à la facette et d_N * deltaX = 0
                {for (int js=1;js<=tab_taille-1;js++)
                  {for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++)
                    (*raideur)(ism,jsm) +=  - phii(is) * penalisation * (
                                                    N(ix) * ( - phii(js) * N(iy))
                                                  + d_T(iddl_facette)(ix) * gap
                                                                        );

                   // dans le cas de l'axi il faut décaler jsm pour la suite, ce qui correspond au z
//                   if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
                  }
                 }; // fin cas actif != 1
              }; // fin du if then else sur : if (cas_collant)

            } // fin du cas if (d_T_pt != NULL)
          else
            {if (cas_collant)
              {for (int js=1;js<=tab_taille-1;js++)
                 {for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++)
                   (*raideur)(ism,jsm)
                       +=  - phii(is) * ( penalisation
                                            * (N(ix) * ( - phii(js) * N(iy)))
                                          + penalisation_tangentielle * (
                                        //                                        (-phi_theta_0(jr)) * (IdHB(ix,iy)+N(ix)*N(iy))
//                                         - phii(js) * N(ix)*N(iy) +(phi_theta_0(js)) * IdHB(ix,iy)
                                          -(- phii(js) * N(ix)*N(iy) +(phi_theta_0(js)) * IdHB(ix,iy))
                                                                         )
                                        );
                  // dans le cas de l'axi il faut décaler jsm pour la suite, ce qui correspond au z
//                  if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
                 };
              }
             else // sinon cas non collant
              {for (int js=1;js<=tab_taille-1;js++)
                 {for (int iy=1;iy<=nb_ddl_en_var;iy++,jsm++,iddl_facette++)
                   (*raideur)(ism,jsm) +=  - phii(is) * penalisation * (
                                                   N(ix) * ( - phii(js) * N(iy))
                                                                       );
                  // dans le cas de l'axi il faut décaler jsm pour la suite, ce qui correspond au z
//                  if (axi) jsm++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus

                 };
              };
            }; // fin du cas où d_T_pt == NULL
        };
       // dans le cas de l'axi il faut décaler ism pour la suite, ce qui correspond au z
//       if (axi) ism++; // qui n'est pas pris en compte
// non: 20 mars 2019: maintenant le SM local a juste les ddl de X1 et X2  donc ce qui correspond à nb_ddl_en_var
// du coup on ne décale plus
       // on abonde au niveau de la réaction normale et tangentielle pour les noeuds maîtres
       noe->ModifDdl_etendu(ddl_reaction_normale).Valeur() += -intens_force*phii(is);
       noe->ModifDdl_etendu(ddl_reaction_tangentielle).Valeur() += intensite_tangentielle*phii(is);
     };
    }
	  else  // *** modif 23 juin 2015 , a virer si c'est ok
	   // on s'occupe quand même des réactions, ce qui nous permettra de les récupérer même sur un solide
	   {for (int ir=1;ir<=tab_taille-1;ir++) 
	    { Noeud* noe = tabNoeud(ir+1); // pour simplifier
       tabForce_cont(ir) = -force_contact*phii(ir);
       for (int ix=1;ix<=dima;ix++)
	       { double force_imp = -force_contact(ix)*phii(ir);
           // on abonde au niveau de la réaction au noeud
           noe->Ajout_val_tdt(Enum_ddl(ix+posi),force_imp); 
        };
       // on abonde au niveau de la réaction normale et tangentielle pour les noeuds maîtres
       noe->ModifDdl_etendu(ddl_reaction_normale).Valeur() += -intens_force*phii(ir);
       noe->ModifDdl_etendu(ddl_reaction_tangentielle).Valeur() += intensite_tangentielle*phii(ir);
     };
//     // --- debug
//{    for (int ir=1;ir<=tab_taille-1;ir++)
//      { Noeud* noe = tabNoeud(ir+1); // pour simplifier
//        if ((noe->Num_noeud() == 5) && (noe->Num_Mail() == 2))
//         {cout << "\n debug : ElContact::SM_K_charge_contact() ";
//          cout << " R_X= ";
//          for (int ix=1;ix<=dima;ix++)
//             cout << noe->Valeur_tdt(Enum_ddl(ix+posi)) << " ";
//         }
//     }
//}
//     // --- fin debug
    };
	  // retour
//	  	  return residu;  // SM est nul à ce niveau du programme
//// --- debug
//cout << "\n debug : ElContact::SM_K_charge_contact() ";
//SM_res.Affiche();
//raideur->Affiche();
//// --- fin debug

		  return el;
	};
 
// récup uniquement des conteneurs raideurs et résidu (pas forcément remplis, mais de la bonne taille)
Element::ResRaid  ElContact::Conteneur_ResRaid() 
  { Mise_a_jour_ddlelement_cas_solide_assemblage(); // mise à jour de ddl element et de cas_solide
    // def de quelques dimensions pour simplifier
    int dima = ParaGlob::Dimension();
    int tab_taille = tabNoeud.Taille();
    int nbddl = ddlElement_assemblage->NbDdl();
    // récup  d'une place pour le résidu local et mise à jour de list_SM éventuellement
    RecupPlaceResidu(nbddl);
    // idem pour la raideur
    RecupPlaceRaideur(nbddl);
    // préparation du retour
    Element::ResRaid el;
    el.res = residu;
    el.raid = raideur;
    return el;
  };
// récup uniquement du conteneur résidu (pas forcément rempli, mais de la bonne taille)
Vecteur* ElContact::Conteneur_Residu()
   {Mise_a_jour_ddlelement_cas_solide_assemblage(); // mise à jour de ddl element et de cas_solide
    // def de quelques dimensions pour simplifier
    int dima = ParaGlob::Dimension();
    int tab_taille = tabNoeud.Taille();
    int nbddl = ddlElement_assemblage->NbDdl();
    // récup  d'une place pour le résidu local et mise à jour de list_SM éventuellement
    RecupPlaceResidu(nbddl);
    return residu;
   };

 //================================= methodes privées ===========================
 
// calcul la normale en fonction de differente conditions
Coordonnee ElContact::Calcul_Normale(int dim, Plan & pl, Droite & dr,int indic)
 {
  Coordonnee N(dim); // le vecteur normal initialisé à 0
  ElFrontiere & elfro = *(elfront->Eleme()); // pour commodite
  // --- le cas des angles morts est particulier, on commence par le traiter
  if ((elfront->Angle_mort()) && (elfro.Type_geom_front() == POINT_G))
   { // s'il s'agit d'un point il n'y a pas d'existance de normale
     // on utilise la normale au noeud
     const TypeQuelconque& tiq = tabNoeud(2)->Grandeur_quelconque(N_FRONT_t);
     const Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) (tiq.Const_Grandeur_pointee()));
     N = gr.ConteneurCoordonnee_const();
   }
  // ---- maintenant on regarde 1) si on veut une normale lissée
  //      2) qui représente aussi le cas d'angle mort avec une ligne normalement qu'en 3D,
  //      pour laquelle il n'y a pas d'existance d'une normale classique
  
  // on utilise ici la normale interpolée aux noeuds
  else if (   (normale_lisser)
           || ((elfront->Angle_mort()) && (elfro.Type_geom_front() == LIGNE))
          )
   {// dans le cas d'une normale lissée on va utiliser une interpolation des normales aux noeuds
    ElFrontiere & elfro = *(elfront->Eleme()); // pour commodite
    // recup et actualisation du dernier plan tangent, coordonnées locale etc.
    // dimensionnement des variables de tangence
//    //debug
//             cout << "\n debug ElContact::Calcul_Normale: ";
//             cout << "\n elfro.DernierTangent " << flush;
//    // fin debug
//    Plan pl(dim); Droite dr(dim); int indic; // def des variables de tangence
    elfro.DernierTangent(dr,pl,indic,false);
    // récup des fonctions phi
    const Vecteur& phii = elfro.Phi();
    N.Zero();
//    //debug
//             cout << "\n debug ElContact::Calcul_Normale: ";
//             cout << "\n phii= " << phii;
//    // fin debug
    // on parcours les noeuds de la frontière
    // retourne le tableau de noeud en lecture lecture/écriture
    Tableau <Noeud *>& tNfront = elfro.TabNoeud();
    int nbNfront = tNfront.Taille();
    Coordonnee Nnoe(dim); // variable inter
    for (int inoe = 1;inoe <= nbNfront;inoe++)
     { const TypeQuelconque& tiq = tNfront(inoe)->Grandeur_quelconque(N_FRONT_t);
       const Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) (tiq.Const_Grandeur_pointee()));
       Nnoe = gr.ConteneurCoordonnee_const();
       
// //------ debug
// if ((tNfront(inoe)->Num_noeud() == 1708) && (tNfront(inoe)->Num_Mail()==2))
//  { cout << "\n debug Coordonnee ElContact::Calcul_Normale:"
//         << " noe "<< tNfront(inoe)->Num_noeud() << " N à t = "; Nnoe.Affiche();
//  };
// // ----- end debug


////debug
//         cout << "\n debug ElContact::Calcul_Normale: ";
//         cout << "\n Nnoe= " << Nnoe;
//// fin debug
//{TypeQuelconque& tiq_t = tNfront(inoe)->ModifGrandeur_quelconque(N_FRONT_t);
// Grandeur_coordonnee& gr_t= *((Grandeur_coordonnee*) (tiq_t.Grandeur_pointee()));
// Coordonnee& normale_t = *gr.ConteneurCoordonnee();
//
//}
       N += phii(inoe)*Nnoe;
//   cout << "\n Nnoe= " << Nnoe;
     };
  #ifdef MISE_AU_POINT
    {if (N.Norme() <= ConstMath::trespetit)
      {cout << "\n *** probleme  ElContact::Calcul_Normale: ";
       this->Affiche(1);
       cout << "\n la norme de la normale calcule est nulle ("<< N.Norme() <<") ";
       cout << "\n les normales aux noeuds: ";
       for (int inoe = 1;inoe <= nbNfront;inoe++)
        { const TypeQuelconque& tiq = tNfront(inoe)->Grandeur_quelconque(N_FRONT_t);
          const Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) (tiq.Const_Grandeur_pointee()));
          Nnoe = gr.ConteneurCoordonnee_const();
          cout << "\n noeud " << tNfront(inoe)->Num_noeud()
               << " N= "<< Nnoe << " phi= " << phii(inoe) ;
        }
       cout <<  endl ;
      }
    }
  #endif
    N.Normer();
   }
   // --- on continue avec les éléments qui ne sont pas d'angle mort et sans lissage
  else if ( (dim == 3) && (indic == 2))
    // cas 3d avec une surface
    {  N = pl.Vecplan(); 
    }
  
  else if (   ((dim == 2) && (indic == 1))
             || (ParaGlob::AxiSymetrie() && (dim == 3) && (indic == 1))
            )
    // cas 2D avec une ligne, (ou 3D bloqué avec une ligne ??)
    // ou 3D axisymétrique avec une ligne

//	 else if((dim == 2) && (indic == 1))
//    // cas 2D ou 3D bloqué avec une ligne
    { Coordonnee V = dr.VecDroite(); // le vecteur tangent
		    // ici il faut faire attention: la normale est prise de manière à être sortante de l'élément
		    // car normalement, le vecteur unitaire de la droite est déterminée avec deux points suivants la numérotation de l'élément
		    // donc la normale à ce vecteur, en suivant le sens trigo, se trouve dirigé vers l'intérieur de l'élément
      N(1) = V(2); N(2) = -V(1);
    }    

  else if ( (dim == 3) && (indic == 1))
      // cas 3d avec une ligne, on va considérer que la normale est la normale dans la direction
		  // du noeud esclave (si celui-ci est suffisemment externe à la ligne
		  { Coordonnee V = dr.VecDroite(); // On récupère le vecteur tangent à la ligne
		    Coordonnee A = noeud->Coord2() - tabNoeud(2)->Coord2(); // on construit un vecteur entre le premier point de ligne -> le noeud esclave
		    Coordonnee B = Util:: ProdVec_coor(V,A); // produit vectoriel
		    // on ne continue que si ce produit vectoriel n'est pas nul
		    if (B.Norme() > ConstMath::petit)
		     { N = Util:: ProdVec_coor(V,B);}
		    else // sinon cela veut dire que le noeud est sur la ligne, dans ce cas particulier, d'une manière arbitraire
		      // on prend la normale dans un des 3 plans de base, en commençant par le plan xy qui serait celui où par exemple
			     // on travaille en 3D, mais avec le z bloqué
		     { // on commence par voir si c'est possible en xy
			      if (DabsMaX(V(1),V(2)) > ConstMath::petit)
			        // là c'est ok la ligne n'est pas // à z
		         // ici il faut faire attention: la normale est prise de manière à être sortante de l'élément
		         // car normalement, le vecteur unitaire de la droite est déterminée avec deux points suivants la numérotation de l'élément
		         // donc la normale à ce vecteur, en suivant le sens trigo, se trouve dirigé vers l'intérieur de l'élément
            { N(1) = V(2); N(2) = -V(1); 
				          N(3)=0.; // on pose z=0 d'où une normale dans le plan xy
				        }
			      else // sinon cela veut dire que l'on est suivant l'axe de z, on choisit x par défaut
			         { N(1)=1; N(2)=N(3)=0.;};
		     };
		  }
  else if ( (dim == 1) && (indic == 0))
    // cas 1D avec un point
    { // la normale sortante de l'élément c'est soit 1 ou -1
      // pour le déterminer on commence par trouver le coté de l'élément frontière qui va rentrer en contact
      // on est obligé de considérer l'élément
      // pour cela on cherche le noeud de l'élément qui correspond au noeud frontière
      // qui normalement est le second noeud du global des noeuds de l'élément de contact
      Noeud * no_front= tabNoeud(2); int num_no_front = no_front->Num_noeud();
      const Tableau < Noeud * > t_N_elem = elfront->PtEI()->Tab_noeud_const();
      int nbN_elem = t_N_elem.Taille(); int num_N=0;
      for (int i=1;i<= nbN_elem;i++)
        if (t_N_elem(i)->Num_noeud() == num_no_front)
          {num_N = i;break;};
      if (num_N == 0)
       { cout << "\n *** warning  on n'a pas trouve de frontiere correct pour le calcul de la normale "
              << "\n cas du contact entre deux points ... le contact sera mal calcule " << endl;
       };
      // maintenant on prend au autre noeud de l'élément et on regarde le sens
      for (int i=1;i<= nbN_elem;i++)
        if (i != num_N)
          {// il s'agit bien d'un autre noeud
           if (t_N_elem(i)->Coord2()(1) > no_front->Coord2()(1) )
                {N(1) = -1.;}
           else {N(1) = 1.;}
           break;
          };
    }
  else
    // autres cas non traite pour l'instant
    { cout << "\n erreur, desole mais le cas de contact en dimension = " << dim 
           << ", avec ";
      if (indic == 1)
        cout << " une droite ";
      else
        cout << " un plan ";
      cout << " n\'est pas actuellement traite \n" ;
      Affiche(1);
      cout << "ElContact::Normal(.." << endl;
      Sortie(1);
    };
  return N;  
 };

// récupération d'informations des classes internes
// N: le vecteur normal
// M_impact: le point d'impact sur la surface (ou ligne ou point)
// phii : les fonctions d'interpolation au point d'impact
// si avec_var est vrai: il y a retour du tableau de variation de la normale
Tableau <Coordonnee >*  ElContact::RecupInfo(Coordonnee& N,Coordonnee& M_impact,Vecteur& phii,bool avec_var)
  { ElFrontiere & elfro = *(elfront->Eleme()); // pour commodite
    // recup de la dimension 
    int dim = noeud->Dimension();
    // recup du dernier plan tangent (ou droite)
    // dimensionnement des variables de tangence
    Plan pl(dim); Droite dr(dim); int indic; // def des variables de tangence
    Tableau <Coordonnee >* d_T = NULL; // init par défaut = elfro.DernierTangent(dr,pl,indic,avec_var);
    // different cas
    N.Change_dim(dim); // le vecteur normal
    // on commence par traiter le cas des éléments particuliers qui décrivent les angles morts
    if ((elfront->Angle_mort()) && (elfro.Type_geom_front() == POINT_G))
     { // s'il s'agit d'un point il n'y a pas d'existance de normale
       // on utilise la normale au noeud
       elfro.DernierTangent(dr,pl,indic,false);
       // on ne considère pas la variation de la normale
       d_T = NULL;
       const TypeQuelconque& tiq = tabNoeud(2)->Grandeur_quelconque(N_FRONT_t);
       const Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) (tiq.Const_Grandeur_pointee()));
       N = gr.ConteneurCoordonnee_const();
       M_impact = elfro.Ref();
     }
    // ---- maintenant on regarde si on veut une normale lissée
    // ---- maintenant on regarde 1) si on veut une normale lissée
    //      2) qui représente aussi le cas d'angle mort avec une ligne normalement qu'en 3D,
    //      pour laquelle il n'y a pas d'existance d'une normale classique
    
    
    // on utilise ici la normale interpolée aux noeuds
    else if (   (normale_lisser)
             || ((elfront->Angle_mort()) && (elfro.Type_geom_front() == LIGNE))
            )
     {// dans le cas d'une normale lissée on va utiliser une interpolation des normales aux noeuds
      ElFrontiere & elfro = *(elfront->Eleme()); // pour commodite
      // recup et actualisation du dernier plan tangent, coordonnées locale etc.
      // dimensionnement des variables de tangence
      Plan pl(dim); Droite dr(dim); int indic; // def des variables de tangence
      elfro.DernierTangent(dr,pl,indic,false);
      // le vecteur normal est fixe ici pendant les itérations
      d_T = NULL;
      // récup des fonctions phi
      const Vecteur& phii = elfro.Phi();
      // on parcours les noeuds de la frontière
      // retourne le tableau de noeud en lecture lecture/écriture
      Tableau <Noeud *>& tNfront = elfro.TabNoeud();
      int nbNfront = tNfront.Taille();
      Coordonnee Nnoe(dim); // variable inter
      N.Zero();
      for (int inoe = 1;inoe <= nbNfront;inoe++)
       { const TypeQuelconque& tiq = tNfront(inoe)->Grandeur_quelconque(N_FRONT_t);
         const Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) (tiq.Const_Grandeur_pointee()));
         Nnoe = gr.ConteneurCoordonnee_const();
         N += phii(inoe)*Nnoe;
       };
      N.Normer();
////debug
//if (Permet_affichage() > 4)
// {cout << "\n debug ElContact::RecupInfo N= "<<N ;
// };
////fin debug

      // récup du point d'impact
      switch (indic)
       { case 2: M_impact = pl.PointPlan(); break;
         case 1: M_impact = dr.PointDroite(); break;
         case 0: M_impact = tabNoeud(2)->Coord2(); break;
         default:
           {cout << "\n *** erreur indic =" << indic <<" c-a-d diff de 2 ou 1 ou 0 "
                 << " on ne peut pas continuer !";
            this->Affiche(1);
           }
           break;
       };
     }
    else
     {
      // cas d'élément de contact autres que ceux qui représentent les angles morts
      // et des normales lissées
      d_T = elfro.DernierTangent(dr,pl,indic,avec_var);
      if (ParaGlob::AxiSymetrie() && (dim == 3))
         N(3) = 0.; // init pour le cas axi
      if ( (dim == 3) && (indic == 2))
        // cas 3d avec une surface
        {  N = pl.Vecplan();
           M_impact = pl.PointPlan();
        }
      else if (   ((dim == 2) && (indic == 1))
               || (ParaGlob::AxiSymetrie() && (dim == 3) && (indic == 1))
              )
        // cas 2D avec une ligne
        // ou 3D axisymétrique avec une ligne
        { Coordonnee V = dr.VecDroite(); // le vecteur tangent
  //        N(1) = -V(2); N(2) = V(1);  // un vecteur normal
  // !!!! pour l'instant il y a une erreur de numérotation: on utilise en fait une numérotation
  // exactement inverse de la doc !!!
          N(1) = V(2); N(2) = -V(1);  // un vecteur normal
          // en axi N(3) est déjà initialisé à 0
          M_impact = dr.PointDroite();
        }
      else if ( (dim == 3) && (indic == 1))
        // cas 3d avec une ligne, on va considérer que la normale est la normale dans la direction
  // **** a revoir car c'est problématique quand le noeud esclave est très proche de la ligne
  // du coup on peut avoir une normale qui oscille d'un coté à l'autre de la ligne ce qui fait que l'on va
  // avoir ensuite une force de contact ou de collage !!! aléatoire !!! pas bon du tout
  // sans doute trouver autre chose : peut-être que c'est ok quand le point est franchement hors de la ligne
  // pas pas autrement
        // du noeud esclave (si celui-ci est suffisemment externe à la ligne
        { Coordonnee V = dr.VecDroite(); // On récupère le vecteur tangent à la ligne
          Coordonnee A = noeud->Coord2() - tabNoeud(2)->Coord2(); // on construit un vecteur entre le premier point de ligne -> le noeud esclave
          Coordonnee B = Util:: ProdVec_coor(V,A); // produit vectoriel
          // on ne continue que si ce produit vectoriel n'est pas nul
          if (B.Norme() > ConstMath::petit)
           { N = Util:: ProdVec_coor(V,B).Normer();}
          else // sinon cela veut dire que le noeud est sur la ligne, dans ce cas particulier, d'une manière arbitraire
           // on prend la normale dans un des 3 plans de base, en commençant par le plan xy qui serait celui où par exemple
           // on travaille en 3D, mais avec le z bloqué
           { // on commence par voir si c'est possible en xy
             if (DabsMaX(V(1),V(2)) > ConstMath::petit)
               // là c'est ok la ligne n'est pas // à z
               // ici il faut faire attention: la normale est prise de manière à être sortante de l'élément
               // car normalement, le vecteur unitaire de la droite est déterminée avec deux points suivants la numérotation de l'élément
               // donc la normale à ce vecteur, en suivant le sens trigo, se trouve dirigé vers l'intérieur de l'élément
              { N(1) = V(2); N(2) = -V(1);
                N(3)=0.; // on pose z=0 d'où une normale dans le plan xy
              }
             else // sinon cela veut dire que l'on est suivant l'axe de z, on choisit x par défaut
              { N(1)=1; N(2)=N(3)=0.;};
           };
          M_impact = dr.PointDroite();
        }
  //    else if ( (dim == 2) && (indic == 1))
  //      // cas 2D avec une ligne
  //      { Coordonnee V = dr.VecDroite(); // le vecteur tangent
  ////        N(1) = -V(2); N(2) = V(1);  // un vecteur normal
  //// !!!! pour l'instant il y a une erreur de numérotation: on utilise en fait une numérotation
  //// exactement inverse de la doc !!!
  //        N(1) = V(2); N(2) = -V(1);  // un vecteur normal
  //        M_impact = dr.PointDroite();
  //      }
      else if ( (dim == 1) && (indic == 0))
        // cas 1D avec un point
        { // la normale sortante de l'élément c'est soit 1 ou -1
          // pour le déterminer on commence par trouver le coté de l'élément frontière qui va rentrer en contact
          // on est obligé de considérer l'élément
          // pour cela on cherche le noeud de l'élément qui correspond au noeud frontière
          // qui normalement est le second noeud du global des noeuds de l'élément de contact
          Noeud * no_front= tabNoeud(2); int num_no_front = no_front->Num_noeud();
          const Tableau < Noeud * > t_N_elem = elfront->PtEI()->Tab_noeud_const();
          int nbN_elem = t_N_elem.Taille(); int num_N=0;
          for (int i=1;i<= nbN_elem;i++)
            if (t_N_elem(i)->Num_noeud() == num_no_front)
              {num_N = i;break;};
          if (num_N == 0)
           { cout << "\n *** warning  on n'a pas trouve de frontiere correct pour le calcul de la normale "
                  << "\n cas du contact entre deux points ... le contact sera mal calcule " << endl;
           };
          // maintenant on prend au autre noeud de l'élément et on regarde le sens
          for (int i=1;i<= nbN_elem;i++)
            if (i != num_N)
              {// il s'agit bien d'un autre noeud
               if (t_N_elem(i)->Coord2()(1) > no_front->Coord2()(1) )
                    {N(1) = -1.;}
               else {N(1) = 1.;}
               break;
              };
  // non erreur !!        M_impact = N; // sera modifié ensuite, la valeur n'a pas d'importance
          // a priori le point d'impact c'est le noeud frontière
          M_impact = no_front->Coord2();
        }
      else if ( (dim == 1) && (indic == 1))
        // cas 1D avec une droite
        { N(1) = 1.;  // le vecteur normal, qui correspond au seul vecteur disponible
          M_impact = dr.PointDroite();;
        }
      else
        // autres cas non traite pour l'instant
        { cout << "\n erreur, desole mais le cas de contact en dimension = " << dim
               << ", avec ";
          if (indic == 1)
            cout << " une droite ";
          else
            cout << " un plan ";
          cout << " n\'est pas actuellement traite \n" ;
          Affiche(1);
          cout << "ElContact::RecupInfo(.."<< endl;
          Sortie(1);
        };
     };
    // récup des fonctions phi
    phii = elfro.Phi();
    // retour
    return d_T;  		
  };

	 // mise à jour de cas_solide et donc de ddlElement en fonction de l'activité des ddl
void ElContact::Mise_a_jour_ddlelement_cas_solide_assemblage()
{cas_solide = 0; // init par défaut: déformable-déformable
 // on regarde l'activité des noeuds
 // 1-- cas du noeud esclave
 int dim = ParaGlob::Dimension();
 bool esclave_solide = true;
 switch (dim) 
   {case 3: esclave_solide = noeud->Ddl_fixe(X3) && esclave_solide ;
	   case 2: esclave_solide = noeud->Ddl_fixe(X2) && esclave_solide ;
	   case 1: esclave_solide = noeud->Ddl_fixe(X1) && esclave_solide ;
   };
 
 // 2-- la frontière 
 int tal = tabNoeud.Taille();
 bool front_solide = true;
 for (int i=2;i<=tal;i++)
  { Noeud* noe = tabNoeud(i);
    switch (dim) 
		  {case 3: front_solide = noe->Ddl_fixe(X3) && front_solide ;
		   case 2: front_solide = noe->Ddl_fixe(X2) && front_solide ;
		   case 1: front_solide = noe->Ddl_fixe(X1) && front_solide ;
		  };
  };
 
 // maintenant on traite les différents cas
 int dim_ddlelement = 0;
 if (esclave_solide &&  front_solide) 
                          {cas_solide = 3;dim_ddlelement=0;}
 else if (esclave_solide) {cas_solide = 2;dim_ddlelement=tal-1;}		
 else if (front_solide)   {cas_solide = 1;dim_ddlelement=1;}		
 else                     {cas_solide = 0;dim_ddlelement=tal;};
 
 // maintenant on dimensionne ddlElement éventuellement
 if (ddlElement_assemblage->NbNoeud() != dim_ddlelement)
   // il faut redimensionner
   {
     // en fait tous les éléments du tableau sont identiques et il suffit qu'ils existent
     // on commence par parcourir la liste pour trouver un bon candidat 
     list <DdlElement>::iterator ili,ilifin=list_Ddl_global.end();
     bool trouve = false;
     for (ili=list_Ddl_global.begin();ili !=ilifin; ili++)
       if ((*ili).NbNoeud() == dim_ddlelement) // on a trouvé un candidat
         {ddlElement_assemblage = &(*ili); trouve = true;};
     // dans le cas où on n'a pas trouvé, on en cré un
     if (!trouve)  
      { int dima = ParaGlob::Dimension();
        // dans le cas où on est en axisymétrie il faut diminuer de 1
        if (ParaGlob::AxiSymetrie())
          dima--;
        DdlElement  tab_ddl(dim_ddlelement,dima);
        int posi = Id_nom_ddl("X1") -1;
        for (int i =1; i<= dima; i++)
	        for (int j=1; j<= dim_ddlelement; j++)
	         tab_ddl.Change_Enum(j,i,Enum_ddl(i+posi));
	       list_Ddl_global.push_front(tab_ddl); 
	       ddlElement_assemblage = &(*list_Ddl_global.begin());
      };
    
	    // ensuite il faut s'occuper du tableau d'assemblage tabNoeud_pour_assemblage.Change_taille(0);
	    switch (cas_solide) 
	     {case 0: tabNoeud_pour_assemblage = tabNoeud; // c'est le cas le plus complet
			      break;
		     case 1: tabNoeud_pour_assemblage.Change_taille(1); // seulement le noeud libre
				     tabNoeud_pour_assemblage(1)=noeud;
			      break;
		     case 2: tabNoeud_pour_assemblage.Change_taille(tal-1); // la facette seule est libre
         for (int i=2;i<=tal;i++)
             tabNoeud_pour_assemblage(i-1) = tabNoeud(i);
			      break;
		     case 3: tabNoeud_pour_assemblage.Change_taille(0); // tout est bloqué
			      break;
		    };		
	  };
};


    // récup  d'une place pour le résidu local et mise à jour de list_SM éventuellement
void ElContact::RecupPlaceResidu(int nbddl)
	{ // tout d'abord on vérifie que la place actuelle ne convient pas
	  if (residu != NULL)
	  	{if (residu->Taille() == nbddl)
	  		{residu->Zero(); return; } // cas courant
	  	};
	  // dans tous les autres cas il faut soit récupérer une place qui convient soit en créé une autre
   // on commence par parcourir la liste pour trouver un bon candidat 
   list <Vecteur>::iterator ilv,ilvfin=list_SM.end();
   bool trouve = false;
   for (ilv=list_SM.begin();ilv !=ilvfin; ilv++)
    if ((*ilv).Taille() == nbddl) // on a trouvé un candidat
      {residu = &(*ilv); residu->Zero();trouve = true;};
   // dans le cas où on n'a pas trouvé, on en cré un
   if (!trouve)
      { Vecteur interv(nbddl,0.); // vecteur mis à 0 par défaut
	       list_SM.push_front(interv);
	       residu = &(*list_SM.begin());
      };
	};
	
    // récup  d'une place pour la raideur locale et mise à jour de list_raideur éventuellement
void ElContact::RecupPlaceRaideur(int nbddl)
	{ // tout d'abord on vérifie que la place actuelle ne convient pas
	  if (raideur != NULL)
	  	{if (raideur->Nb_ligne() == nbddl)
	  		{raideur->Initialise(0.); return; } // cas courant
	  	};
	  // dans tous les autres cas il faut soit récupérer une place qui convient soit en créé une autre
      // on commence par parcourir la liste pour trouver un bon candidat 
      list <Mat_pleine>::iterator ilv,ilvfin=list_raideur.end();
      bool trouve = false;
      for (ilv=list_raideur.begin();ilv !=ilvfin; ilv++)
       if ((*ilv).Nb_ligne() == nbddl) // on a trouvé un candidat
         {raideur = &(*ilv); raideur->Initialise(0.);trouve = true;};
     // dans le cas où on n'a pas trouvé, on en cré un
     if (!trouve)  
      { Mat_pleine interv(nbddl,nbddl,0.); // init à 0 par défaut
	    list_raideur.push_front(interv); 
	    raideur = &(*list_raideur.begin());  
      };
	};

// calcul du facteur de pénalisation en pénétration, en fonction de la géométrie
// du module de compressibilité et des différents cas possibles
// sauvegarde du gap
// éventuellement, calcul de la dérivée: d_beta_gapdu, du facteur par rapport au gap
// la sensibilité dépend du type de calcul du facteur de pénalisation
double ElContact::CalFactPenal(const double& gap,double & d_beta_gap,int contact_type)
{ // récup du facteur donné par l'utilisateur
  double fact_penal=0.; // init par défaut
  if (fct_nD_contact.fct_nD_penalisationPenetration != NULL)
       {fact_penal = Valeur_fct_nD(fct_nD_contact.fct_nD_penalisationPenetration
                        ,ElContact::Fct_nD_contact::tqi_fct_nD_penalisationPenetration
                        ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penalisationPenetration
                        ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penalisationPenetration);
       }
  else {fact_penal = ParaGlob::param->ParaAlgoControleActifs().PenalisationPenetrationContact();}
  double facteur=1.; // un facteur
  // choix en fonction du type de calcul
  int typ_cal= ParaGlob::param->ParaAlgoControleActifs().TypeCalculPenalisationPenetration();
  
  // --- premier choix en fonction de typ_cal ---
  if (typ_cal != 0)
     {if (contact_type != 41)
       {switch (typ_cal)
         { case 1: // cas le plus simple : pas de calcul particulier
                 break; // on garde la valeur d'initialisation (pas de modif due à la géométrie et a la compressibilité)
           case 2: case 3: case 4: case 5: case 6: case 7: case 8:// calcul du type ls-dyna avec la compressibilité du maître
             { // on choisit en fonction de la géométrie si il existe une compressibilité
               ElFrontiere* elfrontiere = elfront->Eleme(); // pour simplifier l'écriture
               Element * elem = elfront->PtEI(); // idem
               // on regarde le cas particulier d'un élément dont la loi de comportement est inactive
               const Loi_comp_abstraite* loi = ((const ElemMeca*)  elem)->LoiDeComportement();
               if (Loi_rien(loi->Id_comport()))
                {// on garde l'init par défaut
                 if (Permet_affichage() > 5)
                 cout << "\n contact: *** attention contact avec un element avec une loi rien (pas de module de compressibilite) "
                      << " on n'en tient pas compte (pour l'instant) pour le calcul du facteur de penalite normal ! ";
                }
               // puis on regarde si la compressibilité moyenne existe et est > 0.
               else if (((const ElemMeca*)  elem)->CompressibiliteMoyenne() > 0.)
                { switch (elfrontiere->Type_geom_front())
                    { case SURFACE:
                       if (elem->ElementGeometrie(X1).Dimension()==3)
                        { // cas d'une surface frontière d'un volume
                          double surf = elfrontiere->SurfaceApprox();
                          double volume = elem->Volume();
                          if (Dabs(volume) <= ConstMath::pasmalpetit)
                           { if (Permet_affichage() > 1)
                               cout << "\n *** attention, le volume de l'element " << elem->Num_elt_const() << " du maillage "
                                    << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
                             if (Permet_affichage() > 4)
                               cout << "\n ElContact::CalFactPenal()";
                             facteur = elfrontiere->MaxDiagonale_tdt();
                           }
                          else 	// sinon c'est bon
                           {facteur = surf*surf/volume;}
                          if (Permet_affichage() > 4)
                            cout << "\n SURFACE d'un volume: facteur = "<< facteur;
                        }
                       else if (elem->ElementGeometrie(X1).Dimension()==2)
                        { // cas d'une surface frontière d'un élément coque ou plaque
                          double surf = elfrontiere->SurfaceApprox();
                          double maxdiag = elfrontiere->MaxDiagonale_tdt();
                          if (maxdiag <= ConstMath::pasmalpetit)
                           { if (Permet_affichage() > 1)
                               cout << "\n *** attention, la surface de l'element " << elem->Num_elt_const() << " du maillage "
                                    << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
                             if (Permet_affichage() > 4)
                               cout << "\n ElContact::CalFactPenal()";
                             facteur = maxdiag;
                           }
                          else 	// sinon c'est bon
                           {facteur = surf/maxdiag;}
                          if (Permet_affichage() > 4)
                           cout << "\n SURFACE d'une coque ou plaque: facteur = "<< facteur;
                        };
                       break;

         // pour mémoire pour la suite POINT_G = 1 , LIGNE , SURFACE, VOLUME, RIEN_TYPE_GEOM
                      case LIGNE:
                       // --- on traite les cas particuliers des éléments d'angle mort
                       if (elfront->Angle_mort())
                        {
// il n'y a pas de notion de surface de contact et l'idée est de récupérer
//                          // une grandeur du type module de compressibilité * une longueur caractéristique de l'élément
//                          // on retient une expression simple
//                          double longueur = elem->LongueurGeometrique_caracteristique(1);
//                          facteur = longueur;
//                          if (Permet_affichage() > 4)
//                             cout << "\n angle mort: facteur1 = "<< facteur;
//                       //%%%% essai %%%%
//                         longueur = elem->LongueurGeometrique_caracteristique(1);
//                         double volume  = elem->Volume();
//                         facteur = volume / (longueur * longueur);
//                         if (Permet_affichage() > 4)
//                            cout << "\n angle mort: facteur2 = "<< facteur;
//
//                        //%%%% fin essai %%%%
                        
                        double longueur = elem->LongueurGeometrique_caracteristique(1);
                        double volume = elem->Volume();
                        if (Dabs(volume) <= ConstMath::pasmalpetit)
                         { if (Permet_affichage() > 1)
                             cout << "\n *** attention, le volume de l'element " << elem->Num_elt_const() << " du maillage "
                                  << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
                           if (Permet_affichage() > 4)
                             cout << "\n ElContact::CalFactPenal()";
                           facteur = elfrontiere->MaxDiagonale_tdt();
                         }
                        else  // sinon c'est bon
                         {facteur = volume/longueur/longueur;};
                        if (Permet_affichage() > 4)
                          cout << "\n LIGNE angle mort: facteur = "<< facteur;
                        }
                       else
                        {// --- cas d'un élément de contact autre que d'angle mort
                         if (ParaGlob::AxiSymetrie())
                          // cas où l'on est en axisymétrique,
                          { // on regarde la dimension de l'élément associé
                            if (elem->ElementGeometrie(X1).Dimension()==2)
                              // élément 2D en axi donc 3D en réalité
                              { // récup de la longueur de la frontière
           //                     double longueur = elfrontiere->LongueurApprox();
                                // on calcul la surface associée
                                // M_impact(1) = x donc = le rayon car on est axi autour de y
           ////                     double surf = longueur * M_impact(1) * ConstMath::Pi * 2.;
           // 9 nov 2016: je crois qu'ici il y a une incompréhension. en fait il s'agit de la surface du maître
           // donc il ne faut pas tenir compte du point d'impact ?? sinon par exemple si le point est au centre
           // M_impact(1) = 0 et on n'a pas de surf !!
           // mais plus généralement, le pt d'impact ne doit pas interférer à mon avis
           // 3 fevr 2023 : c'est incohérent, car surf n'a pas la dimension d'une surface
           // du coup avec le volume qui est vraiment de dim 3 on aura une grandeur non cohérente
           // par rapport à la formule de base                     double surf = longueur * ConstMath::Pi * 2.;
           // du coup on va utiliser la surface de l'élément
                             #ifdef MISE_AU_POINT
                                if (!(elem->SurfExiste(1)))
                                  {cout << "\n *** erreur la surface l'element axi " << elem->Num_elt_const() << " du maillage "
                                       << elem->Num_maillage() << " n'est pas disponible, on stop ";
                                   Sortie(1);
                                  };
                             #endif

                                // on récupère la surface de l'élément qui a créé la frontière
                                ElFrontiere* const  elfro_elem = elem->Frontiere_surfacique(1,false);
                                double surf = elfro_elem->SurfaceApprox(); // là on a bien la surface approximative de l'élément de surface axi
                                double longueur = elfrontiere->MaxDiagonale_tdt();
                                facteur = surf / longueur;
                                // comme on est en axisymétrie, la force résultante doit-être multipliée par la circonférence du point d'application
                                // pour être cohérente avec les forces internes de réaction des éléments maîtres, qui eux sont relatives à des volumes
                                // pour maximiser la force: on utilise la coordonnée x maxi des noeuds de l'élément maître, et on évite la coordonnée x du point
                                // d'impact qui est nulle pour le centre par exemple
                                double maxi_x1 = elem->Maxi_xi_noeud(1, TEMPS_t);// on se contente de la valeur à t, car il s'agit d'une approximation et cela évite l'instabilité
                                facteur *= maxi_x1 * ConstMath::Pi * 2.; // on se contente de la valeur à t, car il s'agit d'une approximation et cela évite l'instabilité
                                
//
//
//
//                                double volume  = elem->Volume(); // là il s'agit du volume totale
//                                if (Dabs(volume) <= ConstMath::pasmalpetit)
//                                 { if (Permet_affichage() > 1)
//                                     cout << "\n *** attention, le volume de l'element " << elem->Num_elt_const() << " du maillage "
//                                          << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
//                                   if (Permet_affichage() > 4)
//                                     cout << "\n ElContact::CalFactPenal()";
//                                   facteur = elfrontiere->MaxDiagonale_tdt();
//                                 }
//                                else 	// sinon c'est bon
//                                 {facteur = surf*surf/volume;}
                                 
                                if (Permet_affichage() > 4)
                                  cout << "\n LIGNE en axi: facteur = "<< facteur
                                       << ", surface= " << surf <<", longueur= " << longueur;
                                 
                              }
                            else // sinon ce n'est pas pris en charge
                              {cout << "\n *** erreur: cas non pris en charge pour l'instant: "
                                    << " type de frontiere : " << Nom_type_geom(elfrontiere->Type_geom_front())
                                    << " contact sur une ligne axisyme trique"
                                    << " \n ElContact::CalFactPenal() ";
                                Sortie(1);
                              };
                          }
                         else if (elem->ElementGeometrie(X1).Dimension()==3) // cas d'une ligne frontière d'un élément volumique
                          { double longueur = elem->LongueurGeometrique_caracteristique(2); // modif 11 oc2024 (1);
                            double volume = elem->Volume();
                            if (Dabs(volume) <= ConstMath::pasmalpetit)
                             { if (Permet_affichage() > 1)
                                 cout << "\n *** attention, le volume de l'element " << elem->Num_elt_const() << " du maillage "
                                      << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
                               if (Permet_affichage() > 4)
                                 cout << "\n ElContact::CalFactPenal()";
                               facteur = elfrontiere->MaxDiagonale_tdt();
                             }
                            else 	// sinon c'est bon
                             {facteur = volume/longueur/longueur;}
                            if (Permet_affichage() > 4)
                              cout << "\n LIGNE d'un volume: facteur = "<< facteur;
                          }
                         else if ((elem->ElementGeometrie(X1).Dimension()==2) && (!ParaGlob::AxiSymetrie()))
                               // ou cas d'une ligne frontière d'une plaque en non axi
                           {double longueur = elem->LongueurGeometrique_caracteristique(2);
                            double volume = elem->Volume();
                            double epais = ((ElemMeca*)  elem)->EpaisseurMoyenne(TEMPS_tdt );
                            double surf = volume / epais;
                            facteur = surf/longueur;
                            if (Permet_affichage() > 4)
                              cout << "\n LIGNE d'une plaque (non axi): facteur = "<< facteur;
                           }
                         else if ((elem->ElementGeometrie(X1).Dimension()==2)
                                  && (ParaGlob::AxiSymetrie())
                                 )
                           // cas d'une ligne frontière d'une plaque en axi
                          { double longueur = elfrontiere->LongueurApprox();
                            double epais = 1.;
                            double surf = elem->Volume() / epais;
                            facteur = surf/longueur;
                            if (Permet_affichage() > 4)
                               cout << "\n LIGNE d'une plaque en axi : facteur = "<< facteur;
                          };
                         };
                       break;
                      case POINT_G:
                       // --- on traite les cas particuliers des éléments d'angle mort
                       if (elfront->Angle_mort())
                        {
//                        // il n'y a pas de notion de surface de contact et l'idée est de récupérer
//                          // une grandeur du type module de compressibilité * une longueur caractéristique de l'élément
//                          // on retient une expression simple
//                          double longueur = elem->LongueurGeometrique_caracteristique(1);
//                          facteur = longueur;
//                          if (Permet_affichage() > 4)
//                             cout << "\n angle mort: facteur1 = "<< facteur;
//                       //%%%% essai %%%%
//                         double volume  = elem->Volume();
//                         facteur = volume / (longueur * longueur);
//                        //%%%% fin essai %%%%
//                         if (Permet_affichage() > 4)
//                            cout << "\n angle mort: facteur2 = "<< facteur;
                         double longueur = elem->LongueurGeometrique_caracteristique(1);
                         double volume = elem->Volume();
                         if (Dabs(volume) <= ConstMath::pasmalpetit)
                          { if (Permet_affichage() > 1)
                              cout << "\n *** attention, le volume de l'element " << elem->Num_elt_const() << " du maillage "
                                   << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
                            if (Permet_affichage() > 4)
                              cout << "\n ElContact::CalFactPenal()";
                            facteur = elfrontiere->MaxDiagonale_tdt();
                          }
                         else  // sinon c'est bon
                          {facteur = volume/longueur/longueur;};
                         if (Permet_affichage() > 4)
                            cout << "\n POINT_G angle mort: facteur = "<< facteur
                                 << ", volume = " << volume << ", longueur = " << longueur;
                        }
                       else
                        { // --- cas d'un élément de contact autre que d'angle mort
                          // on ne considère que le cas d'une dimension 1D non axi, qui est un cas d'école
                          // pour les autres cas, on considère que l'on ne doit pas prendre en compte des frontières
                          // de type point, du coup on génèrera une erreur
                          if (ParaGlob::Dimension() == 1 )
                           {// on veut calculer Ke * Ae * Ae / vol , comme on est dans le cas d'une barre
                            // vol = long * section, et Ae = section , du coup:
                            // Ke * Ae * Ae / vol = Ke * vol / (long * long) avec long = la longueur de l'élément
                            double longueur = elem->LongueurGeometrique_caracteristique(1);
                            double volume  = elem->Volume();
                            facteur = volume / (longueur * longueur);
                            if (Permet_affichage() > 4)
                               cout << "\n POINT_G en dim espace 1D: facteur = "<< facteur
                                    << ", volume = " << volume << ", longueur = " << longueur;
                           }
                          else
                           {cout << "\n *** erreur: cas non pris en charge pour l'instant: "
                                 << " type de frontiere : " << Nom_type_geom(elfrontiere->Type_geom_front())
                                 << "\n contact en dimension " << ParaGlob::Dimension()
                                 << " \n ElContact::CalFactPenal() ";
                             Sortie(1);
                           };
                        }
                       break;

                    default:
                      cout << "\n *** erreur: cas non pris en charge pour l'instant: "
                           << " type de frontiere : " << Nom_type_geom(elfrontiere->Type_geom_front())
                           << " \n ElContact::CalFactPenal() ";
                      Sortie(1);
                    };
                  // on tiens compte maintenant de la compressibilité (moyenne) de l'élément
                  double compmoyenne = ((const ElemMeca*)  elem)->CompressibiliteMoyenne();
//***** à adapter dans le cas d'un élément avec une loi rien !!
                  facteur *= ((const ElemMeca*)  elem)->CompressibiliteMoyenne();
                }// fin du test d'existance d'une compressibilité
               else // cas ou pour l'instant on n'a pas de compressibilité moyenne (mais ce n'est pas une loi rien !!)
                // on n'interdit pas car on pourrait avoir plusieurs maîtres, certains bien définit et
                // d'autres sans comportement donc sans compressibilité:
                {if (Permet_affichage() >5)
                   cout << "\n contact: *** attention le module de compressibilite n'est (encore?) pas defini "
                        << " on n'en tient pas compte (pour l'instant) pour le calcul du facteur de penalite normal ! ";
                };
               break;
             }
           default:
             cout << "\n **** erreur cas de calcul du facteur de penalisation en penetration non existant "
                  << " typ_cal= " << typ_cal
                  << "\n  ElContact::CalFactPenal()";
             Sortie(1);
         };
        // --- deuxième choix en fonction de typ_cal ---
        // on calcul la valeur final du facteur
        switch (typ_cal)
         { case 1: case 2:
            { fact_penal *= facteur;d_beta_gap = 0.;
              if (Permet_affichage() >= 5)
                 cout << "\n fact_penal= " << fact_penal ;
              break;
            }
           case 3:  // on fait varier le coef de pénalisation en fonction de l'enfoncement
            { if ((gap < gap_t) && (gap < 0.))  // la première fois gap_t = 0. donc on a forcément nb_pene_tdt >= 1 après le test
                { nb_pene_tdt++;}
              else if ((gap > gap_t ) && (gap < 0.)) // on diminue que si gap diminue,
                { if (nb_pene_tdt > 1) // on limite à 1 pour le calcul correcte de la puissance
                   { nb_pene_tdt--;
      //     	       cout << "\n ************* on diminue np_pene_tdt *************** ";
                   };
                };
              // l'évolution  du facteur prend en compte le nombre de fois où la pénétration augmente
              // 2**10 = 10^2, 2**100 = 10^30 !!
              const double a = 1.2; const double b=2.;
              fact_penal *= pow(a,nb_pene_tdt*b); //PUISSN(2.,nb_pene_tdt); // on modifie en conséquence le facteur
              d_beta_gap = 0.; // a priori on n'a pas vraiment de dérivée calculable ...
              if (Permet_affichage() >= 5)
                cout << "\n fact_penal= " << fact_penal << "  nb_pene_tdt= "
                     << nb_pene_tdt << " pow("<<a<<",nb_pene_tdt*"<<b<<") " <<pow(a,nb_pene_tdt*b);
              break;
            }
           case 4:     // on fait varier le coef de pénalisation lorsque l'enfoncement est plus petit qu'un maxi
            { // ON récupe la pénétration maxi
              double borne_regularisation;
              if (fct_nD_contact.fct_nD_penetration_borne_regularisation != NULL)
                   {borne_regularisation = Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_borne_regularisation);
                   }
              else {borne_regularisation = ParaGlob::param->ParaAlgoControleActifs().Penetration_borne_regularisation();}
              double max_pene;
              if (fct_nD_contact.fct_nD_penetration_contact_maxi != NULL)
                   {max_pene = Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_contact_maxi
                                  ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_contact_maxi
                                  ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_contact_maxi
                                  ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_contact_maxi);
                   }
              else {max_pene = ParaGlob::param->ParaAlgoControleActifs().Penetration_contact_maxi();}
              // on calcul un facteur de majoration en fonction de la pénétration du pas précédent
      //        double mult_pene = MaX(1., -gap_t/max_pene);
              double mult_pene = MaX(1., (-gap_t/max_pene)*mult_pene_t); // il faut appliquer au facteur précédent !
              mult_pene_tdt = 0.5*(mult_pene_t + mult_pene); // on fait une moyenne glissante de 2
      //////debug
      //if (mult_pene_tdt == 1)
      // {cout << "\n debug ElContact::CalFactPenal()  temps " << ParaGlob::Variables_de_temps().TempsCourant();
      // };
      ////fin debug
              if (Permet_affichage() > 5)
                cout << "\n mult_pene_tdt= " << mult_pene_tdt ;
              // on calcul un facteur en fonction de la distance: thèse dominique chamoret, page 94 formule (3.3)
              if (gap <= (-borne_regularisation))
                { fact_penal *= facteur*mult_pene_tdt;d_beta_gap = 0.;}
              else if (Dabs(gap) < borne_regularisation) // borne_regularisation est positif, donc on continue à avoir une pénalisation pour une pénétration positive !!
      //			   { fact_penal *= facteur * ((gap*0.5/borne_regularisation + 1.)*gap*0.5+borne_regularisation*0.25); // formule fausse de marmoret
                { fact_penal *= facteur *mult_pene_tdt * Sqr((gap-borne_regularisation)/borne_regularisation) * 0.25; // formule du même type
                  d_beta_gap = facteur *mult_pene_tdt * (gap-borne_regularisation)/borne_regularisation/borne_regularisation * 0.5;;
                }
              else
                { fact_penal = 0;d_beta_gap = 0.;};
              break;
            }
           case 5:case 7:  // on fait varier le coef de pénalisation lorsque l'enfoncement est plus petit qu'un maxi
            { // ON récupe la pénétration maxi
              double borne_regularisation;
              if (fct_nD_contact.fct_nD_penetration_borne_regularisation != NULL)
                   {borne_regularisation = Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_borne_regularisation);
                   }
              else {borne_regularisation = ParaGlob::param->ParaAlgoControleActifs().Penetration_borne_regularisation();}
              // on calcul un facteur en fonction de la distance: thèse dominique chamoret, page 94 formule (3.3)
              if (gap <= (-borne_regularisation))
                {fact_penal *= facteur;d_beta_gap = 0.;}
              else if (Dabs(gap) < borne_regularisation) // borne_regularisation est positif, donc on continue à avoir une pénalisation pour une pénétration positive !!
      //			   { fact_penal *= facteur * ((gap*0.5/borne_regularisation + 1.)*gap*0.5+borne_regularisation*0.25); // formule fausse de marmoret
                { fact_penal *= facteur * Sqr((gap-borne_regularisation)/borne_regularisation) * 0.25; // formule du même type
                  d_beta_gap = facteur * (gap-borne_regularisation)/borne_regularisation/borne_regularisation * 0.5;;
                }
              else
                { fact_penal = 0;d_beta_gap = 0.;};
              break;
            }
           case 6: // idem cas 5 mais avec une fonction différente
            { // on récupère la pénétration maxi
              double borne_regularisation;
              if (fct_nD_contact.fct_nD_penetration_borne_regularisation != NULL)
                   {borne_regularisation = Dabs(Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_borne_regularisation));
                   }
              else {borne_regularisation = Dabs(ParaGlob::param->ParaAlgoControleActifs().Penetration_borne_regularisation());}
              // on calcul un facteur en fonction de la distance: = (0.5*(tanh(4.*gap/borne)-1.))
              // 0.25*exp(-gap/(0.25*borne))
              if (gap <= (-borne_regularisation))
                {fact_penal *= facteur;d_beta_gap = 0.;}
              else if (Dabs(gap) < borne_regularisation)
                { fact_penal *= -facteur * 0.5 * (tanh(gap*4./borne_regularisation)-1.); // formule du même type
                  d_beta_gap = 4.*(1.-fact_penal*fact_penal)/borne_regularisation; //
                }
              else
                { fact_penal = 0;d_beta_gap = 0.;};
              break;
            }
           case 8:  // idem 4 mais pour un contact collant: quelque soit le sens de gap
           // on fait varier le coef de pénalisation lorsque l'enfoncement est plus petit qu'un maxi
            {
    //  //------ essai à virer
    //  fact_penal *= facteur;d_beta_gap = 0.;
    //          if (Permet_affichage() >= 5)
    //             cout << "\n fact_penal= " << fact_penal ;
    //          break;
    //  //------ fin essai
            
              // ON récupe la pénétration maxi
              double borne_regularisation;
              if (fct_nD_contact.fct_nD_penetration_borne_regularisation != NULL)
                   {borne_regularisation = Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_borne_regularisation
                                                         ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_borne_regularisation);
                   }
              else {borne_regularisation = ParaGlob::param->ParaAlgoControleActifs().Penetration_borne_regularisation();}
              double max_pene;
              if (fct_nD_contact.fct_nD_penetration_contact_maxi != NULL)
                   {max_pene = Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_contact_maxi
                                     ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_contact_maxi
                                     ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_contact_maxi
                                     ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_contact_maxi);
                   }
              else {max_pene = ParaGlob::param->ParaAlgoControleActifs().Penetration_contact_maxi();}
              // on calcul un facteur de majoration en fonction de la pénétration du pas précédent
      //        double mult_pene = MaX(1., -gap_t/max_pene);
              double mult_pene = MaX(1., (Dabs(gap_t)/max_pene)*mult_pene_t); // il faut appliquer au facteur précédent !
              mult_pene_tdt = 0.5*(mult_pene_t + mult_pene); // on fait une moyenne glissante de 2
      //////debug
      //if (mult_pene_tdt == 1)
      // {cout << "\n debug ElContact::CalFactPenal()  temps " << ParaGlob::Variables_de_temps().TempsCourant();
      // };
      ////fin debug
              if (Permet_affichage() > 5)
                cout << "\n mult_pene_tdt= " << mult_pene_tdt ;
              // on calcul un facteur en fonction de la distance: thèse dominique chamoret, page 94 formule (3.3)
              if (Dabs(gap) >  borne_regularisation)
                { fact_penal *= facteur*mult_pene_tdt;d_beta_gap = 0.;}
              else  // borne_regularisation est positif, donc on continue à avoir une pénalisation pour une pénétration positive !!
      //      { fact_penal *= facteur * ((gap*0.5/borne_regularisation + 1.)*gap*0.5+borne_regularisation*0.25); // formule fausse de marmoret
                { fact_penal *= facteur *mult_pene_tdt * Sqr((-Dabs(gap)-borne_regularisation)/borne_regularisation) * 0.25; // formule du même type
                  d_beta_gap = facteur *mult_pene_tdt * (-Dabs(gap)-borne_regularisation)/borne_regularisation/borne_regularisation * 0.5;;
                }
              break;
            }

           default:
            cout << "\n **** erreur 2 cas de calcul du facteur de penalisation en penetration non existant "
                 << " typ_cal= " << typ_cal
                 << "\n  ElContact::CalFactPenal()";
            Sortie(1);
         };
       }
      else if (contact_type == 41)// si == 41, on utilise une mise à jour de la pénalisation
       { // la méthode est analogue au cas: typ_cal == 4
         // ON récupe la pénétration maxi
         double borne_regularisation;
         if (fct_nD_contact.fct_nD_penetration_borne_regularisation != NULL)
              {borne_regularisation = Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_borne_regularisation
                                                    ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_borne_regularisation
                                                    ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_borne_regularisation
                                                    ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_borne_regularisation);
              }
         else {borne_regularisation = ParaGlob::param->ParaAlgoControleActifs().Penetration_borne_regularisation();}
         double max_pene;
         if (fct_nD_contact.fct_nD_penetration_contact_maxi != NULL)
              {max_pene = Valeur_fct_nD(fct_nD_contact.fct_nD_penetration_contact_maxi
                              ,ElContact::Fct_nD_contact::tqi_fct_nD_penetration_contact_maxi
                              ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penetration_contact_maxi
                              ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penetration_contact_maxi);
              }
         else {max_pene = ParaGlob::param->ParaAlgoControleActifs().Penetration_contact_maxi();}
         // on calcul un facteur de majoration en fonction de la pénétration du pas précédent
         double mult_pene = MaX(1., (-gap_t/max_pene)); // il faut appliquer au facteur précédent !
         if (Permet_affichage() > 5)
           cout << "\n mult_pene_tdt= " << mult_pene ;
         // on calcul un facteur en fonction de la distance:
    //     if (gap <= (-borne_regularisation))
           { fact_penal *= penalisation * mult_pene;d_beta_gap = 0.;}
    //     else if (Dabs(gap) < borne_regularisation) // borne_regularisation est positif, donc on continue à avoir une pénalisation pour une pénétration positive !!
    //        { fact_penal *= penalisation * mult_pene * Sqr((gap-borne_regularisation)/borne_regularisation) * 0.25; // formule du même type
    //          d_beta_gap = penalisation * mult_pene * (gap-borne_regularisation)/borne_regularisation/borne_regularisation * 0.5;;
    //        }
    //     else // sinon on annule tout
    //        { fact_penal = 0;d_beta_gap = 0.;};
       }
      else if (contact_type == 42)// si == 41, on utilise une mise à jour de la pénalisation
       {
       
       };
     };

  // retour
  return fact_penal;
};


// calcul du facteur de pénalisation en tangentiel, en fonction de la géométrie
// du module de compressibilité et des différents possibles
// sauvegarde du dep_T = déplacement tangentiel
// éventuellement, calcul de la dérivée: d_beta_dep_Tdu, du facteur par rapport au dep_T
// la sensibilité dépend du type de calcul du facteur de pénalisation
// suis la même logique que pour la pénétration
double ElContact::CalFactPenalTangentiel(const double& dep_T,double & d_beta_dep_T)
{ // récup du facteur donné par l'utilisateur
  double fact_penal = 0.; // init par défaut
  if (fct_nD_contact.fct_nD_penalisationTangentielle != NULL)
       {fact_penal = Valeur_fct_nD(fct_nD_contact.fct_nD_penalisationTangentielle
                          ,ElContact::Fct_nD_contact::tqi_fct_nD_penalisationTangentielle
                          ,ElContact::Fct_nD_contact::tqi_const_fct_nD_penalisationTangentielle
                          ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_penalisationTangentielle);
       }
  else {fact_penal = ParaGlob::param->ParaAlgoControleActifs().PenalisationTangentielleContact();}
  double facteur=1.; // un facteur
  // choix en fonction du type de calcul
  int typ_cal= ParaGlob::param->ParaAlgoControleActifs().TypeCalculPenalisationTangentielle();
  // --- premier choix en fonction de typ_cal ---
  if (typ_cal != 0)
   {switch (typ_cal)
     { case 1: // cas le plus simple : pas de calcul particulier
             break; // on garde la valeur d'initialisation (pas de modif due à la géométrie et a la compressibilité)
       case 2: case 3: case 4: case 5: case 6: case 7: case 8:// calcul du type ls-dyna avec la compressibilité du maître
         { // on choisit en fonction de la géométrie
           ElFrontiere* elfrontiere = elfront->Eleme(); // pour simplifier l'écriture
           Element * elem = elfront->PtEI(); // idem
           // on regarde le cas particulier d'un élément dont la loi de comportement est inactive
           const Loi_comp_abstraite* loi = ((const ElemMeca*)  elem)->LoiDeComportement();
           if (Loi_rien(loi->Id_comport()))
            {// on garde l'init par défaut
             if (Permet_affichage() > 5)
             cout << "\n contact: *** attention contact avec un element avec une loi rien (pas de module de compressibilite) "
                  << " on n'en tient pas compte (pour l'instant) pour le calcul du facteur de penalite tangentiel ! ";
            }
           // puis on regarde si la compressibilité moyenne existe et est > 0.
           else if (((const ElemMeca*)  elem)->CompressibiliteMoyenne() > 0.)
            { switch (elfrontiere->Type_geom_front())
                { case SURFACE:
                if (elem->ElementGeometrie(X1).Dimension()==3)
                 { // cas d'une surface frontière d'un volume
                   double surf = elfrontiere->SurfaceApprox();
                   double volume = elem->Volume();
                   if (Dabs(volume) <= ConstMath::pasmalpetit)
                    { if (Permet_affichage() > 1)
                        cout << "\n *** attention, le volume de l'element " << elem->Num_elt_const() << " du maillage "
                             << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
                      if (Permet_affichage() > 4)
                        cout << "\n ElContact::CalFactPenalTangentiel()";
                      facteur = elfrontiere->MaxDiagonale_tdt();
                    }
                   else  // sinon c'est bon
                    {facteur = surf*surf/volume;}
                   if (Permet_affichage() > 4)
                    cout << "\n SURFACE d'un volume: facteur = "<< facteur;
                 }
                else if (elem->ElementGeometrie(X1).Dimension()==2)
                 { // cas d'une surface frontière d'un élément coque ou plaque
                   double surf = elfrontiere->SurfaceApprox();
                   double maxdiag = elfrontiere->MaxDiagonale_tdt();
                   if (maxdiag <= ConstMath::pasmalpetit)
                    { if (Permet_affichage() > 1)
                        cout << "\n *** attention, la surface de l'element " << elem->Num_elt_const() << " du maillage "
                             << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
                      if (Permet_affichage() > 4)
                        cout << "\n ElContact::CalFactPenalTangentiel()";
                      facteur = maxdiag;
                    }
                   else  // sinon c'est bon
                    {facteur = surf/maxdiag;}
                   if (Permet_affichage() > 4)
                     cout << "\n SURFACE d'une coque ou plaque: facteur = "<< facteur;
                 };
                break;
  // pour mémoire pour la suite POINT_G = 1 , LIGNE , SURFACE, VOLUME, RIEN_TYPE_GEOM
               case LIGNE:
                // --- on traite les cas particuliers des éléments d'angle mort
                if (elfront->Angle_mort())
                 { // il n'y a pas de notion de surface de contact et l'idée est de récupérer
                   // une grandeur du type module de compressibilité * une longueur caractéristique de l'élément
                   // on retient une expression simple
                   double longueur = elem->LongueurGeometrique_caracteristique(1);
//                   facteur = longueur;
//                   longueur = elem->LongueurGeometrique_caracteristique(1);
                   double volume  = elem->Volume();

                   if (Dabs(volume) <= ConstMath::pasmalpetit)
                    { if (Permet_affichage() > 1)
                        cout << "\n *** attention, le volume de l'element " << elem->Num_elt_const() << " du maillage "
                             << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
                      if (Permet_affichage() > 4)
                        cout << "\n ElContact::CalFactPenalTangentiel()";
                      facteur = elfrontiere->MaxDiagonale_tdt();
                    }
                   else  // sinon c'est bon
                    {facteur = volume/longueur/longueur;};
                   if (Permet_affichage() > 4)
                     cout << "\n LIGNE angle mort: facteur = "<< facteur;
                 }
                else
                 {// --- cas d'un élément de contact autre que d'angle mort
                  if (ParaGlob::AxiSymetrie())
                   // cas où l'on est en axisymétrique,
                   { // on regarde la dimension de l'élément associé
                     if (elem->ElementGeometrie(X1).Dimension()==2)
                       // élément 2D en axi donc 3D en réalité
                       { // récup de la longueur de la frontière
 //                     double longueur = elfrontiere->LongueurApprox();
                      // on calcul la surface associée
                      // M_impact(1) = x donc = le rayon car on est axi autour de y
 ////                     double surf = longueur * M_impact(1) * ConstMath::Pi * 2.;
 // 9 nov 2016: je crois qu'ici il y a une incompréhension. en fait il s'agit de la surface du maître
 // donc il ne faut pas tenir compte du point d'impact ?? sinon par exemple si le point est au centre
 // M_impact(1) = 0 et on n'a pas de surf !!
 // mais plus généralement, le pt d'impact ne doit pas interférer à mon avis
 // 3 fevr 2023 : c'est incohérent, car surf n'a pas la dimension d'une surface
 // du coup avec le volume qui est vraiment de dim 3 on aura une grandeur non cohérente
 // par rapport à la formule de base                     double surf = longueur * ConstMath::Pi * 2.;
 // du coup on va utiliser la surface de l'élément
                    #ifdef MISE_AU_POINT
                         if (!(elem->SurfExiste(1)))
                           {cout << "\n *** erreur la surface l'element axi " << elem->Num_elt_const() << " du maillage "
                                << elem->Num_maillage() << " n'est pas disponible, on stop ";
                            Sortie(1);
                           };
                    #endif

                         // on récupère la surface de l'élément qui a créé la frontière
                         ElFrontiere* const  elfro_elem = elem->Frontiere_surfacique(1,false);
                         double surf = elfro_elem->SurfaceApprox(); // là on a bien la surface approximative de l'élément de surface axi
                         double longueur = elfrontiere->MaxDiagonale_tdt();
                         facteur = surf / longueur;
                         // comme on est en axisymétrie, la force résultante doit-être multipliée par la circonférence du point d'application
                         // pour être cohérente avec les forces internes de réaction des éléments maîtres, qui eux sont relatives à des volumes
                         // pour maximiser la force: on utilise la coordonnée x maxi des noeuds de l'élément maître, et on évite la coordonnée x du point
                         // d'impact qui est nulle pour le centre par exemple
                         double maxi_x1 = elem->Maxi_xi_noeud(1, TEMPS_t);// on se contente de la valeur à t, car il s'agit d'une approximation et cela évite l'instabilité
                         facteur *= maxi_x1 * ConstMath::Pi * 2.; // on se contente de la valeur à t, car il s'agit d'une approximation et cela évite l'instabilité
                                                                                         
                         if (Permet_affichage() > 4)
                           cout << "\n LIGNE en axi: facteur = "<< facteur
                                << ", surface= " << surf <<", longueur= " << longueur;
            
                   }

//                       { // récup de la longueur de la frontière
//                         double longueur = elfrontiere->LongueurApprox();
//                         // on calcul la surface associée
//                         double surf = longueur * ConstMath::Pi * 2.;
//                         // on récupère la surface de l'élément qui a créé la frontière
//                         double volume  = elem->Volume();
//                         if (Dabs(volume) <= ConstMath::pasmalpetit)
//                          { if (Permet_affichage() > 1)
//                              cout << "\n *** attention, le volume de l'element " << elem->Num_elt_const() << " du maillage "
//                                   << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
//                            if (Permet_affichage() > 4)
//                              cout << "\n ElContact::CalFactPenalTangentiel()";
//                            facteur = elfrontiere->MaxDiagonale_tdt();
//                          }
//                         else  // sinon c'est bon
//                          {facteur = surf*surf/volume;}
//                       }
                       
                     else // sinon ce n'est pas pris en charge
                       {cout << "\n *** erreur: cas non pris en charge pour l'instant: "
                             << " type de frontiere : " << Nom_type_geom(elfrontiere->Type_geom_front())
                             << " contact sur une ligne axisyme trique"
                             << " \n ElContact::CalFactPenalTangentiel() ";
                         Sortie(1);
                       };
                   }
                  else if (elem->ElementGeometrie(X1).Dimension()==3)
                    // cas d'une ligne frontière d'un élément coque ou plaque
                       // cas d'une ligne frontière d'un élément volumique
                      { double longueur = elem->LongueurGeometrique_caracteristique(1);
                        double volume = elem->Volume();
                        if (Dabs(volume) <= ConstMath::pasmalpetit)
                         { if (Permet_affichage() > 1)
                             cout << "\n *** attention, le volume de l'element " << elem->Num_elt_const() << " du maillage "
                                  << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
                           if (Permet_affichage() > 4)
                             cout << "\n ElContact::CalFactPenalTangentiel()";
                           facteur = elfrontiere->MaxDiagonale_tdt();
                         }
                        else  // sinon c'est bon
                         {facteur = volume/longueur/longueur;}
                        if (Permet_affichage() > 4)
                          cout << "\n LIGNE d'un volume: facteur = "<< facteur;
                      }
                     else if (elem->ElementGeometrie(X1).Dimension()==2)
                       // cas d'une ligne frontière d'une plaque
                      { double longueur = elfrontiere->LongueurApprox();
                        double epais = 0.; // init
                        if (!ParaGlob::AxiSymetrie()) // si on n'est pas en axi
                          {epais = ((ElemMeca*)  elem)->EpaisseurMoyenne(TEMPS_tdt );}
                        else // si on est en axi
                          {epais = 1.;};
                        double surf = elem->Volume() / epais;
                        facteur = surf/longueur;
                        if (Permet_affichage() > 4)
                           cout << "\n LIGNE d'une plaque: facteur = "<< facteur;
                      };
                     };
                   break;
               case POINT_G:
                // --- on traite les cas particuliers des éléments d'angle mort
                if (elfront->Angle_mort())
                  {
                   double longueur = elem->LongueurGeometrique_caracteristique(1);
                   double volume = elem->Volume();
                   if (Dabs(volume) <= ConstMath::pasmalpetit)
                    { if (Permet_affichage() > 1)
                        cout << "\n *** attention, le volume de l'element " << elem->Num_elt_const() << " du maillage "
                             << elem->Num_maillage() << " est nul, on utilise le max de diagonal ";
                      if (Permet_affichage() > 4)
                        cout << "\n ElContact::CalFactPenalTangentiel()";
                      facteur = elfrontiere->MaxDiagonale_tdt();
                    }
                   else  // sinon c'est bon
                    {facteur = volume/longueur/longueur;};
                   if (Permet_affichage() > 4)
                      cout << "\n POINT_G angle mort: facteur = "<< facteur
                           << ", volume = " << volume << ", longueur = " << longueur;
                  }
                else
                 { // --- cas d'un élément de contact autre que d'angle mort
                   // on ne considère que le cas d'une dimension 1D non axi, qui est un cas d'école
                   // pour les autres cas, on considère que l'on ne doit pas prendre en compte des frontières
                   // de type point, du coup on génèrera une erreur
                   if (ParaGlob::Dimension() == 1 )
                    {// on veut calculer Ke * Ae * Ae / vol , comme on est dans le cas d'une barre
                     // vol = long * section, et Ae = section , du coup:
                     // Ke * Ae * Ae / vol = Ke * vol / (long * long) avec long = la longueur de l'élément
                     double longueur = elem->LongueurGeometrique_caracteristique(1);
                     double volume  = elem->Volume();
                     facteur = volume / (longueur * longueur);
                     if (Permet_affichage() > 4)
                        cout << "\n POINT_G en dim espace 1D: facteur = "<< facteur
                             << ", volume = " << volume << ", longueur = " << longueur;
                    }
                   else
                    {cout << "\n *** erreur: cas non pris en charge pour l'instant: "
                          << " type de frontiere : " << Nom_type_geom(elfrontiere->Type_geom_front())
                          << "\n contact en dimension " << ParaGlob::Dimension()
                          << " \n ElContact::CalFactPenalTangentiel() ";
                      Sortie(1);
                    };
                  };
                break;

             default:
               cout << "\n *** erreur: cas non pris en charge pour l'instant: "
                    << " type de frontiere : " << Nom_type_geom(elfrontiere->Type_geom_front())
                    << " \n ElContact::CalFactPenalTangentiel() ";
               Sortie(1);
             };
              // on tiens compte maintenant de la compressibilité (moyenne) de l'élément
              facteur *= ((const ElemMeca*)  elem)->CompressibiliteMoyenne();
            }// fin du test d'existance d'une compressibilité
           else
            // on n'interdit pas car on pourrait avoir plusieurs maîtres, certains bien définit et
            // d'autres sans comportement donc sans compressibilité:
            {if (Permet_affichage() >5)
                 cout << "\n contact: *** attention le module de compressibilite n'est (encore?) pas defini "
                      << " on n'en tient pas compte (pour l'instant) pour le calcul du facteur de penalite tangentiel ! ";
            };
           break;
         }
       default:
         cout << "\n **** erreur cas de calcul du facteur de penalisation tangentielle non existant "
              << " typ_cal= " << typ_cal
              << "\n  ElContact::CalFactPenalTangentiel()";
         Sortie(1);
     };
    // --- deuxième choix en fonction de typ_cal ---
    // on calcul la valeur final du facteur
    switch (typ_cal)
     { case 1: case 2:
        { fact_penal *= facteur;d_beta_dep_T = 0.;
          if (Permet_affichage() >= 5)
             cout << "\n fact_penal= " << fact_penal ;
          break;
        }
       case 5:case 7:   // on fait varier le coef de pénalisation lorsque le déplacement est plus petit qu'une borne
        { // de régularisation
          double borne_regularisation;
          if (fct_nD_contact.fct_nD_tangentielle_borne_regularisation != NULL)
               {borne_regularisation = Valeur_fct_nD(fct_nD_contact.fct_nD_tangentielle_borne_regularisation
                                          ,ElContact::Fct_nD_contact::tqi_fct_nD_tangentielle_borne_regularisation
                                          ,ElContact::Fct_nD_contact::tqi_const_fct_nD_tangentielle_borne_regularisation
                                          ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_tangentielle_borne_regularisation);
               }
          else {borne_regularisation = ParaGlob::param->ParaAlgoControleActifs().Tangentielle_borne_regularisation();}
          // on calcul un facteur en fonction de la distance
          if (Dabs(dep_T) >  borne_regularisation)
            {fact_penal *= facteur;d_beta_dep_T = 0.;}
          else
            { fact_penal *= facteur * Sqr((dep_T-borne_regularisation)/borne_regularisation) * 0.25;
              d_beta_dep_T = facteur * (dep_T-borne_regularisation)/borne_regularisation/borne_regularisation * 0.5;;
            };
          break;
        }
       case 6: // idem cas 5 mais avec une fonction différente
        { // on récupère le déplacement maxi
          double borne_regularisation;
          if (fct_nD_contact.fct_nD_tangentielle_borne_regularisation != NULL)
               {borne_regularisation = Dabs(Valeur_fct_nD(fct_nD_contact.fct_nD_tangentielle_borne_regularisation
                                             ,ElContact::Fct_nD_contact::tqi_fct_nD_tangentielle_borne_regularisation
                                             ,ElContact::Fct_nD_contact::tqi_const_fct_nD_tangentielle_borne_regularisation
                                             ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_tangentielle_borne_regularisation));
               }
          else {borne_regularisation = Dabs(ParaGlob::param->ParaAlgoControleActifs().Tangentielle_borne_regularisation());}
          // on calcul un facteur en fonction de la distance: = (0.5*(tanh(4.*dep_T/borne)-1.))
          // 0.25*exp(-dep_T/(0.25*borne))
          if (Dabs(dep_T) >  borne_regularisation)
            {fact_penal *= facteur;d_beta_dep_T = 0.;}
          else
            { fact_penal *= -facteur * 0.5 * (tanh(dep_T*4./borne_regularisation)-1.); // formule du même type
              d_beta_dep_T = 4.*(1.-fact_penal*fact_penal)/borne_regularisation; //
            };
          break;
        }
       case 4: case 8: // quelque soit le sens de dep_T
       // on fait varier le coef de pénalisation lorsque le déplacement est plus petit qu'un maxi
        {
  ////------ essai à virer
  //fact_penal *= facteur;d_beta_dep_T = 0.;
  //        if (Permet_affichage() >= 5)
  //           cout << "\n fact_penal= " << fact_penal ;
  //        break;
  ////------ fin essai
          double borne_regularisation;
          if (fct_nD_contact.fct_nD_tangentielle_borne_regularisation != NULL)
               {borne_regularisation = Valeur_fct_nD(fct_nD_contact.fct_nD_tangentielle_borne_regularisation
                                        ,ElContact::Fct_nD_contact::tqi_fct_nD_tangentielle_borne_regularisation
                                        ,ElContact::Fct_nD_contact::tqi_const_fct_nD_tangentielle_borne_regularisation
                                        ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_tangentielle_borne_regularisation);
               }
          else {borne_regularisation = ParaGlob::param->ParaAlgoControleActifs().Tangentielle_borne_regularisation();}
          double max_dep_T;
          if (fct_nD_contact.fct_nD_tangentielle_contact_maxi != NULL)
               {max_dep_T = Valeur_fct_nD(fct_nD_contact.fct_nD_tangentielle_contact_maxi
                               ,ElContact::Fct_nD_contact::tqi_fct_nD_tangentielle_contact_maxi
                               ,ElContact::Fct_nD_contact::tqi_const_fct_nD_tangentielle_contact_maxi
                               ,ElContact::Fct_nD_contact::t_num_ordre_fct_nD_tangentielle_contact_maxi);
               }
          else {max_dep_T = ParaGlob::param->ParaAlgoControleActifs().Tangentielle_contact_maxi();}
          // on calcul un facteur de majoration en fonction du déplacement du pas précédent
  //        double mult_tang = MaX(1., -dep_T_t/max_pene);
          double mult_tang = MaX(1., (Dabs(dep_T_t)/max_dep_T)*mult_tang_t); // il faut appliquer au facteur précédent !
          mult_tang_tdt = 0.5*(mult_tang_t + mult_tang); // on fait une moyenne glissante de 2
          
          if (Permet_affichage() >= 5)
              cout << "\n mult_tang_tdt= " << mult_tang_tdt ;
          // on calcul un facteur en fonction de la distance: du même genre que pour la pénétration
          // sauf qu'ici c'est vrai quelque soit la direction du déplacement
          if (Dabs(dep_T) >  borne_regularisation)
            { fact_penal *= facteur * mult_tang_tdt; d_beta_dep_T = 0.;}
          else
            { fact_penal *= facteur * mult_tang_tdt * Sqr((-Dabs(dep_T)-borne_regularisation)/borne_regularisation) * 0.25;
              d_beta_dep_T = facteur * mult_tang_tdt * (-Dabs(dep_T)-borne_regularisation)/borne_regularisation/borne_regularisation * 0.5;;
            };
          break;
        }

       default:
        cout << "\n **** erreur 2 cas d'un calcul du facteur de penalisation tangentiel non existant "
             << " typ_cal= " << typ_cal
             << "\n  ElContact::CalFactPenalTangentiel()";
        Sortie(1);
     };
   };
   
//  dep_T_tdt = dep_T; // sauvegarde
  // retour
  return fact_penal;
};

// calcul éventuel de la moyenne glissante des positions successive du noeud esclave
// et changement des coordonnées du point passé en paramètre
void ElContact::ChangeEnMoyGlissante(Coordonnee& Noe_atdt)
{  // pour simplifier
   int taille_moyenne_glissante = ParaGlob::param->ParaAlgoControleActifs().Nb_moy_glissant();
   // on dimensionne éventuellement le tableau des positions successives
   if (tab_posi_esclave.Taille() != taille_moyenne_glissante)
      tab_posi_esclave.Change_taille(taille_moyenne_glissante,Coordonnee(ParaGlob::Dimension()));

		 // on calcul éventuellement la moyenne glissante
		 if (taille_moyenne_glissante > 1)
		  { if (nb_posi_esclave_stocker_t < taille_moyenne_glissante)
		     { // c'est le cas où la moyenne n'est pas finalisée
			      nb_posi_esclave_stocker_tdt = nb_posi_esclave_stocker_t+1;
         indice_stockage_glissant_tdt=1;
			      tab_posi_esclave(nb_posi_esclave_stocker_tdt) = noeud->Coord2();
         // calcul de la moyenne
         Noe_atdt=tab_posi_esclave(1); // init
         for (int i=2;i<=nb_posi_esclave_stocker_tdt;i++)
            Noe_atdt += tab_posi_esclave(i);
         Noe_atdt /= nb_posi_esclave_stocker_tdt;
			    }
			   else // cas ou la moyenne est finalisée	
			    { tab_posi_esclave(indice_stockage_glissant_t) = noeud->Coord2();
			      indice_stockage_glissant_tdt=indice_stockage_glissant_t+1; // mise à jour de l'indice pour le prochain stockage
			      // si l'indice dépasse la taille du taille, on remet au début
			      if (indice_stockage_glissant_tdt > taille_moyenne_glissante) indice_stockage_glissant_tdt = 1;
         // calcul de la moyenne
         Noe_atdt=tab_posi_esclave(1); // init
         for (int i=2;i<=taille_moyenne_glissante;i++)
            Noe_atdt += tab_posi_esclave(i);
         Noe_atdt /= taille_moyenne_glissante;
			    };
		  };

};


// calcul de la moyenne glissante de la pénalisation
void ElContact::Moyenne_glissante_penalisation(double& penalisation, double& essai_penalisation)
  {   // penalisation = la somme pondérée
      int tai_val_penal = val_penal.Taille();
      // on vérifie que la taille n'a pas changée
      if (fct_pilotage_contact4 != NULL)
        {if (fct_pilotage_contact4->NbComposante() == 2)
          {Tableau <double> & tava = fct_pilotage_contact4->Valeur_pour_variables_globales();
           int nouvelle_taille = tava(2);
           if (nouvelle_taille != tai_val_penal)
            {if (nouvelle_taille > tai_val_penal)
               // on se contente d'augmenter la taille de stockage
               {val_penal.Change_taille(nouvelle_taille);}
             else
             // on va changer la taille en gardant les dernières valeurs (= les plus récentes)
               {int diff = tai_val_penal-nouvelle_taille;
                for (int i=1;i<= nouvelle_taille;i++)
                  val_penal(i) = val_penal(i+diff);
                val_penal.Change_taille(nouvelle_taille);
                if (pt_dans_val_penal > nouvelle_taille) pt_dans_val_penal = 1;
                tai_val_penal = nouvelle_taille;
               };
             if (Permet_affichage() > 2)
               cout << "\n -> Moyenne_glissante_penalisation: change taille "
                    << tai_val_penal << " -> " << nouvelle_taille;
            };
          };
        };

      if (val_penal(tai_val_penal) == 0.)
       // cas où le tableau n'est pas complètement rempli
       { penalisation *= (pt_dans_val_penal-1); // correspond à la somme
         penalisation = (penalisation+essai_penalisation)/pt_dans_val_penal;
         val_penal(pt_dans_val_penal)=essai_penalisation;
         // pour pt_dans_val_penal == 1 cela donne directement essai_penalisation
       }
      else // sinon le tableau est rempli, on effectue des remplacements
       { penalisation *= tai_val_penal; // la somme
         penalisation = (penalisation - val_penal(pt_dans_val_penal) + essai_penalisation )/tai_val_penal;
         val_penal(pt_dans_val_penal) = essai_penalisation;
       }
      pt_dans_val_penal++; // mise en place du retour périodique
      if (pt_dans_val_penal > tai_val_penal) pt_dans_val_penal = 1;

  };
  
// calcul d'une fonction nD relative à des données de contact
double ElContact::Valeur_fct_nD(Fonction_nD * pt_fonct,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
   Tableau <double> val_ddl_enum (li_enu_scal.size());
   List_io<Ddl_enum_etendu>::const_iterator ie,iefin=li_enu_scal.end();
   int it; // it est l'indice dans le tableau de retour
   for (it=1,ie=li_enu_scal.begin(); ie!=iefin;ie++,it++)
    { // si c'est un ddl pure, on regarde si elle est définie au noeud
      const Ddl_enum_etendu& enu = (*ie); // pour simplifier
      bool trouver = false;
      // on regarde d'abord s'il s'agit d'une info spécifique au contact
      int posi = (*ie).Position()-NbEnum_ddl();
      switch (posi)
        { case 90: // reaction_normale -> en fait pas nécessaire, mais je laisse
           // car existe au noeud en contact
            {val_ddl_enum(it) = force_contact * N; trouver=true; break;}
         // case 91: // reaction_tangentielle déjà stockée au noeud
         // donc sera traité par le noeud en contact
         
///**** en fait les cas X1, X1_t et X1_t0 ne vont pas être traités ici
/// mais vont être traités en grandeurs quelconques car il s'agit de vecteur
// Je laisse quand même mais on ne passera sans doute jamais ici
          case 123: // "X1_t"
            { val_ddl_enum(it) = noeud->Coord1()(1);trouver=true; break;}
          case 124: // "X2_t"
            { val_ddl_enum(it) = noeud->Coord1()(2);trouver=true; break;}
          case 125: // X3_t
            { val_ddl_enum(it) = noeud->Coord1()(3);trouver=true; break;}
          case 126: // X1_t0
            { val_ddl_enum(it) = noeud->Coord0()(1);trouver=true; break;}
          case 127: // X2_t0
            { val_ddl_enum(it) = noeud->Coord0()(2);trouver=true; break;}
          case 128: // X3_t0
            { val_ddl_enum(it) = noeud->Coord0()(3);trouver=true; break;}
//*** fin

          default: // sinon on le stock en ddl_étendu
             trouver=false; break;
        };// fin du switch
      
      // si l'on n'a pas trouver, on regarde si l'info est dispo au noeud en contact
      if (!trouver)
       {if (enu.Position() <= NbEnum_ddl()) // cas d'un ddl pur
         {if (noeud->Existe_ici(enu.Enum()))
               {val_ddl_enum(it) = noeud->Ddl_noeud_tdt(enu.Enum()).Valeur();
                trouver = true;
               };
         }
        else // cas d'un ddl étendu
         { if (noeud->Existe_ici_ddlEtendu(enu))
                {val_ddl_enum(it) = noeud->DdlEtendue(enu).ConstValeur();
                 trouver = true;
                };
         };
       };
       
      // si on n'a rien trouvé
      if (!trouver)
        {cout << "\n erreur***: le ddl " << enu.Nom_plein()
              << " n'existe pas pour le noeud en contact "
              << " la fonction nD " << pt_fonct->NomFonction()
              << " ne peut pas etre renseignee " << flush;
         if (ParaGlob::NiveauImpression() > 2)
          {this->Affiche();
           pt_fonct->Affiche();
          }
         Sortie(1);
        };

    };// -- fin de la boucle sur la liste de Ddl_enum_etendu

   // 2) puis les grandeurs quelconques
   {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();
     
     // on regarde tout d'abord les grandeurs spécifiques à l'élément contact
     switch (enuTQ.EnuTypeQuelconque().EnumTQ())
       { // 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:
           { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*ipq).Grandeur_pointee()));
             *(gr.ConteneurEntier()) = nb_decol_tdt;
             trouver = true;
             break;
           }
         case CONTACT_PENALISATION_N:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*ipq).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = penalisation;
             trouver = true;
             break;
           }
         case CONTACT_PENALISATION_T:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*ipq).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = penalisation_tangentielle;
             trouver = true;
             break;
           }
         case CONTACT_NB_PENET:
           { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*ipq).Grandeur_pointee()));
             *(gr.ConteneurEntier()) = nb_pene_tdt;
             trouver = true;
             break;
           }
         case CONTACT_CAS_SOLIDE:
           { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*ipq).Grandeur_pointee()));
             *(gr.ConteneurEntier()) = cas_solide;
             trouver = true;
             break;
           }
         case CONTACT_ENERG_GLISSE_ELAS:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*ipq).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = energie_frottement.EnergieElastique();
             trouver = true;
             break;
           }
         case CONTACT_ENERG_GLISSE_PLAS:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*ipq).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = energie_frottement.DissipationPlastique();
             trouver = true;
             break;
           }
         case CONTACT_ENERG_GLISSE_VISQ:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*ipq).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = energie_frottement.DissipationVisqueuse();
             trouver = true;
             break;
           }
         case CONTACT_ENERG_PENAL:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*ipq).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = energie_penalisation;
             trouver = true;
             break;
           }
         case NOEUD_PROJECTILE_EN_CONTACT:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*ipq).Grandeur_pointee()));
             if (actif)
               {*(gr.ConteneurDouble()) = 100.;}
             else
               {*(gr.ConteneurDouble()) = -0.1; };
             trouver = true;
             break;
            }
         case NOEUD_FACETTE_EN_CONTACT:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*ipq).Grandeur_pointee()));
             int NBNF = tabNoeud.Taille();
             if (actif)
              { for (int i=2;i<=NBNF;i++)
                  *(gr.ConteneurDouble()) += 100.;
              }
             else
              { for (int i=2;i<=NBNF;i++)
                  *(gr.ConteneurDouble()) -= 0.1;
              };
             trouver = true;
             break;
           }
         case NUM_NOEUD:
           { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*ipq).Grandeur_pointee()));
             *(gr.ConteneurEntier()) = noeud->Num_noeud();
             trouver = true;
             break;
           }
         case NUM_MAIL_NOEUD:
           { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*ipq).Grandeur_pointee()));
             *(gr.ConteneurEntier()) = noeud->Num_Mail();
             trouver = true;
             break;
           }
         case POSITION_GEOMETRIQUE:
           {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*ipq).Grandeur_pointee()));
            (*gr.ConteneurCoordonnee())= noeud->Coord2();
            trouver = true;
             break;
            }
         case POSITION_GEOMETRIQUE_t:
           {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*ipq).Grandeur_pointee()));
            (*gr.ConteneurCoordonnee())= noeud->Coord1();
            trouver = true;
             break;
            }
         case POSITION_GEOMETRIQUE_t0:
           {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*ipq).Grandeur_pointee()));
            (*gr.ConteneurCoordonnee())= noeud->Coord0();
            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:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*ipq).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= Mtdt-noeud->Coord2();
             trouver = true;
              break;
             }
         case PENETRATION_CONTACT_T:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*ipq).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= Mt-noeud->Coord1();
             trouver = true;
              break;
             }
         case GLISSEMENT_CONTACT:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*ipq).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= dep_tangentiel;
             trouver = true;
              break;
             }
         case GLISSEMENT_CONTACT_T:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*ipq).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= dep_T_t;
             trouver = true;
              break;
             }
         case NORMALE_CONTACT:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*ipq).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= N;
             trouver = true;
              break;
             }
         case FORCE_CONTACT:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*ipq).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= force_contact;
             trouver = true;
              break;
             }
         case FORCE_CONTACT_T:
          {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*ipq).Grandeur_pointee()));
           (*gr.ConteneurCoordonnee())= force_contact_t;
           trouver = true;
            break;
           }
         case CONTACT_COLLANT:
          { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*ipq).Grandeur_pointee()));
            *(gr.ConteneurEntier()) = cas_collant;
            trouver = true;
            break;
          }
         case NUM_ZONE_CONTACT:
          { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*ipq).Grandeur_pointee()));
            *(gr.ConteneurEntier()) = num_zone_contact;
            trouver = true;
            break;
          }
       case NUM_ELEMENT:
        { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*ipq).Grandeur_pointee()));
          *(gr.ConteneurEntier()) = Elfront()->PtEI()->Num_elt();
          trouver = true;
          break;
        }
       case NUM_MAIL_ELEM:
        { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*ipq).Grandeur_pointee()));
          *(gr.ConteneurEntier()) = Elfront()->PtEI()->Num_maillage();
          trouver = true;
          break;
        }
        //*** fin

         default:
           trouver = false;
           break;
      };

     // si l'on n'a pas trouver, on regarde si l'info est dispo au noeud en contact
     if (!trouver)
      {if (noeud->Existe_ici(en_ET_TQ))
         { (*(enuTQ.Grandeur_pointee())) = (*noeud->Grandeur_quelconque(en_ET_TQ).Const_Grandeur_pointee());
               trouver = true;
         };
      };
     





     // si on n'a rien trouvé
     if (!trouver)
       {cout << "\n erreur***: la grandeur  " << en_ET_TQ.NomPlein()
             << " n'existe pas pour le noeud en contact "
             << " la fonction nD " << pt_fonct->NomFonction()
             << " ne peut pas etre renseignee " << flush;
        if (ParaGlob::NiveauImpression() > 2)
         {this->Affiche();
          pt_fonct->Affiche();
         }
        Sortie(1);
       };

   };
   };

   // 3) et enfin les grandeurs quelconques:
   //        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);
     
     // on regarde tout d'abord les grandeurs spécifiques à l'élément contact
     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:
           { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*tqi(i)).Grandeur_pointee()));
             *(gr.ConteneurEntier()) = nb_decol_tdt;
             trouver = true;
             break;
           }
         case CONTACT_PENALISATION_N:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*tqi(i)).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = penalisation;
             trouver = true;
             break;
           }
         case CONTACT_PENALISATION_T:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*tqi(i)).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = penalisation_tangentielle;
             trouver = true;
             break;
           }
         case CONTACT_NB_PENET:
           { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*tqi(i)).Grandeur_pointee()));
             *(gr.ConteneurEntier()) = nb_pene_tdt;
             trouver = true;
             break;
           }
         case CONTACT_CAS_SOLIDE:
           { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*tqi(i)).Grandeur_pointee()));
             *(gr.ConteneurEntier()) = cas_solide;
             trouver = true;
             break;
           }
         case CONTACT_ENERG_GLISSE_ELAS:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*tqi(i)).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = energie_frottement.EnergieElastique();
             trouver = true;
             break;
           }
         case CONTACT_ENERG_GLISSE_PLAS:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*tqi(i)).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = energie_frottement.DissipationPlastique();
             trouver = true;
             break;
           }
         case CONTACT_ENERG_GLISSE_VISQ:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*tqi(i)).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = energie_frottement.DissipationVisqueuse();
             trouver = true;
             break;
           }
         case CONTACT_ENERG_PENAL:
           {  Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*tqi(i)).Grandeur_pointee()));
             *(gr.ConteneurDouble()) = energie_penalisation;
             trouver = true;
             break;
           }
         case NOEUD_PROJECTILE_EN_CONTACT:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*tqi(i)).Grandeur_pointee()));
             if (actif)
               {*(gr.ConteneurDouble()) = 100.;}
             else
               {*(gr.ConteneurDouble()) = -0.1; };
             trouver = true;
             break;
            }
         case NOEUD_FACETTE_EN_CONTACT:
           { Grandeur_scalaire_double& gr= *((Grandeur_scalaire_double*) ((*tqi(i)).Grandeur_pointee()));
             int NBNF = tabNoeud.Taille();
             if (actif)
              { for (int i=2;i<=NBNF;i++)
                  *(gr.ConteneurDouble()) += 100.;
              }
             else
              { for (int i=2;i<=NBNF;i++)
                  *(gr.ConteneurDouble()) -= 0.1;
              };
             trouver = true;
             break;
           }
         case NUM_NOEUD:
           { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*tqi(i)).Grandeur_pointee()));
             *(gr.ConteneurEntier()) = noeud->Num_noeud();
             trouver = true;
             break;
           }
         case NUM_MAIL_NOEUD:
           { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*tqi(i)).Grandeur_pointee()));
             *(gr.ConteneurEntier()) = noeud->Num_Mail();
             trouver = true;
             break;
           }
         case POSITION_GEOMETRIQUE:
           {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*tqi(i)).Grandeur_pointee()));
            (*gr.ConteneurCoordonnee())= noeud->Coord2();
            trouver = true;
             break;
            }
         case POSITION_GEOMETRIQUE_t:
           {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*tqi(i)).Grandeur_pointee()));
            (*gr.ConteneurCoordonnee())= noeud->Coord1();
            trouver = true;
             break;
            }
         case POSITION_GEOMETRIQUE_t0:
           {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*tqi(i)).Grandeur_pointee()));
            (*gr.ConteneurCoordonnee())= noeud->Coord0();
            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:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*tqi(i)).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= Mtdt-noeud->Coord2();
             trouver = true;
              break;
             }
         case PENETRATION_CONTACT_T:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*tqi(i)).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= Mt-noeud->Coord1();
             trouver = true;
              break;
             }
         case GLISSEMENT_CONTACT:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*tqi(i)).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= dep_tangentiel;
             trouver = true;
              break;
             }
         case GLISSEMENT_CONTACT_T:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*tqi(i)).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= dep_T_t;
             trouver = true;
              break;
             }
         case NORMALE_CONTACT:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*tqi(i)).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= N;
             trouver = true;
              break;
             }
         case FORCE_CONTACT:
            {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*tqi(i)).Grandeur_pointee()));
             (*gr.ConteneurCoordonnee())= force_contact;
             trouver = true;
              break;
             }
         case FORCE_CONTACT_T:
          {Grandeur_coordonnee& gr= *((Grandeur_coordonnee*) ((*tqi(i)).Grandeur_pointee()));
           (*gr.ConteneurCoordonnee())= force_contact_t;
           trouver = true;
            break;
           }
         case CONTACT_COLLANT:
          { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*tqi(i)).Grandeur_pointee()));
            *(gr.ConteneurEntier()) = cas_collant;
            trouver = true;
            break;
          }
         case NUM_ZONE_CONTACT:
          { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*tqi(i)).Grandeur_pointee()));
            *(gr.ConteneurEntier()) = num_zone_contact;
            trouver = true;
            break;
          }
       case NUM_ELEMENT:
        { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*tqi(i)).Grandeur_pointee()));
          *(gr.ConteneurEntier()) = Elfront()->PtEI()->Num_elt();
          trouver = true;
          break;
        }
       case NUM_MAIL_ELEM:
        { Grandeur_scalaire_entier& gr= *((Grandeur_scalaire_entier*) ((*tqi(i)).Grandeur_pointee()));
          *(gr.ConteneurEntier()) = Elfront()->PtEI()->Num_maillage();
          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 pour le noeud en contact "
             << " la fonction nD " << pt_fonct->NomFonction()
             << " ne peut pas etre renseignee " << flush;
        if (ParaGlob::NiveauImpression() > 2)
         {this->Affiche();
          pt_fonct->Affiche();
         }
        Sortie(1);
       };

   };
   // 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  ElContact::Valeur(... \n";
        Sortie(1);
      };
   #endif
   // on récupère le premier élément du tableau uniquement
   return tab_val(1);
 };