
// 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 "Debug.h"
#include "ElemMeca.h"
#include <iomanip>
#include "ConstMath.h"
#include "Util.h"
#include "Coordonnee2.h"
#include "Coordonnee3.h"
#include "TypeQuelconqueParticulier.h"
#include "TypeConsTens.h"
#include "Loi_iso_elas3D.h"
#include "Loi_iso_elas1D.h"
#include "Loi_iso_elas2D_C.h"
#include "FrontPointF.h"
#include "MathUtil2.h"
#include "Enum_StabMembrane.h"

 // -------------------- stabilisation d'hourglass -------------------
// calcul d'élément de contrôle d'hourglass associée à un comportement
// str_precision : donne le type particulier d'élément à construire
void ElemMeca::Init_hourglass_comp(const ElemGeomC0& elgeHour, const string & str_precision
     ,LoiAbstraiteGeneral * loiHourglass, const BlocGen & bloc)
{ 
 //!!!! en fait pour l'instant on n'utilise pas elgehour, on considère que le nombre de pti complet est celui de l'élément sans info annexe !!!
 // sauf pour l'hexaèdre quadratique pour lequel on a définit un str_precision particulier
  // on met à jour les indicateurs
  if (Type_Enum_StabHourglass_existe(bloc.Nom(1)))
	  { type_stabHourglass = Id_Nom_StabHourglass(bloc.Nom(1).c_str());
	    coefStabHourglass = bloc.Val(1);
	  }
	 else
	  { cout << "\n erreur, le type " << bloc.Nom(2) << " de gestion d'hourglass n'existe pas !!";
	    if (ParaGlob::NiveauImpression() > 5)
		   cout << "\n ElemMeca::Init_hourglass_comp(...";
	    cout << endl;
	    Sortie(1);
		 };
  switch (type_stabHourglass)
   {case STABHOURGLASS_PAR_COMPORTEMENT :
	    { // -- choix de l'element pour le calcul de la raideur -----
		     // on recherche un élément de même type, 
		     // par défaut : numéro de maillage = le numéro de this, car c'est bien rattaché à ce maillage, mais l'élément est caché, donc non accessible par le maillage
		     // numéro d'élément = 1000000 + numéro de this : a priori c'est deux numéros n'ont pas d'importance, car ils ne sont pas
		     // rattachés à un maillage existant en tant que tel
		     int nUmMail = this->Num_elt_const();
		     int nUmElem = 1000000 + this->Num_elt();
		     tab_elHourglass.Change_taille(1);
		     tab_elHourglass(1) =   (ElemMeca*) Element::Choix_element(nUmMail,nUmElem,Id_geometrie(),Id_interpolation(),Id_TypeProblem(),str_precision);

		     if (tab_elHourglass(1) == NULL)
		     	{  // l'operation a echouee
		     		  cout << "\n Erreur dans le choix d'un element interne utilise pour le blocage d'hourglass ****** ";
		     		  cout << "\n l\'element : " << Nom_interpol(id_interpol)
		     			      << " " << Nom_geom(id_geom) << " " << str_precision
                << " n\'est pas present dans le programme ! " << endl;
		     		  if (ParaGlob::NiveauImpression() > 5)
		     	       cout << "\n 	ElemMeca::Init_hourglass_comp(..." << endl;
		     		  Sortie (1);
		     	};
		     // --- définition des noeuds de l'élément -----
		     // on construit à partir des mêmes noeuds que ceux de l'élément père
		     // affectation des noeuds au nouvel élément
		     tab_elHourglass(1)->Tab_noeud() = this->Tab_noeud();
		     //--- def de la loi de comportement ---
		     // affectation de la loi
		     tab_elHourglass(1)->DefLoi(loiHourglass);
		     break;
	    }	
    case STABHOURGLASS_PAR_COMPORTEMENT_REDUIT :
	    { // ici on a besoin de deux éléments. Le premier sert à faire le calcul complet 
	      // le second sert à faire un calcul réduit, comme l'élément réel 
		     tab_elHourglass.Change_taille(2);

	      // -- choix  pour le calcul de la raideur -----
		     // on recherche un élément de même type,
		     // par défaut : numéro de maillage = le numéro de this, car c'est bien rattaché à ce maillage, mais l'élément est caché, donc non accessible par le maillage
		     // numéro d'élément = 1000000 + numéro de this : a priori c'est deux numéros n'ont pas d'importance, car ils ne sont pas
		     // rattachés à un maillage existant en tant que tel
		     //-- le premier élément:
		     int nUmMail = this->Num_elt_const();
		     int nUmElem = 1000000 + this->Num_elt();
		     tab_elHourglass(1) =   (ElemMeca*) Element::Choix_element(nUmMail,nUmElem,Id_geometrie(),Id_interpolation(),Id_TypeProblem(),str_precision);
		     if (tab_elHourglass(1) == NULL)
		     	{  // l'operation a echouee
		     		  cout << "\n Erreur dans le choix d'un element interne utilise pour le blocage d'hourglass ****** ";
		     		  cout << "\n l\'element : " << Nom_interpol(id_interpol)
		     			      << " " << Nom_geom(id_geom) << " " << str_precision
                << " n\'est pas present dans le programme ! " << endl;
		     		  if (ParaGlob::NiveauImpression() > 5)
		     	       cout << "\n 	ElemMeca::Init_hourglass_comp(..." << endl;
		     		  Sortie (1);
		     	};
		     //-- le second élément identique à this: on utilise le même numéro de maillage
		     // même infos annexes
		     nUmElem = 20000000 + this->Num_elt(); // on ajoute une unité pour le numéro
		     tab_elHourglass(2) =   (ElemMeca*) Element::Choix_element(nUmMail,nUmElem,Id_geometrie(),Id_interpolation(),Id_TypeProblem(),this->infos_annexes);

		     if (tab_elHourglass(2) == NULL)
		     	{  // l'operation a echouee
		     		  cout << "\n Erreur dans le choix d'un element interne utilise pour le blocage d'hourglass ****** ";
		     		  cout << "\n l\'element : " << Nom_interpol(id_interpol)
		     			      << " " << Nom_geom(id_geom) << " " << this->infos_annexes
                << " n\'est pas present dans le programme ! " << endl;
		     		  if (ParaGlob::NiveauImpression() > 5)
		     	       cout << "\n 	ElemMeca::Init_hourglass_comp(..." << endl;
		     		  Sortie (1);
		     	};
		     // --- définition des noeuds de l'élément -----
		     // on construit à partir des mêmes noeuds que ceux de l'élément père
		     // affectation des noeuds au nouvel élément
		     tab_elHourglass(1)->Tab_noeud() = this->Tab_noeud();
		     tab_elHourglass(2)->Tab_noeud() = this->Tab_noeud();
		     //--- def de la loi de comportement ---
		     // affectation de la loi
		     tab_elHourglass(1)->DefLoi(loiHourglass);
		     tab_elHourglass(2)->DefLoi(loiHourglass);
       // def de trois vecteurs de travail s'ils ne sont pas encore définit
       if (vec_hourglass.Taille() < 3)
           vec_hourglass.Change_taille(3);
		     break;
	    }	

	   default :
		     	cout << "\n*** Erreur : cas de gestop, d'hourglass non defini !\n";
		     	cout << "\n ElemMeca::Cal_implicit_hourglass() \n";
		     	Sortie(1);
   };
};

// stabilisation pour un calcul implicit
void ElemMeca::Cal_implicit_hourglass()
{E_Hourglass=0.; // init par défaut
 switch (type_stabHourglass)
  {case STABHOURGLASS_PAR_COMPORTEMENT :
	 { // --- calcul de la raideur de l'élément, dans le cas implicite ---
		Element::ResRaid resraid = tab_elHourglass(1)->Calcul_implicit (ParaGlob::param->ParaAlgoControleActifs());
		// on tiend compte du facteur de modération
		(*(resraid.raid)) *= coefStabHourglass;
		(*(resraid.res)) *= coefStabHourglass;
		// on met à jour la raideur et le résidu de l'élément principal
		(*raideur) +=  (*(resraid.raid));
		(*residu) += (*(resraid.res));
  // on récupère les énergies stockées à l'élément
	 const EnergieMeca& energieTotale = tab_elHourglass(1)->EnergieTotaleElement();
		E_Hourglass = coefStabHourglass * (energieTotale.EnergieElastique() 
							                + energieTotale.DissipationPlastique() + energieTotale.DissipationVisqueuse()); 

//---- debug
//cout << "\n raideur locale après stabilisation d'hourglass ";
//matRaideur.Affiche();
//---- fin debug
	 };
	 break;
  case STABHOURGLASS_PAR_COMPORTEMENT_REDUIT :
	 { // --- calcul de la raideur de l'élément, dans le cas implicite ---
	   // ici on calcul la partie supposée supplémentaire à celle déjà présente dans le calcul réduit
		
		// au premier passage ou bien si on demande une grande précision de calcul, on calcul la raideur et le second membre, 
		// ensuite on ne calcul que le second membre
//		if ((raid_hourglass_transitoire == NULL)||(prepa_niveau_precision > 8))
  // en l'expérience prouve que c'est la forme des éléments du début qui est la moins tordu donc vaut mieux
  // ne pas recalculer la raideur sur des éléments tordus, c'est dans ce cas que ça marche le mieux
		if (raid_hourglass_transitoire == NULL)
		 {// 1) calcul de la partie complète
		  Element::ResRaid resraid1 = tab_elHourglass(1)->Calcul_implicit (ParaGlob::param->ParaAlgoControleActifs());
		  // 2) calcul de la partie réduite
		  Element::ResRaid resraid2 = tab_elHourglass(2)->Calcul_implicit (ParaGlob::param->ParaAlgoControleActifs());
		  // on soustrait en gardant le résultat dans resraid1
// **** non ce n'est pas une bonne idée, lorsque l'on fait certain test de stabilité: lorsque le facteur est très grand
// si on ne fait pas la soustraction cela converge, avec la soustraction cela ne converge pas  donc  il ne faut pas soustraire
//		  (*(resraid1.raid)) -= (*(resraid2.raid));
//		  (*(resraid1.res)) -= (*(resraid2.res));
    
    // ---nouvelle idée:
    // on commence par récupérer dans le vecteur résidu les incréments de ddl correspondant à l'élément
    int NBN = tab_noeud.Taille();
    int dim = ParaGlob::Dimension();
    if(ParaGlob::AxiSymetrie())
       dim--; // on passe à une dimension 2 ! et il y a uniquement les x et les y qui sont concernés
    Vecteur& D_X = vec_hourglass(1); // pour simplier
    Vecteur& X = vec_hourglass(2); // pour simplier
    D_X.Change_taille(NBN*dim); // changement de la taille au cas où
    X.Change_taille(NBN*dim); // changement de la taille au cas où
    for (int ine=1;ine<=NBN;ine++)
      { Noeud& noe = *(tab_noeud(ine)); // pour simplifier
        Coordonnee delta_X = noe.Coord2() - noe.Coord1();
        Coordonnee Xnoeud = noe.Coord2();
        int ipos = (ine-1)*dim;
        switch (dim)
          { case 3: {D_X(ipos+1)=delta_X(1);D_X(ipos+2)=delta_X(2);D_X(ipos+3)=delta_X(3);
                     X(ipos+1)=Xnoeud(1);X(ipos+2)=Xnoeud(2);X(ipos+3)=Xnoeud(3);break;}
            case 2: {D_X(ipos+1)=delta_X(1);D_X(ipos+2)=delta_X(2);
                     X(ipos+1)=Xnoeud(1);X(ipos+2)=Xnoeud(2);break;}
            case 1: {D_X(ipos+1)=delta_X(1);
                     X(ipos+1)=Xnoeud(1);break;}
          };
      };
    // on multiplie la matrice par le vecteur delta_X 
    (*(resraid1.res)) = resraid1.raid->Prod_mat_vec(D_X);
		  // on sauvegarde la raideur initiale
    if (raid_hourglass_transitoire == NULL)
		      raid_hourglass_transitoire = new Mat_pleine((*(resraid1.raid)));
    // premier calcul de l'accroissement de l'énergie
    E_Hourglass = 0.5 * (D_X * (*(resraid1.res)));
		  // on tiend compte du facteur de modération
    double coefMultiplicatif = coefStabHourglass;
    if (E_Hourglass < 0.) // on veut un accroissement d'énergie positive (1/2 k deltaX ^2)
		    { coefMultiplicatif = -coefStabHourglass;};
    // grandeurs finales avec le bon signe
    (*(resraid1.raid)) *= coefMultiplicatif;
		  (*(resraid1.res)) *= coefMultiplicatif;
    // calcul définitif: correspond à une grandeur qui peut être comparée avec l'énergie élastique totale
    E_Hourglass = 0.5 * (X * (*(resraid1.res)));
    

//debug
//cout << "\n premier passage, ";
//res.Affiche(); (resraid1.raid)->Affiche();
//fin debug
		  // on met à jour la raideur et le résidu de l'élément principal
		  (*raideur) +=  (*(resraid1.raid));
		  (*residu) -= (*(resraid1.res));
		  }
		else 
		 {// sinon on utilise la précédente raideur sauvegardée et on ne calcul que la partie résidue
// dans la nouvelle idée le vecteur doit exister à la bonne dimension, mais CalculResidu_tdt n'a pas besoin
// d'être appeler **** a améliorer
//		  Vecteur* resHourglass1 = NULL;
//    resHourglass1 = tab_elHourglass(1)->CalculResidu_tdt (ParaGlob::param->ParaAlgoControleActifs());
	/*	  Vecteur* resHourglass2 = NULL;
    resHourglass2 = tab_elHourglass(2)->CalculResidu_tdt (ParaGlob::param->ParaAlgoControleActifs());
		  // on soustrait les résidus en gardant le résultat dans resraid1
		  (*(resHourglass1)) -= (*(resHourglass2));*/
    
    
     // ---nouvelle idée:
    // on commence par récupérer dans le vecteur résidu les incréments de ddl correspondant à l'élément
    int NBN = tab_noeud.Taille();
    int dim = ParaGlob::Dimension();
    if(ParaGlob::AxiSymetrie())
       dim--; // on passe à une dimension 2 ! et il y a uniquement les x et les y qui sont concernés
    Vecteur& D_X = vec_hourglass(1); // pour simplier
    Vecteur& X = vec_hourglass(2); // pour simplier
    D_X.Change_taille(NBN*dim); // changement de la taille au cas où
    X.Change_taille(NBN*dim); // changement de la taille au cas où
    for (int ine=1;ine<=NBN;ine++)
      { Noeud& noe = *(tab_noeud(ine)); // pour simplifier
        Coordonnee delta_X = noe.Coord2() - noe.Coord1();
        Coordonnee Xnoeud = noe.Coord2();
        int ipos = (ine-1)*dim;
        switch (dim)
          { case 3: {D_X(ipos+1)=delta_X(1);D_X(ipos+2)=delta_X(2);D_X(ipos+3)=delta_X(3);
                     X(ipos+1)=Xnoeud(1);X(ipos+2)=Xnoeud(2);X(ipos+3)=Xnoeud(3);break;}
            case 2: {D_X(ipos+1)=delta_X(1);D_X(ipos+2)=delta_X(2);
                     X(ipos+1)=Xnoeud(1);X(ipos+2)=Xnoeud(2);break;}
            case 1: {D_X(ipos+1)=delta_X(1);
                     X(ipos+1)=Xnoeud(1);break;}
          };
      };
//debug
//cout << " res ddl, ";
//res.Affiche();
//fin debug
    Vecteur& res = vec_hourglass(2); // pour simplier
    res.Change_taille(NBN*dim); // changement de la taille au cas où
    // on multiplie le vecteur delta_X par la matrice
    res = raid_hourglass_transitoire->Prod_mat_vec(D_X);
    // on définit un facteur de modération qui est fondé sur le ratio des énergies
    
// --- essai d'adaptation du coef, mais ça ne marche pas du tout, il n'y a que quand c'est fixe que c'est ok
/*    
    double energieHourglassDeBase = 0.5 * (D_X * res);
    const EnergieMeca& energieActuelle = this->EnergieTotaleElement(); // récup de l'énergie actuelle
    double val_ref_energie = (Dabs(energieActuelle.EnergieElastique())
							                + Dabs(energieActuelle.DissipationPlastique())
                       + Dabs(energieActuelle.DissipationVisqueuse()));
    double coef_a_appliquer = coefStabHourglass;
    if (Dabs(energieHourglassDeBase*coefStabHourglass) > MaX(ConstMath::petit,val_ref_energie))
       coef_a_appliquer *= val_ref_energie / energieHourglassDeBase;
		  // on tiend compte du facteur de modération
		  res *= coef_a_appliquer;
		  // on met à jour le résidu de l'élément principal
		  (*residu) -= res;
		  // pour la partie raideur: on met à jour la raideur de l'élément principal
		  (*raideur) +=  coef_a_appliquer * (*raid_hourglass_transitoire);
    // calcul de l'énergie
    E_Hourglass = energieHourglassDeBase * coef_a_appliquer; //0.5 * (D_X * res);
*/

    // premier calcul de l'accroissement de l'énergie
    E_Hourglass = 0.5 * (D_X * res);
		  // on tiend compte du facteur de modération
    double coefMultiplicatif = coefStabHourglass;
    if (E_Hourglass < 0.) // on veut un accroissement d'énergie positive (1/2 k deltaX ^2)
		    { coefMultiplicatif = -coefStabHourglass;};
    // grandeurs finales avec le bon signe
		  res *= coefMultiplicatif;
    // calcul définitif: correspond à une grandeur qui peut être comparée avec l'énergie élastique totale
    E_Hourglass = 0.5 * (X * (res));
		  // on met à jour le résidu de l'élément principal
		  (*residu) -= res;
		  // pour la partie raideur: on met à jour la raideur de l'élément principal
		  (*raideur) +=  coefMultiplicatif * (*raid_hourglass_transitoire);
    
//debug
//if (E_Hourglass < 0.)
//  { D_X.Affiche();
//    res.Affiche();
//    raid_hourglass_transitoire->Affiche();
//    cout << "\n E_Hourglass=  "<< E_Hourglass << endl ;
//    
//  };
//res.Affiche(); (raid_hourglass_transitoire)->Affiche();
//fin debug
		  };

//---- debug
//cout << "\n raideur locale après stabilisation d'hourglass ";
//matRaideur.Affiche();
//---- fin debug
	 };
	 break;
	case STABHOURGLASS_NON_DEFINIE : 
			// on ne fait rien
	 break;
	default :
			cout << "\n*** Erreur : cas de gestop, d'hourglass non defini !\n";
			cout << "\n ElemMeca::Cal_implicit_hourglass() \n";
			Sortie(1);
  };
		
};

// stabilisation pour un calcul explicit
void ElemMeca::Cal_explicit_hourglass(bool atdt)
{ switch (type_stabHourglass)
  {case STABHOURGLASS_PAR_COMPORTEMENT :
	 { // --- calcul de la raideur de l'élément, dans le cas implicite ---
	   Vecteur* resHourglass = NULL;
	   if (atdt)
		    {resHourglass = tab_elHourglass(1)->CalculResidu_tdt (ParaGlob::param->ParaAlgoControleActifs());}
		  else
		    {resHourglass = tab_elHourglass(1)->CalculResidu_t (ParaGlob::param->ParaAlgoControleActifs());};
		  // on tiend compte du facteur de modération
    (*resHourglass) *= coefStabHourglass;
		  // on met à jour  le résidu de l'élément principal
		  (*residu) += (*resHourglass);

//---- debug
//cout << "\n raideur locale après stabilisation d'hourglass ";
//matRaideur.Affiche();
//---- fin debug
	 };
	 break;
  case STABHOURGLASS_PAR_COMPORTEMENT_REDUIT :
	 { // --- on calcul la raideur de l'élément juste à la définition ---
		  if ((raid_hourglass_transitoire == NULL)||(prepa_niveau_precision > 18))  // pour le debug
		  { // 1) calcul de la partie complète
		    Element::ResRaid resraid1 = tab_elHourglass(1)->Calcul_implicit (ParaGlob::param->ParaAlgoControleActifs());
		    // on sauvegarde la raideur initiale
      if (raid_hourglass_transitoire == NULL)
		      raid_hourglass_transitoire = new Mat_pleine((*(resraid1.raid)));
      // on commence par récupérer dans le vecteur résidu les incréments de ddl correspondant à l'élément
      int NBN = tab_noeud.Taille();
      int dim = ParaGlob::Dimension();
      if(ParaGlob::AxiSymetrie())
         dim--; // on passe à une dimension 2 ! et il y a uniquement les x et les y qui sont concernés
      Vecteur& D_X = vec_hourglass(1); // pour simplier
      Vecteur& X = vec_hourglass(2); // pour simplier
      D_X.Change_taille(NBN*dim); // changement de la taille au cas où
      X.Change_taille(NBN*dim); // changement de la taille au cas où
      for (int ine=1;ine<=NBN;ine++)
      { Noeud& noe = *(tab_noeud(ine)); // pour simplifier
        Coordonnee delta_X = noe.Coord2() - noe.Coord1();
        Coordonnee Xnoeud = noe.Coord2();
        int ipos = (ine-1)*dim;
        switch (dim)
          { case 3: {D_X(ipos+1)=delta_X(1);D_X(ipos+2)=delta_X(2);D_X(ipos+3)=delta_X(3);
                     X(ipos+1)=Xnoeud(1);X(ipos+2)=Xnoeud(2);X(ipos+3)=Xnoeud(3);break;}
            case 2: {D_X(ipos+1)=delta_X(1);D_X(ipos+2)=delta_X(2);
                     X(ipos+1)=Xnoeud(1);X(ipos+2)=Xnoeud(2);break;}
            case 1: {D_X(ipos+1)=delta_X(1);
                     X(ipos+1)=Xnoeud(1);break;}
          };
      };
      // on multiplie la matrice par le vecteur delta_X 
      (*(resraid1.res)) = resraid1.raid->Prod_mat_vec(D_X);
    // premier calcul de l'accroissement de l'énergie
      E_Hourglass = 0.5 * (D_X * (*(resraid1.res)));
		    // on tiend compte du facteur de modération
      double coefMultiplicatif = coefStabHourglass;
      if (E_Hourglass < 0.) // on veut un accroissement d'énergie positive (1/2 k deltaX ^2)
		      { coefMultiplicatif = -coefStabHourglass;};
      // grandeurs finales avec le bon signe
      (*(resraid1.raid)) *= coefMultiplicatif;
		    (*(resraid1.res)) *= coefMultiplicatif;
      // calcul définitif: correspond à une grandeur qui peut être comparée avec l'énergie élastique totale
      E_Hourglass = 0.5 * (X * (*(resraid1.res)));

		    // on met à jour le résidu de l'élément principal
		    (*residu) -= (*(resraid1.res));
      // calcul de l'énergie
      E_Hourglass = 0.5 * (D_X * (*(resraid1.res)));
		  }
		  else // cas courant
		  {// on utilise la précédente raideur sauvegardée et on ne calcul qu'une partie résidue linéaire 
     // on commence par récupérer dans le vecteur résidu les incréments de ddl correspondant à l'élément
     int NBN = tab_noeud.Taille();
     int dim = ParaGlob::Dimension();
     if(ParaGlob::AxiSymetrie())
       dim--; // on passe à une dimension 2 ! et il y a uniquement les x et les y qui sont concernés
     Vecteur& D_X = vec_hourglass(1); // pour simplier
     Vecteur& X = vec_hourglass(2); // pour simplier
     D_X.Change_taille(NBN*dim); // changement de la taille au cas où
     X.Change_taille(NBN*dim); // changement de la taille au cas où
     for (int ine=1;ine<=NBN;ine++)
      { Noeud& noe = *(tab_noeud(ine)); // pour simplifier
        Coordonnee delta_X = noe.Coord2() - noe.Coord1();
        Coordonnee Xnoeud = noe.Coord2();
        int ipos = (ine-1)*dim;
        switch (dim)
          { case 3: {D_X(ipos+1)=delta_X(1);D_X(ipos+2)=delta_X(2);D_X(ipos+3)=delta_X(3);
                     X(ipos+1)=Xnoeud(1);X(ipos+2)=Xnoeud(2);X(ipos+3)=Xnoeud(3);break;}
            case 2: {D_X(ipos+1)=delta_X(1);D_X(ipos+2)=delta_X(2);
                     X(ipos+1)=Xnoeud(1);X(ipos+2)=Xnoeud(2);break;}
            case 1: {D_X(ipos+1)=delta_X(1);
                     X(ipos+1)=Xnoeud(1);break;}
          };
      };
     Vecteur& res = vec_hourglass(2); // pour simplier
     res.Change_taille(NBN*dim); // changement de la taille au cas où
     // on multiplie le vecteur delta_X par la matrice
     res = raid_hourglass_transitoire->Prod_mat_vec(D_X);
    // premier calcul de l'accroissement de l'énergie
     E_Hourglass = 0.5 * (D_X * res);
		   // on tiend compte du facteur de modération
     double coefMultiplicatif = coefStabHourglass;
     if (E_Hourglass < 0.) // on veut un accroissement d'énergie positive (1/2 k deltaX ^2)
		    { coefMultiplicatif = -coefStabHourglass;};
     // grandeurs finales avec le bon signe
		   res *= coefMultiplicatif;
     // calcul définitif: correspond à une grandeur qui peut être comparée avec l'énergie élastique totale
     E_Hourglass = 0.5 * (X * (res));
		   // on met à jour le résidu de l'élément principal
		   (*residu) -= res;
		  };
	 };
	 break;
	case STABHOURGLASS_NON_DEFINIE : 
			// on ne fait rien
	 break;
	default :
			cout << "\n*** Erreur : cas de gestop, d'hourglass non defini !\n";
			cout << "\n ElemMeca::Cal_implicit_hourglass() \n";
			Sortie(1);
  };
		
};
         
/*
// récupération de l'energie d'hourglass éventuelle
double ElemMeca::Energie_Hourglass()
{double enerHourglass = 0.;
 switch (type_stabHourglass)
 {case STABHOURGLASS_PAR_COMPORTEMENT :
	    { // on récupère les énergies stockées à l'élément
	      const EnergieMeca& energieTotale = tab_elHourglass(1)->EnergieTotaleElement();
		     enerHourglass = coefStabHourglass * (energieTotale.EnergieElastique() 
							                + energieTotale.DissipationPlastique() + energieTotale.DissipationVisqueuse()); 
	      break;
	    }
  case STABHOURGLASS_PAR_COMPORTEMENT_REDUIT :
	  {// --- l'énergie est purement une énergie élastique du type 0.5 <D_X> [K] (D_X) ---
    if (raid_hourglass_transitoire == NULL)
      { enerHourglass = 0.;} // car rien n'a été calculé
		  else
		  { int NBN = tab_noeud.Taille(); // récup du nombre de noeuds
      int dim = ParaGlob::Dimension();
      Vecteur& D_X = vec_hourglass(1); // pour simplier
      D_X.Change_taille(NBN*dim); // changement de la taille au cas où
      for (int ine=1;ine<=NBN;ine++)
       { Noeud& noe = *(tab_noeud(ine)); // pour simplifier
         Coordonnee delta_X = noe.Coord2() - noe.Coord1();
         int ipos = (ine-1)*dim;
         switch (dim)
          { case 3: {D_X(ipos+1)=delta_X(1);D_X(ipos+2)=delta_X(2);D_X(ipos+3)=delta_X(3);break;}
            case 2: {D_X(ipos+1)=delta_X(1);D_X(ipos+2)=delta_X(2);break;}
            case 1: {D_X(ipos+1)=delta_X(1);break;}
          };
       };
      Vecteur& res = vec_hourglass(2); // pour simplier
      res.Change_taille(NBN*dim); // changement de la taille au cas où
      // on multiplie la matrice par le vecteur delta_X 
      res = raid_hourglass_transitoire->Prod_mat_vec(D_X);
      enerHourglass = 0.5 * (D_X * res);
//debug
//cout << " , " << enerHourglass;
//fin debug
     
		  };
    break;
	  case STABHOURGLASS_NON_DEFINIE : 
			// on ne fait rien
	  break;
   }
 };
 return enerHourglass;
};*/

// -------------------- stabilisation transversale de membrane ou biel  -------------------

// mise à jour de "a_calculer" en fonction du contexte
// NB: pour l'instant ne concerne que le calcul en implicite
void ElemMeca::Mise_a_jour_A_calculer_force_stab()
{if (pt_StabMembBiel != NULL)
  {pt_StabMembBiel->Aa_calculer() = true; // on init par défaut
   Enum_StabMembraneBiel enu_stab = pt_StabMembBiel->Type_stabMembrane();
   switch (enu_stab)
    { case STABMEMBRANE_BIEL_PREMIER_ITER: case STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER:
      case STABMEMBRANE_BIEL_PREMIER_ITER_NORMALE_AU_NOEUD:
      case STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER_NORMALE_AU_NOEUD:
       {// on récupère le pointeur correspondant aux iteration
        const void* pointe =  (ParaGlob::param->GrandeurGlobal(COMPTEUR_ITERATION_ALGO_GLOBAL));
        if (pointe == NULL)
          { cout << "\n *** pb dans la stabilisation de membrane et/ou biel "
                 << " le type de stabilisation est  " << Nom_StabMembraneBiel(enu_stab)
                 << "et la variable globale COMPTEUR_ITERATION_ALGO_GLOBAL n'est pas disponible"
                 << "  on ne peut pas continuer  "
                 << "\nElemMeca::Mise_a_jour_A_calculer_force_stab(..."<<endl;
            Sortie(1);
          };
        // récup du conteneur du compteur
        TypeQuelconque* gr_quelc = (TypeQuelconque*) (pointe);
        Grandeur_scalaire_entier& gr
                        = *((Grandeur_scalaire_entier*) gr_quelc->Grandeur_pointee()); // pour simplifier
        int compteur_iter = *(gr.ConteneurEntier());
        if (compteur_iter > 1) // cela veut dire que la force de stabilisation a déjà été calculé
          {pt_StabMembBiel->Aa_calculer() = false;}
        else // sinon il faut la calculer
          {pt_StabMembBiel->Aa_calculer() = true;};
        break;
       }
      case STABMEMBRANE_BIEL_PREMIER_ITER_INCR: case STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER_INCR:
      case STABMEMBRANE_BIEL_PREMIER_ITER_INCR_NORMALE_AU_NOEUD:
      case STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER_INCR_NORMALE_AU_NOEUD:
       {int compteur_iter = 0;// init
        {// on récupère le pointeur correspondant aux iteration
         const void* pointe =  (ParaGlob::param->GrandeurGlobal(COMPTEUR_ITERATION_ALGO_GLOBAL));
         if (pointe == NULL)
           { cout << "\n *** pb dans la stabilisation de membrane et/ou biel "
                  << " le type de stabilisation est  " << Nom_StabMembraneBiel(enu_stab)
                  << "et la variable globale COMPTEUR_ITERATION_ALGO_GLOBAL n'est pas disponible"
                  << "  on ne peut pas continuer  "
                  << "\nElemMeca::Cal_implicit_StabMembBiel(..."<<endl;
             Sortie(1);
           };
         // récup du conteneur du compteur
         TypeQuelconque* gr_quelc = (TypeQuelconque*) (pointe);
         Grandeur_scalaire_entier& gr
                         = *((Grandeur_scalaire_entier*) gr_quelc->Grandeur_pointee()); // pour simplifier
         compteur_iter = *(gr.ConteneurEntier());
        }
        // on récupère le pointeur correspondant aux incréments
        int compteur_incr = 0; // init
        {const void* pointe =  (ParaGlob::param->GrandeurGlobal(COMPTEUR_INCREMENT_CHARGE_ALGO_GLOBAL));
         if (pointe == NULL)
          { cout << "\n *** pb dans la stabilisation de membrane et/ou biel "
                 << " le type de stabilisation est  STABMEMBRANE_BIEL_PREMIER_ITER_INCR "
                 << "et la variable globale COMPTEUR_INCREMENT_CHARGE_ALGO_GLOBAL n'est pas disponible"
                 << "  on ne peut pas continuer  "
                 << "\nElemMeca::Cal_implicit_StabMembBiel(..."<<endl;
            Sortie(1);
          };
         // récup du conteneur du compteur
         TypeQuelconque* gr_quelc = (TypeQuelconque*) (pointe);
         Grandeur_scalaire_entier& gr
                         = *((Grandeur_scalaire_entier*) gr_quelc->Grandeur_pointee()); // pour simplifier
         compteur_iter = *(gr.ConteneurEntier());
        }
        if ((compteur_incr > 1) && (compteur_iter > 1)) // cela veut dire que la force de stabilisation a déjà été calculé
          {pt_StabMembBiel->Aa_calculer() = false;}
        else // sinon il faut la calculer
          {pt_StabMembBiel->Aa_calculer() = true;};
        break;
       }
      default:
        cout << "\n *** erreur le type de stabilisation de membrane et/ou biel n'est pas definie "
             << " on ne peut pas continuer "
             << "\nElemMeca::Mise_a_jour_A_calculer_force_stab(..."<<endl;
        Sortie(1);
        break;
    };
  };
};

// stabilisation pour un calcul implicit,
// iteg -> donne le numéro du dernier pti sur lequel on a travaillé, auquel met est associé
//         iteg == 0 : un seul calcul global, et on est à la suite d'une  boucle
//         iteg == -1 : fin d'un calcul avec boucle sur tous les pti, et on est à la suite de la boucle
//                     correspond au calcul  d'alpha: == stab_ref
//         iteg entre 1 et nbint: on est dans une boucle de pti
// nbint : le nombre maxi de pti
// poids_volume : si iteg > 0 :  le poids d'intégration
//                 si iteg <= 0 :  l'intégrale de l'élément (ici la surface totale)
void ElemMeca::Cal_implicit_StabMembBiel(int iteg,const Met_abstraite::Impli& ex, const int& nbint
                                         ,const double& poids_volume
                                         ,Tableau <int> * noeud_a_prendre_en_compte)
{// l'idée est de bloquer la direction normale à l'élément pour une plaque et
 // les deux directions transverses pour une biel
 int niveau_affichage = ParaGlob::NiveauImpression();
 if (pt_StabMembBiel->Affichage_stabilisation() > 0)
    niveau_affichage = pt_StabMembBiel->Affichage_stabilisation();

 //---- détermination de l'intensité de stabilisation
 // celle-ci n'est calculé que dans le cas ou iteg == 0
 // c'est à dire que l'on n'est pas dans une boucle sur les pti, dans la méthode appelant
 double alpha = 0.;  // init
 bool stab_utilisable = false; // init
// bool alpha_vient_etre_calculee = false;
 Enum_StabMembraneBiel enu_stab = pt_StabMembBiel->Type_stabMembrane();
 switch (enu_stab)
  { case STABMEMBRANE_BIEL_PREMIER_ITER: case STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER:
    case STABMEMBRANE_BIEL_PREMIER_ITER_NORMALE_AU_NOEUD :
    case STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER_NORMALE_AU_NOEUD:
     {// on récupère le pointeur correspondant aux iteration
      const void* pointe =  (ParaGlob::param->GrandeurGlobal(COMPTEUR_ITERATION_ALGO_GLOBAL));
      if (pointe == NULL)
        { cout << "\n *** pb dans la stabilisation de membrane et/ou biel "
               << " le type de stabilisation est " << Nom_StabMembraneBiel(enu_stab)
               << "et la variable globale COMPTEUR_ITERATION_ALGO_GLOBAL n'est pas disponible"
               << "  on ne peut pas continuer  "
               << "\nElemMeca::Cal_implicit_StabMembBiel(..."<<endl;
          Sortie(1);
        };
      // récup du conteneur du compteur
      TypeQuelconque* gr_quelc = (TypeQuelconque*) (pointe);
      Grandeur_scalaire_entier& gr
                      = *((Grandeur_scalaire_entier*) gr_quelc->Grandeur_pointee()); // pour simplifier
      int compteur_iter = *(gr.ConteneurEntier());
      
      if ((iteg > 0) && (compteur_iter > 1)) // cela veut dire que la la référence de stabilisation a déjà été calculée
        {alpha = pt_StabMembBiel->Stab_ref(); // récup de l'intensité de stabilisation
         // ce n'est pas un pointeur, alpha est une variable locale
         
         // dans le cas d'une fonction multiplicatrice ,
         // et il faut appeler la fonction qui peut varier pendant les itérations
         if (pt_StabMembBiel->Pt_fct_gamma() != NULL)
           // cas où il y a une fonction nD
           {Fonction_nD* fct = pt_StabMembBiel->Pt_fct_gamma(); // récupération de la fonction nD
            // on commence par récupérer les conteneurs des grandeurs à fournir
            List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
            List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
            bool absolue = true; // on se place systématiquement en absolu
            // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
            int cas = -1; // signifie que l'on utilise la métrique qui vient juste d'être calculée
            Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi(absolue,TEMPS_tdt,li_enu_scal,iteg,-cas));
            // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
            Valeurs_Tensorielles(absolue, TEMPS_tdt,li_quelc,iteg,-cas);
            // calcul de la valeur et retour dans tab_ret
            Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
            // seule la première valeur est ok
            alpha *= tab_ret(1) ;
            pt_StabMembBiel->Valgamma() = alpha; // on sauvegarde la valeur d'alpha
           }
         else // sinon cela veut dire que l'intensité de stabilisation est fixe
           {alpha *= pt_StabMembBiel->Valgamma();}
         stab_utilisable=true;
        }
      else if ((pt_StabMembBiel->Aa_calculer())
               && (iteg == 0)
              ) // cas ou il faut calculer la référence de stabilisation, mais on ne le fait qu'ici car
        // on est sortie de la boucle sur des pti, de manière à utiliser la raideur totale
        {// on récupère la valeur maxi de la raideur
         int i,j; // def
         double& maxival = pt_StabMembBiel->Stab_ref(); // init
         // là il s'agit d'un pointeur donc on travaille directement sur la grandeur stockée
         
         if (  (enu_stab == STABMEMBRANE_BIEL_PREMIER_ITER)
             ||(enu_stab == STABMEMBRANE_BIEL_PREMIER_ITER_NORMALE_AU_NOEUD))
          {maxival = (*raideur).MaxiValAbs(i,j) / poids_volume;
//           alpha_vient_etre_calculee = true;
          }
         else if (  (enu_stab == STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER)
                  ||(enu_stab == STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER_NORMALE_AU_NOEUD))
          {maxival = (*raideur).Maxi_ligne_ValAbs(i) / poids_volume;
//           alpha_vient_etre_calculee = true;
          }
         else
          { cout << "\n *** pb dans la stabilisation de membrane et/ou biel "
                 << " le type de stabilisation est " << Nom_StabMembraneBiel(enu_stab)
                 << " pas pris en compte pour l'instant .... "
                 << "  on ne peut pas continuer  "
                 << "\nElemMeca::Cal_implicit_StabMembBiel(..."<<endl;
            Sortie(1);
          };
         // arrivée ici Stab_ref() a une valeur
         
         // maintenant on va calculer la valeur locale d'alpha
         // on récupère la stabilisation pour lui donner une valeur
         if (pt_StabMembBiel->Pt_fct_gamma() != NULL)
           // cas où il y a une fonction nD
           {Fonction_nD* fct = pt_StabMembBiel->Pt_fct_gamma(); // récupération de la fonction nD
            // on commence par récupérer les conteneurs des grandeurs à fournir
            List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
            List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
            bool absolue = true; // on se place systématiquement en absolu
            // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
            int cas = -1; // signifie que l'on utilise la métrique qui vient juste d'être calculée
            // ici c'est la métrique correspondante au dernier pti
            // comme iteg == 0 on est donc sortie de la boucle sur les pti, le dernier pti c'est nbint
            Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi(absolue,TEMPS_tdt,li_enu_scal,nbint,-cas));
            // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
            Valeurs_Tensorielles(absolue, TEMPS_tdt,li_quelc,nbint,-cas);
            // calcul de la valeur et retour dans tab_ret
            Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
             // seule la première valeur est ok
             alpha = tab_ret(1) * maxival;
             pt_StabMembBiel->Valgamma() = alpha; // on sauvegarde la valeur d'alpha
           }
          else // sinon  cela veut dire que l'intensité de stabilisation est fixe ;
            {alpha = pt_StabMembBiel->Valgamma() * maxival; // on prend une proportion du maxi
            };
          stab_utilisable = true;
         }
      else if (iteg == -1)//  cas de la fin d'une boucle, on passe car il n'y a rien à faire
       {if (pt_StabMembBiel->Pt_fct_gamma() != NULL)
          {alpha = pt_StabMembBiel->Valgamma();} // on récupère la dernière valeur d'alpha calculée
        else // sinon il s'agit d'une valeur fixe, on recalcule alpha
          {alpha = pt_StabMembBiel->Valgamma() * pt_StabMembBiel->Stab_ref();};
        stab_utilisable = true;
       }
      else //  ce n'est pas prévu
       { cout << "\n *** erreur, iteg = "<< iteg //<< " local= "<< local
              << " cas non prevu dans la stabilisation "
              << "\n ElemMeca::Cal_implicit_StabMembBiel(..";
         Sortie(1);
       };

         
      break;
     }
    case STABMEMBRANE_BIEL_PREMIER_ITER_INCR: case STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER_INCR:
    case STABMEMBRANE_BIEL_PREMIER_ITER_INCR_NORMALE_AU_NOEUD:
    case STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER_INCR_NORMALE_AU_NOEUD:
     {int compteur_iter = 0;// init
      // on récupère le pointeur correspondant aux iteration
      {const void* pointe =  (ParaGlob::param->GrandeurGlobal(COMPTEUR_ITERATION_ALGO_GLOBAL));
       if (pointe == NULL)
         { cout << "\n *** pb dans la stabilisation de membrane et/ou biel "
                << " le type de stabilisation est " << Nom_StabMembraneBiel(enu_stab)
                << "et la variable globale COMPTEUR_ITERATION_ALGO_GLOBAL n'est pas disponible"
                << "  on ne peut pas continuer  "
                << "\nElemMeca::Cal_implicit_StabMembBiel(..."<<endl;
           Sortie(1);
         };
       // récup du conteneur du compteur
       TypeQuelconque* gr_quelc = (TypeQuelconque*) (pointe);
       Grandeur_scalaire_entier& gr
                       = *((Grandeur_scalaire_entier*) gr_quelc->Grandeur_pointee()); // pour simplifier
       compteur_iter = *(gr.ConteneurEntier());
      }
      // on récupère le pointeur correspondant aux incréments
      int compteur_incr = 0; // init
      {const void* pointe =  (ParaGlob::param->GrandeurGlobal(COMPTEUR_INCREMENT_CHARGE_ALGO_GLOBAL));
       if (pointe == NULL)
        { cout << "\n *** pb dans la stabilisation de membrane et/ou biel "
               << " le type de stabilisation est " << Nom_StabMembraneBiel(enu_stab)
               << "et la variable globale COMPTEUR_INCREMENT_CHARGE_ALGO_GLOBAL n'est pas disponible"
               << "  on ne peut pas continuer  "
               << "\nElemMeca::Cal_implicit_StabMembBiel(..."<<endl;
          Sortie(1);
        };
       // récup du conteneur du compteur
       TypeQuelconque* gr_quelc = (TypeQuelconque*) (pointe);
       Grandeur_scalaire_entier& gr
                       = *((Grandeur_scalaire_entier*) gr_quelc->Grandeur_pointee()); // pour simplifier
       compteur_iter = *(gr.ConteneurEntier());
      }

      if ((iteg != 0) && (compteur_incr > 1) && (compteur_iter > 1)) // cela veut dire que la la référence de stabilisation a déjà été calculée
        {alpha = pt_StabMembBiel->Stab_ref(); // init de l'intensité de stabilisation
         // dans le cas d'une fonction, la partie liée à la raideur est stockée dans Valgamma()
         // et il faut appeler la fonction qui peut varier pendant les itérations
         if (pt_StabMembBiel->Pt_fct_gamma() != NULL)
           // cas où il y a une fonction nD
           {Fonction_nD* fct = pt_StabMembBiel->Pt_fct_gamma(); // récupération de la fonction nD
            // on commence par récupérer les conteneurs des grandeurs à fournir
            List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
            List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
            bool absolue = true; // on se place systématiquement en absolu
            // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
            int cas = -1; // signifie que l'on utilise la métrique qui vient juste d'être calculée
            Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi(absolue,TEMPS_tdt,li_enu_scal,iteg,-cas));
            // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
            Valeurs_Tensorielles(absolue, TEMPS_tdt,li_quelc,iteg,-cas);
            // calcul de la valeur et retour dans tab_ret
            Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
             // seule la première valeur est ok
             alpha *= tab_ret(1);
             pt_StabMembBiel->Valgamma() = alpha; // on sauvegarde la valeur d'alpha
           }
         else // sinon cela veut dire que l'intensité de stabilisation est fixe
            {alpha *= pt_StabMembBiel->Valgamma();}
          stab_utilisable=true;
        }
      else if ((pt_StabMembBiel->Aa_calculer())
             && (iteg == 0)
            ) // cas ou il faut calculer la référence de stabilisation, mais on ne le fait qu'ici car
      // on est sortie de la boucle sur des pti, de manière à utiliser la raideur totale
        {// on récupère la valeur maxi de la raideur
         int i,j; // def
         double& maxival = pt_StabMembBiel->Stab_ref(); // init
         if (   (enu_stab == STABMEMBRANE_BIEL_PREMIER_ITER_INCR)
             || (enu_stab == STABMEMBRANE_BIEL_PREMIER_ITER_INCR_NORMALE_AU_NOEUD))
          {maxival = (*raideur).MaxiValAbs(i,j);
//           alpha_vient_etre_calculee = true;
          }
         else if (  (enu_stab == STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER_INCR)
                  ||(enu_stab == STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER_INCR_NORMALE_AU_NOEUD))
          {maxival = (*raideur).Maxi_ligne_ValAbs(i);
//           alpha_vient_etre_calculee = true;
          }
         else
          { cout << "\n *** pb dans la stabilisation de membrane et/ou biel "
                 << " le type de stabilisation est " << Nom_StabMembraneBiel(enu_stab)
                 << " pas pris en compte pour l'instant .... "
                 << "  on ne peut pas continuer  "
                 << "\nElemMeca::Cal_implicit_StabMembBiel(..."<<endl;
            Sortie(1);
          };
         
         // on récupère la stabilisation
         if (pt_StabMembBiel->Pt_fct_gamma() != NULL)
           // cas où il y a une fonction nD
           {Fonction_nD* fct = pt_StabMembBiel->Pt_fct_gamma(); // récupération de la fonction nD
            // on commence par récupérer les conteneurs des grandeurs à fournir
            List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
            List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
            bool absolue = true; // on se place systématiquement en absolu
            // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
            int cas = -1; // signifie que l'on utilise la métrique qui vient juste d'être calculée
            // ici c'est la métrique correspondante au dernier pti
            // comme iteg == 0 on est donc sortie de la boucle sur les pti, le dernier pti c'est nbint
            Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi(absolue,TEMPS_tdt,li_enu_scal,nbint,-cas));
            // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
            Valeurs_Tensorielles(absolue, TEMPS_tdt,li_quelc,nbint,-cas);
            // calcul de la valeur et retour dans tab_ret
            Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
             // seule la première valeur est ok
             alpha = tab_ret(1) * maxival;
             pt_StabMembBiel->Valgamma() = alpha; // on sauvegarde la valeur d'alpha
            }
         else // sinon  cela veut dire que l'intensité de stabilisation est fixe ;
           {alpha = pt_StabMembBiel->Valgamma() * maxival; // on prend une proportion du maxi
           };
         stab_utilisable = true;
         }
      else if (iteg == -1)//  cas de la fin d'une boucle, on passe car il n'y a rien à faire
       {if (pt_StabMembBiel->Pt_fct_gamma() != NULL)
          {alpha = pt_StabMembBiel->Valgamma();} // on récupère la dernière valeur d'alpha calculée
        else // sinon il s'agit d'une valeur fixe, on recalcule alpha
          {alpha = pt_StabMembBiel->Valgamma() * pt_StabMembBiel->Stab_ref();};
        stab_utilisable = true;
       }
      else //  ce n'est pas prévu
       { cout << "\n *** erreur, iteg = "<< iteg //<< " local= "<< local
              << " cas non prevu dans la stabilisation "
              << "\n ElemMeca::Cal_implicit_StabMembBiel(..";
         Sortie(1);
       };
      break;
     }
/*
//    case STAB_M_B_ITER_1_VIA_F_EXT: case STAB_M_B_ITER_1_VIA_F_EXT_NORMALE_AU_NOEUD:
//     {// on récupère le pointeur correspondant aux iteration
//      const void* pointe =  (ParaGlob::param->GrandeurGlobal(COMPTEUR_ITERATION_ALGO_GLOBAL));
//      if (pointe == NULL)
//        { cout << "\n *** pb dans la stabilisation de membrane et/ou biel "
//               << " le type de stabilisation est " << Nom_StabMembraneBiel(enu_stab)
//               << "et la variable globale COMPTEUR_ITERATION_ALGO_GLOBAL n'est pas disponible"
//               << "  on ne peut pas continuer  "
//               << "\nElemMeca::Cal_implicit_StabMembBiel(..."<<endl;
//          Sortie(1);
//        };
//      // récup du conteneur du compteur
//      TypeQuelconque* gr_quelc = (TypeQuelconque*) (pointe);
//      Grandeur_scalaire_entier& gr
//                      = *((Grandeur_scalaire_entier*) gr_quelc->Grandeur_pointee()); // pour simplifier
//      int compteur_iter = *(gr.ConteneurEntier());
//      if (compteur_iter > 1) // cela veut dire que la force de stabilisation a déjà été calculé
//        {// dans le cas d'une fonction, la partie liée à la raiseur est stockée dans Valgamma()
//         // et il faut appeler la fonction qui peut varier pendant les itérations
//         if (pt_StabMembBiel->Pt_fct_gamma() != NULL)
//           // cas où il y a une fonction nD
//           { Tableau <double>& tab_val = pt_StabMembBiel->Pt_fct_gamma()->Valeur_pour_variables_globales();
//             // seule la première valeur est ok
//             alpha = tab_val(1) * pt_StabMembBiel->Valgamma();
//             pt_StabMembBiel->FF_StabMembBiel() = alpha; // on sauvegarde
//           }
//         else // sinon cela veut dire que l'intensité de stabilisation est fixe
//          {alpha = pt_StabMembBiel->FF_StabMembBiel();}
//         alpha_utilisable=true;
//        }
//      else if (!local) // sinon il faut la calculer, mais on ne le fait que
//       // si on est sortie de la boucle sur des pti,
//        {// ***** à faire ***** on récupère la valeur maxi de la raideur
//         { cout << "\n *** pb dans la stabilisation de membrane et/ou biel "
//                << " partie pas operationnelle pour l'instant !! "
//                << "  on ne peut pas continuer  "
//                << "\nElemMeca::Cal_implicit_StabMembBiel(..."<<endl;
//           Sortie(1);
//         };
//         int i,j; // def
//         double maxival = 0.; // init
//         if (  (enu_stab == STABMEMBRANE_BIEL_PREMIER_ITER)
//             ||(enu_stab == STABMEMBRANE_BIEL_PREMIER_ITER_NORMALE_AU_NOEUD))
//          {maxival = (*raideur).MaxiValAbs(i,j);
//           alpha_vient_etre_calculee = true;
//          }
//         else if (  (enu_stab == STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER)
//                  ||(enu_stab == STABMEMBRANE_BIEL_Gerschgorin_PREMIER_ITER_NORMALE_AU_NOEUD))
//          {maxival = (*raideur).Maxi_ligne_ValAbs(i);
//           alpha_vient_etre_calculee = true;
//          }
//         else
//          { cout << "\n *** pb dans la stabilisation de membrane et/ou biel "
//                 << " le type de stabilisation est " << Nom_StabMembraneBiel(enu_stab)
//                 << " pas pris en compte pour l'instant .... "
//                 << "  on ne peut pas continuer  "
//                 << "\nElemMeca::Cal_implicit_StabMembBiel(..."<<endl;
//            Sortie(1);
//          };
//         // on récupère la stabilisation
//         alpha=0.; // init
//         if (pt_StabMembBiel->Pt_fct_gamma() != NULL)
//           // cas où il y a une fonction nD
//           { Tableau <double>& tab_val = pt_StabMembBiel->Pt_fct_gamma()->Valeur_pour_variables_globales();
//             // seule la première valeur est ok
//             alpha = tab_val(1) * maxival;
//             pt_StabMembBiel->Valgamma() = maxival; // sauvegarde
//           }
//         else // sinon  cela veut dire que l'intensité de stabilisation est fixe ;
//           {alpha = pt_StabMembBiel->Valgamma() * maxival; // on prend une proportion du maxi
//           };
//         pt_StabMembBiel->FF_StabMembBiel() = alpha; // on sauvegarde
//         alpha_utilisable = true;
//        };
//      break;
//     }
*/

    default:
      cout << "\n *** erreur le type de stabilisation de membrane et/ou biel n'est pas definie "
           << " on ne peut pas continuer " << endl;
      Sortie(1);
      break;
  };
 
 // si la stabilisation est utilisable on s'en sert
 if (stab_utilisable)
// if (   (alpha_utilisable)
//        // et soit la stabilisation vient juste d'être calculée, donc on est en dehors de la boucle sur les pti
//        // donc on calcule globalement l'impact sur la raideur
//        // ou soit on est en local : et on calcul localement l'impact sur la raideur, mais à la sortie de la boucle
//        // on ne recalculera pas en globa
//     && (alpha_vient_etre_calculee || local)
//    )
   {// choix en fonction de la géométrie
    Enum_type_geom enu_type_geom = Type_geom_generique(this->Id_geometrie());
    switch (enu_type_geom)
     { case SURFACE:
//#ifdef MISE_AU_POINT
// if (num_elt==25) // -----debug à virer
//  {cout << "\n ElemMeca::Cal_implicit_StabMembBiel(..";
//   cout << "\n iteg= "<< iteg << flush;
//   };
//#endif
        if (!Contient_Normale_au_noeud(enu_stab))
         // cas où on utilise la normale au pti
          {// la normale vaut le produit vectoriel des 2 premiers vecteurs
           Coordonnee normale = Util::ProdVec_coor( (*ex.giB_tdt).Coordo(1), (*ex.giB_tdt).Coordo(2));
           // calcul de la variation de la normale
           // 1) variation du produit vectoriel
           Tableau <Coordonnee > D_pasnormale =
                    Util::VarProdVect_coor( (*ex.giB_tdt).Coordo(1),(*ex.giB_tdt).Coordo(2),(*ex.d_giB_tdt));
           // 2) de la normale
           Tableau <Coordonnee> D_normale = Util::VarUnVect_coor(normale,D_pasnormale,normale.Norme());
           
           // calcul de la normale normée et pondérée par la stabilisation
           normale.Normer();
           int nbddl = (*residu).Taille();
           int dimf = 3;   // ne fonctionne qu'en dim 3
           if(ParaGlob::AxiSymetrie())
             // cas  axisymétrique, dans ce cas on ne prend en compte que les
             // dimf-1 coordonnées donc on décrémente
             dimf--;
           int nbne = tab_noeud.Taille();
           double ponder_F_t = 1.; // init
           
           // si iteg == 0 ==> un seul calcul, global: correspond au calcul  d'alpha: == stab_ref
           // si teg == 1 ==> premier calcul correspondant au premier pti
           if ((iteg == 0) || (iteg == 1))
            { matD->Initialise(0.);
              resD->Inita(0.);
              maxi_F_t = 0.;
              // on initialise les forces et énergie
              int taille = pt_StabMembBiel->Taille();
              for (int i=1;i<=taille;i++)
                 {pt_StabMembBiel->FF_StabMembBiel(i) = 0.;
                  pt_StabMembBiel->EE_StabMembBiel(i) = pt_StabMembBiel->EE_StabMembBiel_t(i);
                 }
            };
           // on récupère l'élément géométrique
           ElemGeomC0& elemGeom = this->ElementGeometrique();
           // récup des fonctions d'interpolation
           const Tableau <Vecteur>& taphi = elemGeom.TaPhi();

           int nb_noeud_pris_en_compte = 0; //pour faire la moyenne dans le cas iteg == 0
           // on applique une  force de stabilisation
           // uniquement suivant la direction de la normale
           int num_pti=iteg; // init
           if (iteg <= 0) num_pti = nbint;
           // la force est proportionelle au déplacement suivant la normale
           if (iteg > -1) // c-a-d == 0 ou plus
            { int ix=1;
              for (int ne =1; ne<= nbne; ne++)
               {bool continuer = true;
                if (noeud_a_prendre_en_compte != NULL)
                 if (!(*noeud_a_prendre_en_compte)(ne))
                   continuer = false;
                if (continuer)
                  {Noeud& noe = *(tab_noeud(ne)); // pour simplifier
                   nb_noeud_pris_en_compte++;
               
                   // on va bloquer le déplacement dans la direction de la normale calculée au pti
                   
                   //dans un calcul implicit on utilise la position à l'itération 0
                   // celle-ci peut-être différente de celle à t=0, lorsque l'on a une succession d'algo sur le même incrément
                   //  on commence par récupérer les coordonnées pour l'itération 0
                   TypeQuelconque_enum_etendu enu(XI_ITER_0);
                   // récupération du conteneur pour lecture uniquement
                   const TypeQuelconque& typquel = noe.Grandeur_quelconque(enu);
                   const  Grandeur_coordonnee& cofo = *((Grandeur_coordonnee*) typquel.Const_Grandeur_pointee());
                   Coordonnee delta_X = noe.Coord2() - cofo.ConteneurCoordonnee_const();
                  
                   double normal_fois_delta_X = normale * delta_X; // le déplacement au noeud suivant la normale au pti
                   // dans le cas où on veut pondérer F_t, on calcule la fonction
                   if (pt_StabMembBiel->Pt_fct_ponder_Ft() != NULL)
                     {Fonction_nD* fct = pt_StabMembBiel->Pt_fct_ponder_Ft(); // récupération de la fonction nD
                      // on commence par récupérer les conteneurs des grandeurs à fournir
                      List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
                      List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
                      bool absolue = true; // on se place systématiquement en absolu
                      // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
                      int cas = -1; // signifie que l'on utilise la métrique qui vient juste d'être calculée
                      Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi
                             (absolue,TEMPS_tdt,li_enu_scal,num_pti,-cas));
                      // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
                      Valeurs_Tensorielles(absolue, TEMPS_tdt,li_quelc,num_pti,-cas);
                      // calcul de la valeur et retour dans tab_ret
                      Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
                      // seule la première valeur est ok
                      ponder_F_t = tab_ret(1) ;
                     }
                   // un - car la force doit s'opposer au déplacement
                   double intensite = -alpha * normal_fois_delta_X
                                      + ponder_F_t * pt_StabMembBiel->FF_StabMembBiel_t(num_pti);
                   // on enregistre le maxi de la force à t
                   maxi_F_t = DabsMaX(maxi_F_t, pt_StabMembBiel->FF_StabMembBiel_t(num_pti));

                   // d'où une variation réelle d'intensité
                   double delta_intensite = intensite - ponder_F_t * pt_StabMembBiel->FF_StabMembBiel_t(num_pti);
                   pt_StabMembBiel->EE_StabMembBiel(num_pti) += 0.5 * delta_intensite * normal_fois_delta_X;
                   #ifdef MISE_AU_POINT
                   if (niveau_affichage>5)
 //                   if (num_elt==25) // -----debug à virer
                     {cout << "\n ElemMeca::Cal_implicit_StabMembBiel(..";
                      cout << "\n noeud ("<<ne<<"), alpha= "<<alpha << " intensite= "<< intensite
                           << " delta_intensite= " << delta_intensite
                           << " delta_X= "<< delta_X
                           << "\n normale= "<<normale << " normal_fois_delta_X= " << normal_fois_delta_X << flush;
                      };
                   #endif

//                   //----- debug -----
//                   if (num_elt==25) // -----debug à virer
//                      {int nb_pti = pt_StabMembBiel->Taille();
//                       for (int i=1;i<=nb_pti;i++)
//                        {cout << "\n avant update F_stab_t("<<i<<")= "<<pt_StabMembBiel->FF_StabMembBiel_t(i)<< " ";
//                         cout << " F_stab("<<i<<")= "<<pt_StabMembBiel->FF_StabMembBiel(i)<< " ";
//                         cout << " ponder_F_t= " << ponder_F_t;
//                        }
//                      };
//                   //--- fin debug ----
                   // calcul de la raideur: on traite les cas :
                   // iteg >0 et iteg == 0
                   // le cas iteg == -1 n'est pas à prendre en compte car il correspond
                   // au cas où on vient de balayer tous les pti
                   if (iteg > 0) // cas où on est dans une boucle d'intégration
                    {// un premier coef pour optimiser
                     double phi_jacob_poids = taphi(iteg)(ne)  * (*ex.jacobien_tdt) * poids_volume ;

                     pt_StabMembBiel->FF_StabMembBiel(iteg) += intensite * taphi(iteg)(ne); // * phi_jacob_poids;
     //                    taphi(iteg)(ne) * intensite * (*ex.jacobien_tdt) * poids_volume ;
                     // NB: 1) comme on multiplie par taphi(iteg)(ne), et que l'on boucle sur les noeuds (ne = 1 à nbne)
                     //     au sortir de la boucle on a l'intensité totale qui est due au déplacement de tous les noeuds
                     //     2) si alpha * normal_fois_delta_X = 0 c-a-d pas d'accroissement d'intensité, on va obtenir
                     //     somme_noeud(ponder_F_t * pt_StabMembBiel->FF_StabMembBiel_t(num_pti) * taphi(iteg)(ne))
                     //     et comme somme_noeud(taphi(iteg)(ne))=1, on devrait obtenir une intensité globale identique
                     //     à celle à t
                     
                     pt_StabMembBiel->EE_StabMembBiel(iteg) += 0.5 * intensite * normal_fois_delta_X * phi_jacob_poids;
     //                  0.5 * delta_intensite * normal_fois_delta_X * taphi(iteg)(ne) * (*ex.jacobien_tdt) * poids_volume ;
                 
                     // un second coef pour optimiser
                     double phi_poids = taphi(iteg)(ne)  * poids_volume ;
                  
//             pour mémoire: intensite = -alpha * normal_fois_delta_X
//                                     + pt_StabMembBiel->FF_StabMembBiel_t(num_pti);
                     for (int i=1;i<= dimf;i++,ix++)
                      {  // =    (- normale(i) * alpha * normal_fois_delta_X + F_t)
                         //    * (taphi(iteg)(ne) * poids * jacobien)  ;
                         (*resD)(ix) += normale(i) * intensite * phi_jacob_poids;
//                                        taphi(iteg)(ne)* normale(i) * intensite
//                                        * (*ex.jacobien_tdt) * poids_volume ; // ok
                         //  contribution à la raideur: de signe inverse à celui du résidu
                         int j=1;
                         int me = 1; // numéro de noeud dans la direction des ddl
                 
                         for (int jx = 1; jx <= nbddl; jx++,j++)
                           {if (j>dimf) {j=1;me++;};
                            (*matD)(ix,jx) += phi_poids // == taphi(iteg)(ne)  * poids_volume
                                              *( (*ex.jacobien_tdt) *
                                                 ( D_normale(jx)(i) * alpha * normal_fois_delta_X
                                                   + normale(i) * alpha * (D_normale(jx) * delta_X)
                                                 )
                                                + (*ex.d_jacobien_tdt)(jx) * normale(i) * (-intensite)
                                                );
                             if (ne == me)
                               {(*matD)(ix,jx) += phi_poids * normale(i) *  alpha  * normale(j) * (*ex.jacobien_tdt);
//                                                taphi(iteg)(ne)*  alpha * normale(i) * normale(j) * poids_volume;
                               };
                           };
                      };
                    }
                   else if (iteg == 0)// on est hors boucle poids_volume == toute la surface
                    {// pour le cas particulier d'iteg == 0, on n'a qu'un calcul global
                     // et on affecte la même intensité à tous les pti
                     // du coup on sauvegarde uniquement pour le dernier
                     // ensuite après on répartira sur l'ensemble de pti
                     // --> en fait on cumulle l'influence de tous les normales
                     // il faudra ensuite prendre la moyenne
                     
                     pt_StabMembBiel->FF_StabMembBiel(num_pti) += intensite ; //* poids_volume;
   //                                taphi(iteg)(ne) * intensite * poids_volume ;
                     pt_StabMembBiel->EE_StabMembBiel(num_pti) += 0.5 * intensite * normal_fois_delta_X * poids_volume;
   //                    0.5 * intensite * normal_fois_delta_X * taphi(iteg)(ne) *  poids_volume ;

                     for (int i=1;i<= dimf;i++,ix++)
                      {  // - normale(i) * alpha * normal_fois_delta_X;
                         (*resD)(ix) +=  normale(i) * intensite * poids_volume ; // ok
                         //  contribution à la raideur: de signe inverse à celui du résidu
                         int j=1;
                         int me = 1; // numéro de noeud dans la direction des ddl
                         for (int jx = 1; jx <= nbddl; jx++,j++)
                           {if (j>dimf) {j=1;me++;};
                            (*matD)(ix,jx) += poids_volume
                                               *(( D_normale(jx)(i) * alpha * normal_fois_delta_X
                                                   + normale(i) * alpha *  (D_normale(jx) * delta_X)
                                                  )
                                                );
                             if (ne == me)
                               {(*matD)(ix,jx) +=  poids_volume * normale(i) * alpha  * normale(j);
                               };
                           };
                      };
                    };
                    
                 }; // fin du test sur les noeuds à considérer
                 
              }; // fin de la boucle sur les noeuds
            };
            
           // dans le cas particulier de iteg == 0, c-a-d correspond au calcul  d'alpha: == stab_ref
           // on n'a pas bouclé sur les pti, c'est uniquement un calcul global qui a été effectué
           if (iteg == 0)
            { // on commence par moyenner: se rappeller que l'on a bouclé aussi sur les noeuds !
              int taille = pt_StabMembBiel->Taille();
              pt_StabMembBiel->FF_StabMembBiel(num_pti) /= (nb_noeud_pris_en_compte);
              pt_StabMembBiel->EE_StabMembBiel(num_pti) /= (nb_noeud_pris_en_compte);
              // ensuite on répartie la même moyenne sur tous les pti
              for (int i=1;i<=taille;i++)
                  {pt_StabMembBiel->FF_StabMembBiel(i) = pt_StabMembBiel->FF_StabMembBiel(num_pti);
                   pt_StabMembBiel->EE_StabMembBiel(i) = pt_StabMembBiel->EE_StabMembBiel(num_pti);
                  };
              // également sur le résidu et la raideur, car due à la boucle sur les noeuds
              if (nb_noeud_pris_en_compte > 0)
                 { double inverse = 1./nb_noeud_pris_en_compte;
                   (*matD) *= inverse;
                   (*resD) *= inverse;
                 };
            };
////----- debug -----
//   if (num_elt==25) // -----debug à virer
//   {int nb_pti = pt_StabMembBiel->Taille();
//    for (int i=1;i<=nb_pti;i++)
//      cout << "\n final F_stab("<<i<<")= "<<pt_StabMembBiel->FF_StabMembBiel(i)<< " ";
//   };
////--- fin debug ----

           // on est en dehors de la boucle, on effectue 2 choses
           // 1) application d'une limitation éventuelle des efforts
           // 2) prise en compte dans la raideur et résidu de l'élément, de la stabilisation
           
           if ((iteg == 0) || (iteg == -1)) // signifie que l'on est en dehors de la boucle
             {
              if (pt_StabMembBiel->Gestion_maxi_mini())
               {
                #ifdef MISE_AU_POINT
                if (niveau_affichage>9)
  //                 if (num_elt==1) // -----debug à virer
                   {cout << "\n residu de stabilisation avant limitation: "<< (*resD)
                         << "\n residu initial (sans stabilisation):      "<< (*residu)
                         << "\n maxi_F_t= " << maxi_F_t;
                   };
                if (niveau_affichage > 5)
  //                 if (num_elt==25) // -----debug à virer
                   {int nb_pti = pt_StabMembBiel->Taille();
                    for (int i=1;i<=nb_pti;i++)
                      cout << "\n F_stab("<<i<<")= "<<pt_StabMembBiel->FF_StabMembBiel(i)<< " ";
                   };
                #endif

                // 1) ---- on regarde s'il faut limiter la valeur de la stabilisation
                // et on prend en compte dans la raideur globale et le résidu global
                // la stabilisation
                bool limitation = false;
                double coef=1.; // coef de limitation éventuelle si limitation devient true
              
                // récup des limitations éventuelles
                double beta = pt_StabMembBiel->Beta();
                double F_mini = pt_StabMembBiel->F_mini();
                double d_maxi = pt_StabMembBiel->D_maxi();

                // on fait une boucle sur les noeuds
                int ix=1;
                for (int ne =1; ne<= nbne; ne++)
                 {bool continuer = true;
                  if (noeud_a_prendre_en_compte != NULL)
                   if (!(*noeud_a_prendre_en_compte)(ne))
                     continuer = false;
                  if (continuer)
                    {Noeud& noe = *(tab_noeud(ne)); // pour simplifier
                     // récup de la force de stabilisation généralisée au noeud
                     // c'est différent de celle au pti car elle inclue la surface de l'élément
                     Coordonnee force_stab(dimf); // init
                     for (int i=1;i<= dimf;i++,ix++)
                       force_stab(i) = (*resD)(ix);
                     double intensite_generalise = force_stab.Norme();
                   
                     // on récupère la force externe au noeud à l'instant t
                     TypeQuelconque_enum_etendu enu_ext(FORCE_GENE_EXT_t);
                     // récupération du conteneur pour lecture uniquement
                     const TypeQuelconque& typquel_ext = noe.Grandeur_quelconque(enu_ext);
                     const  Grandeur_coordonnee& cofo_ext = *((Grandeur_coordonnee*) typquel_ext.Const_Grandeur_pointee());

                     // on calcule l'intensité de la force externe selon la normale
                     double intensite_Fext = Dabs(cofo_ext.ConteneurCoordonnee_const() * normale);
                     // si l'intensité de la stabilisation est supérieure à beta * intensite_Fext
                     // on limite
                     
                     double maxi_intensite = beta * intensite_Fext;
                     double max_intens_via_Fext = maxi_intensite; // on sauve pour le cas où on est dep max
                     
                     // on applique l'algo théorique cf. doc théorique
  // erreur, maxi_intensité est une limitation, mais ce que l'on cherche à voir c'est
  // est-ce que les forces externes sont non-nulles, donc c'est intensite_Fext qu'il faut tester
  //                   if (maxi_intensite >= F_mini)
                     if (intensite_Fext >= F_mini)
                      // on est dans le cas où les forces externes sont à prendre en compte
                      {if ((intensite_generalise > maxi_intensite)
                           && (intensite_generalise > ConstMath::petit) // au cas où maxi_intensite et intensite très petites
                          )
                         {limitation = true;
                          if (niveau_affichage>3)
                            {cout << "\n limitation stabilisation due aux forces externes, F_stab gene=  "<< intensite_generalise
                                  << " ==> " << maxi_intensite;
                             if (niveau_affichage > 5)
                               cout << "\n   F_mini= "<< F_mini << " intensite_Fext= "<< intensite_Fext;
                            };
                          coef = DabsMiN(coef, maxi_intensite/intensite_generalise);
                         };
                       // sinon pas de pb donc on ne fait rien
                       }
                     else // cas où l'intensité dans la direction normale, des forces externes n'existent pas
                      // la limitation va s'exercée via un déplacement maxi
                      {maxi_intensite = Dabs(-alpha*d_maxi +  maxi_F_t);
                       {if ((intensite_generalise > maxi_intensite)
                         && (intensite_generalise > ConstMath::petit) // au cas où maxi_intensite et intensite très petites
                         && (Dabs(alpha) > ConstMath::pasmalpetit) // il faut qu'alpha ne soit pas nul
                        )
                         {limitation = true;
                          if ((niveau_affichage > 3))
                            {cout << "\n limitation stabilisation due deplace maxi acceptable, F_stab gene= "<< intensite_generalise
                                  << " ==> " << maxi_intensite
                                  << " (max_intens_via_Fext= "<<max_intens_via_Fext<<")" ;
                             if ((niveau_affichage > 4))
                                  cout << "\n  maxi_F_t= "<< maxi_F_t << " alpha*d_maxi= "
                                       << (alpha*d_maxi)
                                       << " alpha= " << alpha
                                       << " d_maxi= " << d_maxi;
                            };
                          coef = DabsMiN(coef, maxi_intensite/intensite_generalise);
                         };
                       };
                      };
                   
                    }; // fin du test sur les noeuds à considérer

                 };// fin de la boucle sur les noeuds
            
                if (limitation)
                 { (*resD) *= coef; // mise à jour de la force de stabilisation
                  // idem au niveau des pti
                  int taille = pt_StabMembBiel->Taille();
                  for (int i=1;i<=taille;i++)
                     {pt_StabMembBiel->FF_StabMembBiel(i) *=coef;
                      // cas des énergies
                      double delta_energie_initiale =
                          pt_StabMembBiel->EE_StabMembBiel(i) - pt_StabMembBiel->EE_StabMembBiel_t(i);
                      // on coefficiente le delta
                      double delta_energie = delta_energie_initiale * coef;
                      //ce qui donne au final:
                      pt_StabMembBiel->EE_StabMembBiel(i) =
                           delta_energie + pt_StabMembBiel->EE_StabMembBiel_t(i);
                     };
                 };
               };

              // 2) ----  on met à jour la raideur et résidu global
              // en tenant compte de la stabilisation
              (*residu) += (*resD);
              (*raideur) += (*matD);

//et du coup on ne boucle pas sur les pti et on peut limiter la force sauf qu'une fois le calcul des forces
//aux noeuds fait, il faut ramener par interpolation, la force au pti pour la sauvegarder... bordel
//non... on sauvegarde dans un tableau indicé par les num de noeud, et seulement pour le post-traitement
//on calcul la valeur aux pti
//
            
               #ifdef MISE_AU_POINT
               if ((niveau_affichage > 9))
//                  if (num_elt==1) // -----debug à virer
                  {cout << "\n residu de stabilisation: "<< (*resD);
                   cout << "\n raideur de stabilisation: "; matD->Affiche();
                   cout << "\n fin ElemMeca::Cal_implicit_StabMembBiel(..";
                  };
               #endif
             }; // fin du cas iteg == 0 ou -1
          } // fin du cas où on utilise la normale aux pti

        else
          {if (iteg < 1)
            // on ne calcul que si iteg < 1, c-a-d qu'on est sortie
            // de la boucle sur les pti
            // sinon cas où on utilise la normale aux noeuds
            {int nbddl = (*residu).Taille();
             int dimf = 3;   // ne fonctionne qu'en dim 3
             if(ParaGlob::AxiSymetrie())
               // cas  axisymétrique, dans ce cas on ne prend en compte que les
               // dimf-1 coordonnées donc on décrémente
               dimf--;
             int nbne = tab_noeud.Taille();
             matD->Initialise(0.);
             resD->Inita(0.);
             maxi_F_t = 0.;

             #ifdef MISE_AU_POINT
             // on vérifie le bon dimensionnement
             int taille = pt_StabMembBiel->Taille();
             if (taille != nbne)
               {cout << "\n erreur** ElemMeca::Cal_implicit_StabMembBiel(.."
                     << " le stockage est mal dimensionne: taille = " << taille
                     << " alors qu'il devrait etre : nbe = " << nbne << flush ;
                Sortie(1);
               };
             #endif

             // on applique une  force de stabilisation aux noeuds
             // uniquement suivant la direction de la normale
             // la force est proportionelle au déplacement suivant la normale au noeud
             int ix=1;
             for (int ne =1; ne<= nbne; ne++)
              {Noeud& noe = *(tab_noeud(ne)); // pour simplifier
              
               // on initialise les forces et énergie
               pt_StabMembBiel->FF_StabMembBiel(ne) = 0.;
               pt_StabMembBiel->EE_StabMembBiel(ne) = pt_StabMembBiel->EE_StabMembBiel_t(ne);

               // pour chaque noeud  on va bloquer le déplacement dans la direction de la normale au noeud à t
               // on récupère la normale au noeud
               const TypeQuelconque& tiq = noe.Grandeur_quelconque(NN_SURF_t);
               const Grandeur_coordonnee& gr= *((const Grandeur_coordonnee*) (tiq.Const_Grandeur_pointee()));
               const Coordonnee& normale = gr.ConteneurCoordonnee_const();


               //dans un calcul implicit on utilise la position à l'itération 0
               // celle-ci peut-être différente de celle à t=0, lorsque l'on a une succession d'algo sur le même incrément
               //  on commence par récupérer les coordonnées pour l'itération 0
               TypeQuelconque_enum_etendu enu(XI_ITER_0);
               // récupération du conteneur pour lecture uniquement
               const TypeQuelconque& typquel = noe.Grandeur_quelconque(enu);
               const  Grandeur_coordonnee& cofo = *((Grandeur_coordonnee*) typquel.Const_Grandeur_pointee());
               Coordonnee delta_X = noe.Coord2() - cofo.ConteneurCoordonnee_const();
               double normal_fois_delta_X = normale * delta_X;
               double ponder_F_t = 1.; // init
               // dans le cas où on veut pondérer F_t, on calcule la fonction
               if (pt_StabMembBiel->Pt_fct_ponder_Ft() != NULL)
                 {Fonction_nD* fct = pt_StabMembBiel->Pt_fct_ponder_Ft(); // récupération de la fonction nD
                  // on commence par récupérer les conteneurs des grandeurs à fournir
                  List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
                  List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
                  bool absolue = true; // on se place systématiquement en absolu
                  // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
                  int cas = -1; // signifie que l'on utilise la métrique qui vient juste d'être calculée
                  // au dernier pti !
                  Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi
                         (absolue,TEMPS_tdt,li_enu_scal,nbint,-cas));
                  // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
                  Valeurs_Tensorielles(absolue, TEMPS_tdt,li_quelc,nbint,-cas);
                  // calcul de la valeur et retour dans tab_ret
                  Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
                  // seule la première valeur est ok
                  ponder_F_t = tab_ret(1) ;
                 }

               // un - car la force doit s'opposer au déplacement
               double intensite = -alpha * normal_fois_delta_X
                                  + ponder_F_t * pt_StabMembBiel->FF_StabMembBiel_t(ne);
               // on enregistre le maxi de la force à t
               maxi_F_t = DabsMaX(maxi_F_t, pt_StabMembBiel->FF_StabMembBiel_t(ne));

               // d'où une variation réelle d'intensité
               double delta_intensite = intensite - ponder_F_t * pt_StabMembBiel->FF_StabMembBiel_t(ne);
               pt_StabMembBiel->EE_StabMembBiel(ne) += 0.5 * delta_intensite * normal_fois_delta_X;
               #ifdef MISE_AU_POINT
               if ((niveau_affichage > 5))
                 {cout << "\n ElemMeca::Cal_implicit_StabMembBiel(..";
                  cout << "\n alpha= "<<alpha << " intensite= "<< intensite
                       << " delta_intensite= " << delta_intensite
                       << " delta_X= "<< delta_X
                       << "\n normale= "<<normale << " normal_fois_delta_X= " << normal_fois_delta_X << flush;
                  };
               #endif

               //             pour mémoire: intensite = -alpha * normal_fois_delta_X
               //                                     + pt_StabMembBiel->FF_StabMembBiel_t(num_pti);
               for (int i=1;i<= dimf;i++,ix++)
                  { // - normale(i) * stabili * normal_fois_delta_X;
                    (*resD)(ix) +=  normale(i) * intensite ;
                    //  contribution à la raideur: de signe inverse à celui du résidu
                    int j=1;
                    int me = 1; // numéro de noeud dans la direction des ddl
                    for (int jx = 1; jx <= nbddl; jx++,j++)
                      {if (j>dimf) {j=1;me++;};
                       if (ne == me)
                          {(*matD)(ix,jx) +=  alpha * normale(i) * normale(j);
                          };
                       };
                  };

              }; // fin de la boucle sur les noeuds

              // on est en dehors de la boucle, on effectue 2 choses
              // 1) application d'une limitation éventuelle des efforts
              // 2) prise en compte dans la raideur et résidu de l'élément, de la stabilisation
              
              if (pt_StabMembBiel->Gestion_maxi_mini())
                {
                  #ifdef MISE_AU_POINT
                  if ((niveau_affichage > 9))
                     {cout << "\n residu de stabilisation avant limitation: "<< (*resD)
                           << "\n residu initial (sans stabilisation):      "<< (*residu)
                           << "\n maxi_F_t= " << maxi_F_t;
                     };
                  if ((niveau_affichage > 5))
                     {int taille = pt_StabMembBiel->Taille();
                      for (int i=1;i<=taille;i++)
                        cout << "\n F_stab("<<i<<")= "<<pt_StabMembBiel->FF_StabMembBiel(i)<< " ";
                     };
                  #endif
      
                  // 1) ---- on regarde s'il faut limiter la valeur de la stabilisation
                  // et on prend en compte dans la raideur globale et le résidu global
                  // la stabilisation
                  bool limitation = false;
                  double coef=1.; // coef de limitation éventuelle si limitation devient true
           
                  // récup des limitations éventuelles
                  double beta = pt_StabMembBiel->Beta();
                  double F_mini = pt_StabMembBiel->F_mini();
                  double d_maxi = pt_StabMembBiel->D_maxi();
                  // on refait une boucle sur les noeuds
                  int ix=1;
                  for (int ne =1; ne<= nbne; ne++)
                    {bool continuer = true;
                     if (noeud_a_prendre_en_compte != NULL)
                      if (!(*noeud_a_prendre_en_compte)(ne))
                        continuer = false;
                     if (continuer)
                       {Noeud& noe = *(tab_noeud(ne)); // pour simplifier
                        // récup de la force de stabilisation généralisée au noeud
                        // c'est différent de celle au pti car elle inclue la surface de l'élément
                        Coordonnee force_stab(dimf); // init
                        for (int i=1;i<= dimf;i++,ix++)
                          force_stab(i) = (*resD)(ix);
                        double intensite_generalise = force_stab.Norme();
                      
                        // on récupère la force externe au noeud à l'instant t
                        TypeQuelconque_enum_etendu enu_ext(FORCE_GENE_EXT_t);
                        // récupération du conteneur pour lecture uniquement
                        const TypeQuelconque& typquel_ext = noe.Grandeur_quelconque(enu_ext);
                        const  Grandeur_coordonnee& cofo_ext = *((Grandeur_coordonnee*) typquel_ext.Const_Grandeur_pointee());
                        // on récupère la normale au noeud
                        const TypeQuelconque& tiq = noe.Grandeur_quelconque(NN_SURF_t);
                        const Grandeur_coordonnee& gr= *((const Grandeur_coordonnee*) (tiq.Const_Grandeur_pointee()));
                        const Coordonnee& normale = gr.ConteneurCoordonnee_const();

                        // on calcule l'intensité de la force externe selon la normale
                        double intensite_Fext = Dabs(cofo_ext.ConteneurCoordonnee_const() * normale);
                        // si l'intensité de la stabilisation est supérieure à beta * intensite_Fext
                        // on limite
                        
                        double maxi_intensite = beta * intensite_Fext;
                        double max_intens_via_Fext = maxi_intensite; // on sauve pour le cas où on est dep max

                                                
                        // on applique l'algo théorique cf. doc théorique
                        if (intensite_Fext >= F_mini)
                         // on est dans le cas où les forces externes existent
                         {if ((intensite_generalise > maxi_intensite)
                              && (intensite_generalise > ConstMath::petit) // au cas où maxi_intintensite très petites
                             )
                            {limitation = true;
                             if ((niveau_affichage > 3))
                               {cout << "\n limitation stabilisation due aux forces externes, F_stab gene=  "<< intensite_generalise
                                     << " ==> " << maxi_intensite;
                                if ((niveau_affichage>4)
                                    || (pt_StabMembBiel->Affichage_stabilisation() > 4))
                                  cout << "\n   F_mini= "<< F_mini << " intensite_Fext= "<< intensite_Fext;
                               };
                             coef = DabsMiN(coef, maxi_intensite/intensite_generalise);
                            };
                          // sinon pas de pb donc on ne fait rien
                          }
                        else // cas où les forces externes n'existent pas
                         // la limitation va s'exercée via un déplacement maxi
                         {maxi_intensite = Dabs(-alpha*d_maxi +  maxi_F_t);
                          {if ((intensite_generalise > maxi_intensite)
                            && (intensite_generalise > ConstMath::petit) // au cas où maxi_intintensite très petites
                            && (Dabs(alpha) > ConstMath::pasmalpetit) // il faut qu'alpha ne soit pas nul
                           )
                            {limitation = true;
                             if ((niveau_affichage > 3))
                               {cout << "\n limitation stabilisation due deplace maxi acceptable, F_s"<< intensite_generalise
                                     << " ==> " << maxi_intensite
                                     << " (max_intens_via_Fext= "<<max_intens_via_Fext<<")" ;

                                if ((niveau_affichage > 4))
                                     cout << "\n  maxi_F_t= "<< maxi_F_t << " alpha*d_maxi= "
                                          << (alpha*d_maxi)
                                          << " alpha= " << alpha
                                          << " d_maxi= " << d_maxi;
                               };
                             coef = DabsMiN(coef, maxi_intensite/intensite_generalise);
                            };
                          };
                         };
                      
                       }; // fin du test sur les noeuds à considérer

                    };// fin de la boucle sur les noeuds
                         
                  if (limitation)
                   { (*resD) *= coef; // mise à jour de la force de stabilisation
                    // idem au niveau des noeuds
                    int taille = pt_StabMembBiel->Taille();
                    for (int i=1;i<=taille;i++)
                       {pt_StabMembBiel->FF_StabMembBiel(i) *=coef;
                        // cas des énergies
                        double delta_energie_initiale =
                            pt_StabMembBiel->EE_StabMembBiel(i) - pt_StabMembBiel->EE_StabMembBiel_t(i);
                        // on coefficiente le delta
                        double delta_energie = delta_energie_initiale * coef;
                        //ce qui donne au final:
                        pt_StabMembBiel->EE_StabMembBiel(i) =
                             delta_energie + pt_StabMembBiel->EE_StabMembBiel_t(i);
                       };
                   
                    // 2) ----  on met à jour la raideur et résidu global
                    // en tenant compte de la stabilisation
                    (*residu) += (*resD);
                    (*raideur) += (*matD);
                   }; // fin du cas if (limitation)
              }; // fin du cas ou on applique éventuellement une limitation

             #ifdef MISE_AU_POINT
             if ((niveau_affichage > 9))
                {cout << "\n residu de stabilisation: "<< (*resD);
                 cout << "\n raideur de stabilisation: "; matD->Affiche();
                 cout << "\n fin debug ElemMeca::Cal_implicit_StabMembBiel(..";
                };
             #endif
            } // fin du cas où on utilise la normale aux noeuds
          };

        break;

       case LIGNE:
       break;

       default: // on ne fait rien pour l'instant pour les autres types
         break;
     };
   };
 
};

// stabilisation pour un calcul explicit
void ElemMeca::Cal_explicit_StabMembBiel(int iteg,const Met_abstraite::Expli_t_tdt ex
                      , const int& nbint,const double& poids_volume
                      ,Tableau <int> * noeud_a_prendre_en_compte)
{
 // la stabilisation en explicite n'est possible que s'il existe soit:
 // - une force de stabilisation déjà enregistrée
 // - un coefficient alpha non nulle que l'on peut calculer avec les données déjà enregistrées

  // l'idée est de bloquer la direction normale à l'élément pour une plaque et
  // les deux directions transverses pour une biel
  int niveau_affichage = ParaGlob::NiveauImpression();
  if (pt_StabMembBiel->Affichage_stabilisation() > 0)
     niveau_affichage = pt_StabMembBiel->Affichage_stabilisation();

  //---- détermination de l'intensité de stabilisation
  // ici on ne s'occupe pas de l'itération ni de l'incrément, dans tous les cas
  // on utilise les grandeurs déjà stockées
  // ceci quelque soit le type de stabilisation
  
  double alpha = 0.;  // init
  bool stab_utilisable = false; // init
  Enum_StabMembraneBiel enu_stab = pt_StabMembBiel->Type_stabMembrane();
  
  
  if (pt_StabMembBiel != NULL)
   if (pt_StabMembBiel->Activite_en_explicite())
    { //alpha = pt_StabMembBiel->Stab_ref(); // récup de l'intensité de stabilisation
      // ce n'est pas un pointeur, alpha est une variable locale
      //  stab_ref,  par défaut == 1, mais dans le cas d'un passage
      // implicit -> explicite : vaut la dernière valeur implicite calculée
      // cela permet de profiter du passage en implicite, en récupérant l'intensité
      // de raideur réelle
      alpha = pt_StabMembBiel->Stab_ref(); // init
      
      // dans le cas d'une fonction multiplicatrice ,
      // et il faut appeler la fonction qui peut varier
      if (pt_StabMembBiel->Pt_fct_gamma() != NULL)
        // cas où il y a une fonction nD
        {Fonction_nD* fct = pt_StabMembBiel->Pt_fct_gamma(); // récupération de la fonction nD
         // on commence par récupérer les conteneurs des grandeurs à fournir
         List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
         List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
         bool absolue = true; // on se place systématiquement en absolu
         // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
         int cas = -1; // signifie que l'on utilise la métrique qui vient juste d'être calculée
         int num_iteg_a_considerer; // init
         if (iteg > 0) // on est dans la boucle
           num_iteg_a_considerer = iteg;
         else // sinon on est à la sortie de la boucle
           num_iteg_a_considerer = nbint;
         Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi
                (absolue,TEMPS_tdt,li_enu_scal,num_iteg_a_considerer,-cas));
         // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
         Valeurs_Tensorielles(absolue, TEMPS_tdt,li_quelc,num_iteg_a_considerer,-cas);
         // calcul de la valeur et retour dans tab_ret
         Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
         // seule la première valeur est ok
         alpha *= tab_ret(1) ;
         pt_StabMembBiel->Valgamma() = alpha; // on sauvegarde la valeur d'alpha
        }
      else // sinon cela veut dire que l'intensité de stabilisation est fixe
           // en explicite c'est directement la valeur lue
        {alpha *= pt_StabMembBiel->Valgamma() ;}
      stab_utilisable=true;
    };
  
  // si la stabilisation est utilisable on s'en sert
  if (stab_utilisable)
    {// choix en fonction de la géométrie
     Enum_type_geom enu_type_geom = Type_geom_generique(this->Id_geometrie());
     switch (enu_type_geom)
      { case SURFACE:
         if (!Contient_Normale_au_noeud(enu_stab))
          // --- cas où on utilise la normale au pti
           {// la normale vaut le produit vectoriel des 2 premiers vecteurs
            Coordonnee normale = Util::ProdVec_coor( (*ex.giB_tdt).Coordo(1), (*ex.giB_tdt).Coordo(2));
            
            // calcul de la normale normée et pondérée par la stabilisation
            normale.Normer();
            int nbddl = (*residu).Taille();
            int dimf = 3;   // ne fonctionne qu'en dim 3
            if(ParaGlob::AxiSymetrie())
              // cas  axisymétrique, dans ce cas on ne prend en compte que les
              // dimf-1 coordonnées donc on décrémente
              dimf--;
            int nbne = tab_noeud.Taille();
            double ponder_F_t = 1.; // init

            // si iteg == 1 ==> premier calcul correspondant au premier pti
            if (iteg == 1)
             { resD->Inita(0.);
               maxi_F_t = 0.;
               // on initialise les forces et énergie
               int taille = pt_StabMembBiel->Taille();
               for (int i=1;i<=taille;i++)
                  {pt_StabMembBiel->FF_StabMembBiel(i) = 0.;
                   pt_StabMembBiel->EE_StabMembBiel(i) = pt_StabMembBiel->EE_StabMembBiel_t(i);
                  }
             };
            // on récupère l'élément géométrique
            ElemGeomC0& elemGeom = this->ElementGeometrique();
            // récup des fonctions d'interpolation
            const Tableau <Vecteur>& taphi = elemGeom.TaPhi();

            // on applique une  force de stabilisation
            // uniquement suivant la direction de la normale
            // ici iteg peut soit == -1 : on est sortie de la boucle sur les pti
            // soit > 0 : on et dans la boucle des pti
            
            int num_pti=iteg; // init
            if (iteg == -1) num_pti = nbint;
            // la force est proportionelle au déplacement suivant la normale
            if (iteg > 0)
             { int ix=1;
               for (int ne =1; ne<= nbne; ne++)
                {bool continuer = true;
                 if (noeud_a_prendre_en_compte != NULL)
                  if (!(*noeud_a_prendre_en_compte)(ne))
                    continuer = false;
                 if (continuer)
                   {Noeud& noe = *(tab_noeud(ne)); // pour simplifier
                
                    // pour chaque noeud on va rectifier la normale par rapport à celle calculée au pti
                    // on va bloquer le déplacement dans la direction de la normale au noeud à t
                    // celle-ci peut-être différente de celle à t=0, lorsque l'on a une succession d'algo sur le même incrément
                    //  on commence par récupérer les coordonnées pour l'itération 0
                    TypeQuelconque_enum_etendu enu(XI_ITER_0);
                    // récupération du conteneur pour lecture uniquement
                    const TypeQuelconque& typquel = noe.Grandeur_quelconque(enu);
                    const  Grandeur_coordonnee& cofo = *((Grandeur_coordonnee*) typquel.Const_Grandeur_pointee());
                    Coordonnee delta_X = noe.Coord2() - cofo.ConteneurCoordonnee_const();
                   
                    double normal_fois_delta_X = normale * delta_X; // le déplacement suivant la normale
                    // dans le cas où on veut pondérer F_t, on calcule la fonction
                    if (pt_StabMembBiel->Pt_fct_ponder_Ft() != NULL)
                      {Fonction_nD* fct = pt_StabMembBiel->Pt_fct_ponder_Ft(); // récupération de la fonction nD
                       // on commence par récupérer les conteneurs des grandeurs à fournir
                       List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
                       List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
                       bool absolue = true; // on se place systématiquement en absolu
                       // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
                       int cas = -1; // signifie que l'on utilise la métrique qui vient juste d'être calculée
                       Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi
                              (absolue,TEMPS_tdt,li_enu_scal,num_pti,-cas));
                       // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
                       Valeurs_Tensorielles(absolue, TEMPS_tdt,li_quelc,num_pti,-cas);
                       // calcul de la valeur et retour dans tab_ret
                       Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
                       // seule la première valeur est ok
                       ponder_F_t = tab_ret(1) ;
                      }
                    // un - car la force doit s'opposer au déplacement
                    double intensite = -alpha * normal_fois_delta_X
                                       + ponder_F_t * pt_StabMembBiel->FF_StabMembBiel_t(num_pti);
                    // on enregistre le maxi de la force à t
                    maxi_F_t = DabsMaX(maxi_F_t, pt_StabMembBiel->FF_StabMembBiel_t(num_pti));

                    // d'où une variation réelle d'intensité
                    double delta_intensite = intensite - ponder_F_t * pt_StabMembBiel->FF_StabMembBiel_t(num_pti);
                    pt_StabMembBiel->EE_StabMembBiel(num_pti) += 0.5 * delta_intensite * normal_fois_delta_X;
                    #ifdef MISE_AU_POINT
                    if ((niveau_affichage > 5))
                      {cout << "\n ElemMeca::Cal_explicit_StabMembBiel(..";
                       cout << "\n alpha= "<<alpha << " intensite= "<< intensite
                            << " delta_intensite= " << delta_intensite
                            << " delta_X= "<< delta_X
                            << "\n normale= "<<normale << " normal_fois_delta_X= " << normal_fois_delta_X << flush;
                       };
                    #endif

                    // calcul de la raideur: on traite les cas :
                    // iteg >0 et iteg == 0
                    // le cas iteg == -1 n'est pas à prendre en compte car il correspond
                    // au cas où on vient de balayer tous les pti
                    if (iteg > 0) // cas où on est dans une boucle d'intégration
                     {// un premier coef pour optimiser
                      double phi_jacob_poids = taphi(iteg)(ne)  * (*ex.jacobien_tdt) * poids_volume ;
                     
                      pt_StabMembBiel->FF_StabMembBiel(iteg) += intensite * taphi(iteg)(ne); // * phi_jacob_poids;
      //                    taphi(iteg)(ne) * intensite * (*ex.jacobien_tdt) * poids_volume ;
                      pt_StabMembBiel->EE_StabMembBiel(iteg) += 0.5 * intensite * normal_fois_delta_X * phi_jacob_poids;
      //                  0.5 * delta_intensite * normal_fois_delta_X * taphi(iteg)(ne) * (*ex.jacobien_tdt) * poids_volume ;
                  
                      // un second coef pour optimiser
 //                     double phi_poids = taphi(iteg)(ne)  * poids_volume ;
                   
 //             pour mémoire: intensite = -alpha * normal_fois_delta_X
 //                                     + pt_StabMembBiel->FF_StabMembBiel_t(num_pti);
                      for (int i=1;i<= dimf;i++,ix++)
                       {  // =    (- normale(i) * alpha * normal_fois_delta_X + F_t)
                          //    * (taphi(iteg)(ne) * poids * jacobien)  ;
                          (*resD)(ix) += normale(i) * intensite * phi_jacob_poids;
 //                                        taphi(iteg)(ne)* normale(i) * intensite
 //                                        * (*ex.jacobien_tdt) * poids_volume ; // ok
                       };
                     }
                     
                  }; // fin du test sur les noeuds à considérer
                  
               }; // fin de la boucle sur les noeuds
             };
             
            // on est en dehors de la boucle, on effectue 2 choses
            // 1) application d'une limitation éventuelle des efforts
            // 2) prise en compte dans la raideur et résidu de l'élément, de la stabilisation
            // ici seul le cas iteg == -1 est pris en compte contrairement à l'implicite
            if (iteg == -1) // signifie que l'on est en dehors de la boucle
              {
               if (pt_StabMembBiel->Gestion_maxi_mini())
                 {
                  #ifdef MISE_AU_POINT
                  if ((niveau_affichage > 5))
                     {cout << "\n residu de stabilisation avant limitation: "<< (*resD)
                           << "\n residu initial (sans stabilisation):      "<< (*residu)
                           << "\n maxi_F_t= " << maxi_F_t;
                     };
                  if ((niveau_affichage > 5))
                     {int nb_pti = pt_StabMembBiel->Taille();
                      for (int i=1;i<=nb_pti;i++)
                        cout << "\n F_stab("<<i<<")= "<<pt_StabMembBiel->FF_StabMembBiel(i)<< " ";
                     };
                  #endif

                  // 1) ---- on regarde s'il faut limiter la valeur de la stabilisation
                  // et on prend en compte dans la raideur globale et le résidu global
                  // la stabilisation
                  bool limitation = false;
                  double coef=1.; // coef de limitation éventuelle si limitation devient true
                  
                  // récup des limitations éventuelles
                  double beta = pt_StabMembBiel->Beta();
                  double F_mini = pt_StabMembBiel->F_mini();
                  double d_maxi = pt_StabMembBiel->D_maxi();

                  // on refait une boucle sur les pti
                  int ix=1;
                  for (int ne =1; ne<= nbne; ne++)
                   {bool continuer = true;
                    if (noeud_a_prendre_en_compte != NULL)
                     if (!(*noeud_a_prendre_en_compte)(ne))
                       continuer = false;
                    if (continuer)
                      {Noeud& noe = *(tab_noeud(ne)); // pour simplifier
                       // récup de la force de stabilisation généralisée au noeud
                       // c'est différent de celle au pti car elle inclue la surface de l'élément
                       Coordonnee force_stab(dimf); // init
                       for (int i=1;i<= dimf;i++,ix++)
                         force_stab(i) = (*resD)(ix);
                       double intensite_generalise = force_stab.Norme();
                     
                       // on récupère la force externe au noeud à l'instant t
                       TypeQuelconque_enum_etendu enu_ext(FORCE_GENE_EXT_t);
                       // récupération du conteneur pour lecture uniquement
                       const TypeQuelconque& typquel_ext = noe.Grandeur_quelconque(enu_ext);
                       const  Grandeur_coordonnee& cofo_ext = *((Grandeur_coordonnee*) typquel_ext.Const_Grandeur_pointee());

                       // on calcule l'intensité de la force externe selon la normale
                       double intensite_Fext = Dabs(cofo_ext.ConteneurCoordonnee_const() * normale);
                       // si l'intensité de la stabilisation est supérieure à beta * intensite_Fext
                       // on limite
                       
                       double maxi_intensite = beta * intensite_Fext;
                       double max_intens_via_Fext = maxi_intensite; // on sauve pour le cas où on est dep max

                                               
                       // on applique l'algo théorique cf. doc théorique
                       if (intensite_Fext >= F_mini)
                        // on est dans le cas où les forces externes existent
                        {if ((intensite_generalise > maxi_intensite)
                             && (intensite_generalise > ConstMath::petit) // au cas où maxi_intensite et intensite très petites
                            )
                           {limitation = true;
                            if ((niveau_affichage > 3))
                              {cout << "\n limitation stabilisation due aux forces externes, F_stab gene=  "<< intensite_generalise
                                    << " ==> " << maxi_intensite;
                               if ((niveau_affichage > 5))
                                 cout << "\n   F_mini= "<< F_mini << " intensite_Fext= "<< intensite_Fext;
                              };
                            coef = DabsMiN(coef, maxi_intensite/intensite_generalise);
                           };
                         // sinon pas de pb donc on ne fait rien
                         }
                       else // cas où les forces externes n'existent pas
                        // la limitation va s'exercée via un déplacement maxi
                        {maxi_intensite = Dabs(-alpha*d_maxi +  maxi_F_t);
                         {if ((intensite_generalise > maxi_intensite)
                           && (intensite_generalise > ConstMath::petit) // au cas où maxi_intensite et intensite très petites
                           && (Dabs(alpha) > ConstMath::pasmalpetit) // il faut qu'alpha ne soit pas nul
                          )
                           {limitation = true;
                            if ((niveau_affichage > 3))
                              {cout << "\n limitation stabilisation due deplace maxi acceptable, F_stab gene= "<< intensite_generalise
                                    << " ==> " << maxi_intensite
                                    << " (max_intens_via_Fext= "<<max_intens_via_Fext<<")" ;

                               if ((niveau_affichage > 4))
                                    cout << "\n  maxi_F_t= "<< maxi_F_t << " alpha*d_maxi= "
                                         << (alpha*d_maxi)
                                         << " alpha= " << alpha
                                         << " d_maxi= " << d_maxi;
                              };
                            coef = DabsMiN(coef, maxi_intensite/intensite_generalise);
                           };
                         };
                        };
                     
                      }; // fin du test sur les noeuds à considérer

                   };// fin de la boucle sur les noeuds
              
                  if (limitation)
                   { (*resD) *= coef; // mise à jour de la force de stabilisation
                    // idem au niveau des pti
                    int taille = pt_StabMembBiel->Taille();
                    for (int i=1;i<=taille;i++)
                       {pt_StabMembBiel->FF_StabMembBiel(i) *=coef;
                        // cas des énergies
                        double delta_energie_initiale =
                            pt_StabMembBiel->EE_StabMembBiel(i) - pt_StabMembBiel->EE_StabMembBiel_t(i);
                        // on coefficiente le delta
                        double delta_energie = delta_energie_initiale * coef;
                        //ce qui donne au final:
                        pt_StabMembBiel->EE_StabMembBiel(i) =
                             delta_energie + pt_StabMembBiel->EE_StabMembBiel_t(i);
                       };
                   };
                 }; // fin mise en place d'une limitation éventuelle

               // 2) ----  on met à jour le résidu global
               // en tenant compte de la stabilisation
               (*residu) += (*resD);
             
               #ifdef MISE_AU_POINT
               if ((niveau_affichage > 9))
                  {cout << "\n residu de stabilisation: "<< (*resD);
                   cout << "\n fin ElemMeca::Cal_explicit_StabMembBiel(..";
                  };
               #endif
              }; // fin du cas iteg == -1
           } // fin du cas où on utilise la normale aux pti

         else //--- cas où on utilise la normale au noeud
           {if (iteg == -1)
             // on ne calcul que si iteg == -1, c-a-d qu'on est sortie
             // de la boucle sur les pti
             // sinon cas où on utilise la normale aux noeuds
             {int nbddl = (*residu).Taille();
              int dimf = 3;   // ne fonctionne qu'en dim 3
              if(ParaGlob::AxiSymetrie())
                // cas  axisymétrique, dans ce cas on ne prend en compte que les
                // dimf-1 coordonnées donc on décrémente
                dimf--;
              int nbne = tab_noeud.Taille();
              resD->Inita(0.);
              maxi_F_t = 0.;

              #ifdef MISE_AU_POINT
              // on vérifie le bon dimensionnement
              int taille = pt_StabMembBiel->Taille();
              if (taille != nbne)
                {cout << "\n erreur** ElemMeca::Cal_explicit_StabMembBiel(.."
                      << " le stockage est mal dimensionne: taille = " << taille
                      << " alors qu'il devrait etre : nbe = " << nbne << flush ;
                 Sortie(1);
                };
              #endif

              // on applique une  force de stabilisation aux noeuds
              // uniquement suivant la direction de la normale
              // la force est proportionelle au déplacement suivant la normale au noeud
              int ix=1;
              for (int ne =1; ne<= nbne; ne++)
               {Noeud& noe = *(tab_noeud(ne)); // pour simplifier
               
                // on initialise les forces et énergie
                pt_StabMembBiel->FF_StabMembBiel(ne) = 0.;
                pt_StabMembBiel->EE_StabMembBiel(ne) = pt_StabMembBiel->EE_StabMembBiel_t(ne);

                // pour chaque noeud  on va bloquer le déplacement dans la direction de la normale au noeud à t
                // on récupère la normale au noeud
                const TypeQuelconque& tiq = noe.Grandeur_quelconque(NN_SURF_t);
                const Grandeur_coordonnee& gr= *((const Grandeur_coordonnee*) (tiq.Const_Grandeur_pointee()));
                const Coordonnee& normale = gr.ConteneurCoordonnee_const();


                //dans un calcul implicit on utilise la position à l'itération 0
                // celle-ci peut-être différente de celle à t=0, lorsque l'on a une succession d'algo sur le même incrément
                //  on commence par récupérer les coordonnées pour l'itération 0
                TypeQuelconque_enum_etendu enu(XI_ITER_0);
                // récupération du conteneur pour lecture uniquement
                const TypeQuelconque& typquel = noe.Grandeur_quelconque(enu);
                const  Grandeur_coordonnee& cofo = *((Grandeur_coordonnee*) typquel.Const_Grandeur_pointee());
                Coordonnee delta_X = noe.Coord2() - cofo.ConteneurCoordonnee_const();
                double normal_fois_delta_X = normale * delta_X;
                double ponder_F_t = 1.; // init
                // dans le cas où on veut pondérer F_t, on calcule la fonction
                if (pt_StabMembBiel->Pt_fct_ponder_Ft() != NULL)
                  {Fonction_nD* fct = pt_StabMembBiel->Pt_fct_ponder_Ft(); // récupération de la fonction nD
                   // on commence par récupérer les conteneurs des grandeurs à fournir
                   List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
                   List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
                   bool absolue = true; // on se place systématiquement en absolu
                   // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
                   int cas = -1; // signifie que l'on utilise la métrique qui vient juste d'être calculée
                   // au dernier pti !
                   Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi
                          (absolue,TEMPS_tdt,li_enu_scal,nbint,-cas));
                   // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
                   Valeurs_Tensorielles(absolue, TEMPS_tdt,li_quelc,nbint,-cas);
                   // calcul de la valeur et retour dans tab_ret
                   Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
                   // seule la première valeur est ok
                   ponder_F_t = tab_ret(1) ;
                  }

                // un - car la force doit s'opposer au déplacement
                double intensite = -alpha * normal_fois_delta_X
                                   + ponder_F_t * pt_StabMembBiel->FF_StabMembBiel_t(ne);
                // on enregistre le maxi de la force à t
                maxi_F_t = DabsMaX(maxi_F_t, pt_StabMembBiel->FF_StabMembBiel_t(ne));

                // d'où une variation réelle d'intensité
                double delta_intensite = intensite - ponder_F_t * pt_StabMembBiel->FF_StabMembBiel_t(ne);
                pt_StabMembBiel->EE_StabMembBiel(ne) += 0.5 * delta_intensite * normal_fois_delta_X;
                #ifdef MISE_AU_POINT
                if ((niveau_affichage > 5))
                  {cout << "\n ElemMeca::Cal_explicit_StabMembBiel(..";
                   cout << "\n alpha= "<<alpha << " intensite= "<< intensite
                        << " delta_intensite= " << delta_intensite
                        << " delta_X= "<< delta_X
                        << "\n normale= "<<normale << " normal_fois_delta_X= " << normal_fois_delta_X << flush;
                   };
                #endif

                //             pour mémoire: intensite = -alpha * normal_fois_delta_X
                //                                     + pt_StabMembBiel->FF_StabMembBiel_t(num_pti);
                for (int i=1;i<= dimf;i++,ix++)
                   { // - normale(i) * stabili * normal_fois_delta_X;
                     (*resD)(ix) +=  normale(i) * intensite ;
                   };

               }; // fin de la boucle sur les noeuds

               // on est en dehors de la boucle, on effectue 2 choses
               // 1) application d'une limitation éventuelle des efforts
               // 2) prise en compte dans la raideur et résidu de l'élément, de la stabilisation
               
               if (pt_StabMembBiel->Gestion_maxi_mini())
                  {
                    #ifdef MISE_AU_POINT
                    if ((niveau_affichage > 4))
                       {cout << "\n residu de stabilisation avant limitation: "<< (*resD)
                             << "\n residu initial (sans stabilisation):      "<< (*residu)
                             << "\n maxi_F_t= " << maxi_F_t;
                       };
                    if ((niveau_affichage > 4))
                       {int taille = pt_StabMembBiel->Taille();
                        for (int i=1;i<=taille;i++)
                          cout << "\n F_stab("<<i<<")= "<<pt_StabMembBiel->FF_StabMembBiel(i)<< " ";
                       };
                    #endif
        
                    // 1) ---- on regarde s'il faut limiter la valeur de la stabilisation
                    // et on prend en compte dans la raideur globale et le résidu global
                    // la stabilisation
                    bool limitation = false;
                    double coef=1.; // coef de limitation éventuelle si limitation devient true
             
                    // récup des limitations éventuelles
                    double beta = pt_StabMembBiel->Beta();
                    double F_mini = pt_StabMembBiel->F_mini();
                    double d_maxi = pt_StabMembBiel->D_maxi();
                    // on refait une boucle sur les noeuds
                    int ix=1;
                    for (int ne =1; ne<= nbne; ne++)
                      {bool continuer = true;
                       if (noeud_a_prendre_en_compte != NULL)
                        if (!(*noeud_a_prendre_en_compte)(ne))
                          continuer = false;
                       if (continuer)
                         {Noeud& noe = *(tab_noeud(ne)); // pour simplifier
                          // récup de la force de stabilisation généralisée au noeud
                          // c'est différent de celle au pti car elle inclue la surface de l'élément
                          Coordonnee force_stab(dimf); // init
                          for (int i=1;i<= dimf;i++,ix++)
                            force_stab(i) = (*resD)(ix);
                          double intensite_generalise = force_stab.Norme();
                        
                          // on récupère la force externe au noeud à l'instant t
                          TypeQuelconque_enum_etendu enu_ext(FORCE_GENE_EXT_t);
                          // récupération du conteneur pour lecture uniquement
                          const TypeQuelconque& typquel_ext = noe.Grandeur_quelconque(enu_ext);
                          const  Grandeur_coordonnee& cofo_ext = *((Grandeur_coordonnee*) typquel_ext.Const_Grandeur_pointee());
                          // on récupère la normale au noeud
                          const TypeQuelconque& tiq = noe.Grandeur_quelconque(NN_SURF_t);
                          const Grandeur_coordonnee& gr= *((const Grandeur_coordonnee*) (tiq.Const_Grandeur_pointee()));
                          const Coordonnee& normale = gr.ConteneurCoordonnee_const();

                          // on calcule l'intensité de la force externe selon la normale
                          double intensite_Fext = Dabs(cofo_ext.ConteneurCoordonnee_const() * normale);
                          // si l'intensité de la stabilisation est supérieure à beta * intensite_Fext
                          // on limite
                          
                          double maxi_intensite = beta * intensite_Fext;
                          double max_intens_via_Fext = maxi_intensite; // on sauve pour le cas où on est en dep max
                          
                                                  
                          // on applique l'algo théorique cf. doc théorique
                          if (intensite_Fext >= F_mini)
                           // on est dans le cas où les forces externes existent
                           {if ((intensite_generalise > maxi_intensite)
                                && (intensite_generalise > ConstMath::petit) // au cas où maxi_intintensite très petites
                               )
                              {limitation = true;
                               if ((niveau_affichage > 3))
                                 {cout << "\n limitation stabilisation due aux forces externes, F_stab gene=  "<< intensite_generalise
                                       << " ==> " << maxi_intensite;
                                  if ((niveau_affichage > 4))
                                    cout << "\n   F_mini= "<< F_mini << " intensite_Fext= "<< intensite_Fext;
                                 };
                               coef = DabsMiN(coef, maxi_intensite/intensite_generalise);
                              };
                            // sinon pas de pb donc on ne fait rien
                            }
                          else // cas où les forces externes n'existent pas
                           // la limitation va s'exercée via un déplacement maxi
                           {maxi_intensite = Dabs(-alpha*d_maxi +  maxi_F_t);
                            {if ((intensite_generalise > maxi_intensite)
                              && (intensite_generalise > ConstMath::petit) // au cas où maxi_intintensite très petites
                              && (Dabs(alpha) > ConstMath::pasmalpetit) // il faut qu'alpha ne soit pas nul
                             )
                              {limitation = true;
                               if ((niveau_affichage > 3))
                                 {cout << "\n limitation stabilisation due deplace maxi acceptable, F_s"<< intensite_generalise
                                       << " ==> " << maxi_intensite << " (max_intens_via_Fext= "<<max_intens_via_Fext<<")" ;
                                  if ((niveau_affichage > 4))
                                       cout << "\n  maxi_F_t= "<< maxi_F_t << " alpha*d_maxi= "
                                            << (alpha*d_maxi)
                                            << " alpha= " << alpha
                                            << " d_maxi= " << d_maxi;
                                 };
                               coef = DabsMiN(coef, maxi_intensite/intensite_generalise);
                              };
                            };
                           };
                        
                         }; // fin du test sur les noeuds à considérer

                      };// fin de la boucle sur les noeuds
                           
                    if (limitation)
                     { (*resD) *= coef; // mise à jour de la force de stabilisation
                      // idem au niveau des noeuds
                      int taille = pt_StabMembBiel->Taille();
                      for (int i=1;i<=taille;i++)
                         {pt_StabMembBiel->FF_StabMembBiel(i) *=coef;
                          // cas des énergies
                          double delta_energie_initiale =
                              pt_StabMembBiel->EE_StabMembBiel(i) - pt_StabMembBiel->EE_StabMembBiel_t(i);
                          // on coefficiente le delta
                          double delta_energie = delta_energie_initiale * coef;
                          //ce qui donne au final:
                          pt_StabMembBiel->EE_StabMembBiel(i) =
                               delta_energie + pt_StabMembBiel->EE_StabMembBiel_t(i);
                         };
                  
                     }; // fin du cas if (limitation)
                  }; // fin du cas ou on applique éventuellement une limitation
               
               // 2) ----  on met à jour la raideur et résidu global
               // en tenant compte de la stabilisation
               (*residu) += (*resD);

               #ifdef MISE_AU_POINT
               if ((niveau_affichage > 9))
                  {cout << "\n residu de stabilisation: "<< (*resD);
                   cout << "\n fin debug ElemMeca::Cal_explicit_StabMembBiel(..";
                  };
               #endif
             } // fin du cas où on utilise la normale aux noeuds
           };

         break;

        case LIGNE:
        break;

        default: // on ne fait rien pour l'instant pour les autres types
          break;
      };
    };
  
 };


// fonction a renseigner par les classes dérivées, concernant les répercutions
// éventuelles due à la suppression de tous les  frontières
// nums_i : donnent les listes de frontières supprimées
void ElemMeca::Prise_en_compte_des_consequences_suppression_tous_frontieres()
{// il faut supprimer toutes les déformations liés aux frontières
 int tail_S = defSurf.Taille();
 for (int i=1;i<=tail_S;i++)
   { delete defSurf(i);
     defSurf(i)=NULL;
   };
 int tail_A = defArete.Taille();
 for (int i=1;i<=tail_A;i++)
   { delete defArete(i);
     defArete(i)=NULL;
   };
};

// idem pour une frontière (avant qu'elle soit supprimée)        
void ElemMeca::Prise_en_compte_des_consequences_suppression_une_frontiere(ElFrontiere* elemFront)
{  int taille = tabb.Taille();
// on choisit en fonction du type de frontière
  if (( elemFront->Type_geom_front() == LIGNE)&& (defArete.Taille()!=0))
   {int taille_ligne = taille - posi_tab_front_lin; // a priori = cas sans les points
    if (posi_tab_front_point != 0) // cas où il y a des points
       taille_ligne = posi_tab_front_point-posi_tab_front_lin;
    for (int i=1; i<=taille_ligne; i++)
     {if ((tabb(i+posi_tab_front_lin) == elemFront)&& (defArete(i) != NULL))
           {delete defArete(i);defArete(i)=NULL;break;}
     }
   }
  else if (( elemFront->Type_geom_front() == SURFACE) && (defSurf.Taille()!=0))
   {if (posi_tab_front_lin != 0) // si == 0 cela signifie qu'il n'y a pas de surface à supprimer !!
     { for (int i=1; i<=posi_tab_front_lin; i++) // posi_tab_front_lin == le nombre de surfaces, qui sont obligatoirement en début de tableau 
        if ((tabb(i) == elemFront)&& (defSurf(i) != NULL))
           {delete defSurf(i);defSurf(i)=NULL;break;}
     };
   };

};

// Calcul des frontieres de l'element
//  creation des elements frontieres et retour du tableau de ces elements
// la création n'a lieu qu'au premier appel
// ou lorsque l'on force le paramètre force a true
// dans ce dernier cas seul les frontière effacées sont recréée
// cas :
//  = 0 -> on veut toutes les frontières
//  = 1 -> on veut uniquement les surfaces
//  = 2 -> on veut uniquement les lignes
//  = 3 -> on veut uniquement les points
//  = 4 -> on veut les surfaces + les lignes
//  = 5 -> on veut les surfaces + les points
//  = 6 -> on veut les lignes + les points
Tableau <ElFrontiere*> const & ElemMeca::Frontiere_elemeca(int cas, bool force)
{// le calcul et la création ne sont effectués qu'au premier appel
 // ou lorsque l'on veut forcer une recréation
 int taille = tabb.Taille(); // la taille initiales des frontières
 if (force) // dans ce cas on commence par tout effacer
   { // on efface les surfaces (s'il y en a)
     for (int i=1;i<=posi_tab_front_lin;i++) 
      {if (tabb(i) != NULL)
         {delete tabb(i); //on commence par supprimer
          tabb(i)=NULL;
          // on supprime également éventuellement la déformation associée 
          if (defSurf.Taille() != 0)
            if (defSurf(i) != NULL)
              {delete defSurf(i);defSurf(i)=NULL;};
          ind_front_surf = 0; // on indique qu'il ne reste plus de frontière surface
         };
      };
     // on efface les lignes (s'il y en a)
     for (int i=1;i<=posi_tab_front_point - posi_tab_front_lin;i++) 
      {if (tabb(i+posi_tab_front_lin) != NULL)
         {delete tabb(i+posi_tab_front_lin); //on commence par supprimer
          tabb(i+posi_tab_front_lin)=NULL;
          // on supprime également éventuellement la déformation associée 
          if (defArete.Taille() != 0)
            if (defArete(i) != NULL)
              {delete defArete(i);defArete(i)=NULL;};
          ind_front_lin = 0; // on indique qu'il ne reste plus de frontière ligne
         };
      };
     // on efface les points (s'il y en a)
     for (int i=1;i<=taille - posi_tab_front_point;i++) 
      {if (tabb(i+posi_tab_front_point) != NULL)
         {delete tabb(i+posi_tab_front_point); //on commence par supprimer
          tabb(i+posi_tab_front_point)=NULL;
          ind_front_point = 0; // on indique qu'il ne reste plus de frontière ligne
         };
      };
   };

 // -- maintenant on s'occupe de la construction conditionnelle
 bool built_surf = false;bool built_ligne = false; bool built_point = false;
 switch (cas)
 {case 0: built_surf = built_ligne = built_point = true; break;
  case 1: built_surf = true; break;
  case 2: built_ligne = true; break;
  case 3: built_point = true; break;
  case 4: built_surf = built_ligne = true; break;
  case 5: built_surf = built_point = true; break;
  case 6: built_ligne = built_point = true; break;
 };

 if ( ((ind_front_surf == 0)&& (ind_front_lin == 0) && (ind_front_point == 0)) || force )
   {
     // récup de l'élément géométrique
     ElemGeomC0& el = ElementGeometrique();
     int tail_ar = el.NbSe(); // nombre potentiel d'arêtes
     int tail_fa = el.NbFe(); // nombre potentiel de faces
     int tail_po = el.Nbne(); // nombre potentiel de points
     // récup du tableau de ddl actuel
     const DdlElement & tdd = TableauDdl();

     // --- on va construire en fonction des indicateurs des tableaux intermédiaires
     int new_posi_tab_front_point = 0; //init par défaut
     int new_posi_tab_front_lin = 0; //init par défaut
     int new_ind_front_point = 0;
     int new_ind_front_lin = 0;
     int new_ind_front_surf = 0;
     // -- pour les surfaces
     Tableau <ElFrontiere*> tabb_surf;
     if ((built_surf)&& ((ind_front_surf == 0)||force))
      {tabb_surf.Change_taille(tail_fa,NULL);// init par défaut
       for (int num=1;num<=tail_fa;num++)
          { int nbnoe = el.Nonf()(num).Taille(); // nb noeud de la surface
            Tableau <Noeud *> tab(nbnoe); // les noeuds de la frontiere 
            DdlElement ddelem(nbnoe);  // les ddlelements des noeuds frontieres
            for (int i=1;i<= nbnoe;i++)
              { tab(i) = tab_noeud(el.Nonf()(num)(i));
                ddelem.Change_un_ddlNoeudElement(i,tdd(el.Nonf()(num)(i)));
              };
            tabb_surf(num) = new_frontiere_surf(num,tab,ddelem);
          };
       // nouveau indicateur d'existence
       new_ind_front_surf = 1; 
       // on positionne les nouvelles positions
       new_posi_tab_front_point += tail_fa;
       new_posi_tab_front_lin += tail_fa;
      };
     // -- pour les lignes
     Tableau <ElFrontiere*> tabb_ligne;
     if ((built_ligne)&& ((ind_front_lin == 0)||force))
      {tabb_ligne.Change_taille(tail_ar,NULL);// init par défaut
       for (int num=1;num<=tail_ar;num++)
          { int nbnoe = el.NonS()(num).Taille(); // nb noeud de l'arête
            Tableau <Noeud *> tab(nbnoe); // les noeuds de l'arête frontiere 
            DdlElement ddelem(nbnoe);  // les ddlelements des noeuds frontieres
            for (int i=1;i<= nbnoe;i++)
              { tab(i) = tab_noeud(el.NonS()(num)(i));
                ddelem.Change_un_ddlNoeudElement(i,tdd(el.NonS()(num)(i)));
              };
            tabb_ligne(num) = new_frontiere_lin(num,tab,ddelem);
          };
       // nouveau indicateur d'existence
       new_ind_front_lin = 1;
       // on positionne les nouvelles positions
       new_posi_tab_front_point += tail_ar;
      };
     // -- pour les points
     Tableau <ElFrontiere*> tabb_point;
     if ((built_point) && ((ind_front_point == 0)||force))
      {tabb_point.Change_taille(tail_po,NULL);// init par défaut
       // maintenant création des frontière point éventuellement
       Tableau <Noeud *> tab(1); // les noeuds de la frontiere (tab de travail)
       DdlElement ddelem(1);  // les ddlelements des points frontieres (tab de travail)
       for (int i=1;i<=tail_po;i++)
        if (tabb_point(i) == NULL) 
          { tab(1) = tab_noeud(i);
            ddelem.Change_un_ddlNoeudElement(1,tdd(i));
            tabb_point(i) = new FrontPointF(tab,ddelem);
          };
       // nouveau indicateur d'existence
       new_ind_front_point = 1;
      };
      
     // --- mise à jour du tableau globale et des indicateurs ad hoc
     int taille_finale = tabb_surf.Taille()+tabb_ligne.Taille()+tabb_point.Taille();
     tabb.Change_taille(taille_finale);
     //  cas des points
     if (new_ind_front_point) // là is s'agit de nouveaux éléments
       {for (int i=tail_po;i>0;i--) // transfert pour les noeuds
         { tabb(i+new_posi_tab_front_point) = tabb_point(i);}
       }
     else if (ind_front_point) // là il s'agit d'anciens éléments
       {for (int i=tail_po;i>0;i--) // transfert pour les noeuds en descendant
            { tabb(i+new_posi_tab_front_point) = tabb(i+posi_tab_front_point);}
       };
     //  cas des lignes
     if (new_ind_front_lin) // là il s'agit de nouveaux éléments
       {for (int i=1;i<=tail_ar;i++) // transfert
         { tabb(i+new_posi_tab_front_lin) = tabb_ligne(i);}
       }
     else if (ind_front_lin) // là il s'agit d'anciens éléments
       {for (int i=tail_ar;i>0;i--) // transfert  en descendant
            { tabb(i+new_posi_tab_front_lin) = tabb(i+posi_tab_front_lin);}
       };
     //  cas des surfaces
     if (new_ind_front_surf) // là is s'agit de nouveaux éléments
       {for (int i=1;i<=tail_fa;i++) // transfert
         { tabb(i) = tabb_surf(i);}
       };
     // dans le cas où il y avait des anciens éléments, il n'y a rien n'a faire
     // car le redimentionnement de tabb ne change pas les premiers éléments
     
     // mis à jour des indicateurs
     ind_front_surf = new_ind_front_surf;
     posi_tab_front_lin = new_posi_tab_front_lin;
     ind_front_lin = new_ind_front_lin;
     posi_tab_front_point = new_posi_tab_front_point;
     ind_front_point = new_ind_front_point;
   };
 // retour du tableau
 return (Tableau <ElFrontiere*>&)tabb;          
};

// ramène la frontière point
// éventuellement création des frontieres points de l'element et stockage dans l'element 
// si c'est la première fois  sinon il y a seulement retour de l'elements
// a moins que le paramètre force est mis a true
// dans ce dernier cas la frontière effacéee est recréée
// num indique le numéro du point à créer (numérotation EF)
ElFrontiere* const  ElemMeca::Frontiere_points(int num,bool force)
{ // -- tout d'abord on évacue le cas où il n'y a pas de frontière point à calculer
  // récup de l'élément géométrique
  ElemGeomC0& el = ElementGeometrique();
  int tail_po = el.Nbne(); // nombre potentiel de points
  if (num > tail_po)
      return NULL;

  // le calcul et la création ne sont effectués qu'au premier appel
  // ou lorsque l'on veut forcer une recréation
  //   on regarde si les frontières points existent sinon on les crée
  if (ind_front_point == 1)
    {return (ElFrontiere*)tabb(posi_tab_front_point+num);}
  else if ( ind_front_point == 2)
   // cas où certaines frontières existent
   {if (tabb(posi_tab_front_point+num) != NULL)
     return (ElFrontiere*)tabb(posi_tab_front_point+num);
   };
  
  // arrivée ici cela veut dire que la frontière point n'existe pas
  // on l'a reconstruit éventuellement
  
  
  // le calcul et la création ne sont effectués qu'au premier appel
  // ou lorsque l'on veut forcer une recréation
  if ((ind_front_point == 0) || force )
   {// récup du tableau de ddl
    const DdlElement & tdd = TableauDdl();
    int taille = tabb.Taille(); // la taille initiales des frontières
    int tail_fa = el.NbFe(); // nombre potentiel de faces
    int tail_ar = el.NbSe(); // nombre potentiel d'arêtes
    // dimensionnement du tableau de frontières ligne si nécessaire
    if (ind_front_point == 0)
        {if ((ind_front_lin > 0)  && (ind_front_surf == 0))
         // cas où les frontières lignes existent seules, on ajoute les points
         { int taille_finale = tail_ar + tail_po;
           tabb.Change_taille(taille_finale);
           for (int i=1;i<= tail_ar;i++) // transfert pour les lignes
              tabb(i) = tabb(i+tail_ar);
           posi_tab_front_point=tail_ar;
           posi_tab_front_lin = 0; // car on n'a pas de surface
         }      
         else if ((ind_front_lin > 0)  && (ind_front_surf > 0))
         // cas où les frontières lignes existent et surfaces et pas de points, donc on les rajoutes
         { int taille_finale = tail_fa + tail_po+tail_ar;
           tabb.Change_taille(taille_finale);// les grandeurs pour les surfaces et les lignes sont
                        // conservées, donc pas de transferts à prévoir
           posi_tab_front_point=tail_ar+tail_fa; // après les faces et les lignes
           posi_tab_front_lin = tail_fa; // après les surfaces
         }      
         else
         { // cas où il n'y a pas de frontières lignes         
           if (ind_front_surf == 0)  // cas où il n'y a pas de surface
                {tabb.Change_taille(tail_po,NULL); // on n'a pas de ligne, pas de point et pas de surface
                 posi_tab_front_point = posi_tab_front_lin = 0;}
           else {tabb.Change_taille(tail_po+tail_fa); // cas où les surfaces existent
                   // le redimensionnement n'affecte pas les surfaces qui sont en début de tableau
                 posi_tab_front_lin = posi_tab_front_point = tail_fa; // après les surfaces
                 };
         };
         // et on met les pointeurs de points en NULL
         for (int i=1;i<= tail_po;i++) 
            { tabb(i+posi_tab_front_point) = NULL;}
        };
    // maintenant création de la frontière point
    Tableau <Noeud *> tab(1); // les noeuds de la frontiere
    tab(1) = tab_noeud(num);
    DdlElement ddelem(1);  // les ddlelements des points frontieres
    ddelem.Change_un_ddlNoeudElement(1,tdd(num));
    tabb(posi_tab_front_point+num) = new FrontPointF(tab,ddelem);
    // on met à jour l'indicateur ind_front_point
    ind_front_point = 1; // a priori
    for (int npoint=1;npoint<=tail_po;npoint++)
      if (tabb(posi_tab_front_point+npoint) == NULL)
        { ind_front_point = 2; break;};
   };
  // maintenant normalement la frontière est créé on la ramène
  return (ElFrontiere*)tabb(posi_tab_front_point+num);
};
		
// ramène la frontière linéique
// éventuellement création des frontieres linéique de l'element et stockage dans l'element 
// si c'est la première fois et en 3D sinon il y a seulement retour de l'elements
// a moins que le paramètre force est mis a true
// dans ce dernier cas la frontière effacéee est recréée
// num indique le numéro de l'arête à créer (numérotation EF)
// nbneA: nombre de noeuds des segments frontières
// el : l'élément
ElFrontiere* const  ElemMeca::Frontiere_lineique(int num,bool force)
{ // -- tout d'abord on évacue le cas où il n'y a pas de frontière linéique à calculer
  // récup de l'élément géométrique
  ElemGeomC0& el = ElementGeometrique();
  int tail_ar = el.NbSe(); // nombre potentiel d'arêtes
  if (num > tail_ar)
    return NULL;

  // le calcul et la création ne sont effectués qu'au premier appel
  // ou lorsque l'on veut forcer une recréation
  //   on regarde si les frontières linéiques existent sinon on les crée
  if (ind_front_lin == 1)
    {return (ElFrontiere*)tabb(posi_tab_front_lin+num);}
  else if ( ind_front_lin == 2)
   // cas où certaines frontières existent
   {if (tabb(posi_tab_front_lin+num) != NULL)
     return (ElFrontiere*)tabb(posi_tab_front_lin+num);
   };
  
  // arrivée ici cela veut dire que la frontière ligne n'existe pas
  // on l'a reconstruit
  
  
  // le calcul et la création ne sont effectués qu'au premier appel
  // ou lorsque l'on veut forcer une recréation
  if ((ind_front_lin == 0) || force ) 
   {// récup du tableau de ddl
    const DdlElement & tdd = TableauDdl();
    int taille = tabb.Taille(); // la taille initiales des frontières
    int tail_fa = el.NbFe(); // nombre potentiel de faces
    int tail_po = el.Nbne(); // nombre potentiel de points
    // dimensionnement du tableau de frontières ligne si nécessaire
    if (ind_front_lin == 0)
      {if ((ind_front_point > 0)  && (ind_front_surf == 0))
        // cas où les frontières points existent seules, on ajoute les lignes
        { int taille_finale = tail_ar + tail_po;
          tabb.Change_taille(taille_finale);
          for (int i=1;i<= tail_po;i++)
             tabb(i+tail_ar) = tabb(i);
          posi_tab_front_point=tail_ar;
          posi_tab_front_lin = 0; // car on n'a pas de surface
        }
       else if ((ind_front_point > 0)  && (ind_front_surf > 0))
        // cas où les frontières points existent et surfaces et pas de ligne, donc on les rajoutes
        { int taille_finale = tail_fa + tail_po+tail_ar;
          tabb.Change_taille(taille_finale);// les grandeurs pour les surfaces sont conservées
          for (int i=1;i<= tail_po;i++) // transfert pour les noeuds
              { tabb(i+tail_ar+tail_fa) = tabb(i+tail_fa);};
          posi_tab_front_point=tail_ar+tail_fa; // après les faces et les lignes
          posi_tab_front_lin = tail_fa; // après les surfaces
        }
       else
        { // cas où il n'y a pas de frontières points
          if (ind_front_surf == 0)  // cas où il n'y a pas de surface
               {tabb.Change_taille(tail_ar,NULL); // on n'a pas de ligne, pas de point et pas de surface
                posi_tab_front_lin = 0; posi_tab_front_point = tail_ar;
               }
          else {tabb.Change_taille(tail_ar+tail_fa); // cas où les surfaces existent
                  // le redimensionnement n'affecte pas les surfaces qui sont en début de tableau
                posi_tab_front_lin =  tail_fa; // après les surfaces
                posi_tab_front_point = tail_fa+tail_ar;
                };
        };
       // et on met les pointeurs de lignes en NULL
       for (int i=1;i<= tail_ar;i++) // transfert pour les noeuds
          { tabb(i+posi_tab_front_lin) = NULL;}
      };
    // maintenant création de la ligne 
    int nbnoe = el.NonS()(num).Taille(); // nb noeud de l'arête
    Tableau <Noeud *> tab(nbnoe); // les noeuds de l'arête frontiere 
    DdlElement ddelem(nbnoe);  // les ddlelements des noeuds frontieres
    for (int i=1;i<= nbnoe;i++)
      { tab(i) = tab_noeud(el.NonS()(num)(i));
        ddelem.Change_un_ddlNoeudElement(i,tdd(el.NonS()(num)(i)));
      };
    tabb(posi_tab_front_lin+num) = new_frontiere_lin(num,tab,ddelem);
    ind_front_lin = 1; // a priori
    for (int nlign=1;nlign<=tail_ar;nlign++)
      if (tabb(posi_tab_front_lin+nlign) == NULL)
        { ind_front_lin = 2; break;};
   };
  // maintenant normalement la frontière est créé on la ramène
  return (ElFrontiere*)tabb(posi_tab_front_lin+num);
};

// ramène la frontière surfacique
// éventuellement création des frontieres surfacique de l'element et stockage dans l'element 
// si c'est la première fois sinon il y a seulement retour de l'elements
// a moins que le paramètre force est mis a true
// dans ce dernier cas la frontière effacéee est recréée
// num indique le numéro de la surface à créer (numérotation EF)
ElFrontiere* const  ElemMeca::Frontiere_surfacique(int num,bool force)
{ // -- tout d'abord on évacue le cas où il n'y a pas de frontière surfacique à calculer
  // récup de l'élément géométrique
  ElemGeomC0& el = ElementGeometrique();
  int tail_fa = 0; // init
 
  // test --- retour rapide si l'existance n'est pas possible
  if (ParaGlob::AxiSymetrie())
   // dans le cas axiSymétrique, le test est un peu différent car les surfaces sont générées
   // par les lignes,
   {tail_fa = el.NbSe(); // nombre potentiel de segments
    if (num > tail_fa)
        return NULL;
   }
  else // dans le cas où on n'est pas en axi, test simple
   {tail_fa = el.NbFe(); // nombre potentiel de faces
    if (num > tail_fa)
        return NULL;
   };

  // --- la suite concerne le cas où on peut faire des surfaces (non axi ou axi !)
  // le calcul et la création ne sont effectués qu'au premier appel
  // ou lorsque l'on veut forcer une recréation
  //   on regarde si les frontières surfacique existent sinon on les crée
  if (ind_front_surf == 1)
    {return (ElFrontiere*)tabb(num);}
  else if ( ind_front_surf == 2)
   // cas où certaines frontières existent
   {if (tabb(num) != NULL)
     return (ElFrontiere*)tabb(num);
   };
  
  // arrivée ici cela veut dire que la frontière surface n'existe pas
  // on l'a reconstruit
  
  
  // le calcul et la création ne sont effectués qu'au premier appel
  // ou lorsque l'on veut forcer une recréation
  if ((ind_front_surf == 0) || force )
   {// récup du tableau de ddl
    const DdlElement & tdd = TableauDdl();
    int taille = tabb.Taille(); // la taille initiales des frontières
    int tail_ar = el.NbSe(); // nombre potentiel d'arêtes
    int tail_po = el.Nbne(); // nombre potentiel de points
    // dimensionnement du tableau de frontières surfaces si nécessaire
    if (ind_front_surf == 0)
        {if ((ind_front_point > 0)  && (ind_front_lin == 0))
         // cas où les frontières points existent seules, on ajoute les surfaces
         { int taille_finale = tail_fa + tail_po;
           tabb.Change_taille(taille_finale);
           for (int i=1;i<= tail_po;i++)
               tabb(i+tail_fa) = tabb(i);
           posi_tab_front_lin=posi_tab_front_point=tail_fa;
         }      
         else if ((ind_front_point > 0)  && (ind_front_lin > 0))
         // cas où les frontières points existent et lignes et pas de surfaces, donc on les rajoutes
         { int taille_finale = tail_fa + tail_po+tail_ar;
           tabb.Change_taille(taille_finale);
           // --transfert pour les noeuds et les lignes
           for (int i=1;i<= tail_po;i++) // transfert pour les noeuds
               { tabb(i+tail_ar+tail_fa) = tabb(i+tail_ar);};
           for (int i=1;i<= tail_ar;i++) // transfert pour les lignes
               { tabb(i+tail_fa) = tabb(i);};
           // --def des indicateurs
           posi_tab_front_point=tail_ar+tail_fa; // après les faces et les lignes
           posi_tab_front_lin = tail_fa; // après les surfaces
          }      
         else
         { // cas où il n'y a pas de frontières points         
           if (ind_front_lin == 0)  // cas où il n'y a pas de lignes
                {tabb.Change_taille(tail_fa,NULL); // on n'a pas de ligne, pas de point et pas de surface
                 posi_tab_front_lin = posi_tab_front_point = tail_fa;
                 }         // on peut tout mettre à NULL
           else {tabb.Change_taille(tail_ar+tail_fa); // cas où les lignes existent
                 for (int i=1;i<= tail_ar;i++) // transfert pour les lignes
                    tabb(i+tail_fa) = tabb(i);
                 posi_tab_front_lin = tail_fa; // après les surfaces
                 posi_tab_front_point = tail_fa + tail_ar;
                 };
         };
           // --et on met les pointeurs de surfaces en NULL
         for (int i=1;i<=tail_fa;i++) 
            tabb(i)=NULL;
        };
    // maintenant création de la surface 
    int nbnoe = el.Nonf()(num).Taille(); // b noeud de la surface
    Tableau <Noeud *> tab(nbnoe); // les noeuds de la frontiere 
    DdlElement ddelem(nbnoe);  // les ddlelements des noeuds frontieres
    for (int i=1;i<= nbnoe;i++)
      { tab(i) = tab_noeud(el.Nonf()(num)(i));
        ddelem.Change_un_ddlNoeudElement(i,tdd(el.Nonf()(num)(i)));
      };
    tabb(num) = new_frontiere_surf(num,tab,ddelem);
    ind_front_surf = 1; // a priori
    for (int nsurf=1;nsurf<=tail_fa;nsurf++)
      if (tabb(nsurf) == NULL)
        { ind_front_surf = 2; break;};
   };
  // maintenant normalement la frontière est créé on la ramène
  return (ElFrontiere*)tabb(num);
};

// calcul des intégrales de volume et de volume + temps
// cas = 1 : pour un appel après un calcul implicit
// cas = 2 : pour un appel après un calcul explicit
void ElemMeca::Calcul_Integ_vol_et_temps(int cas,const double& poid_jacobien,int iteg)
   {  // 1) intégration de volume uniquement
      //List_io <TypeQuelconque> integ_vol_typeQuel,integ_vol_typeQuel_t;
      if (integ_vol_typeQuel != NULL)
      // on balaie les conteneurs
      { int taille = integ_vol_typeQuel->Taille();
        Tableau <TypeQuelconque> & tab_integ = *integ_vol_typeQuel; // pour simplifier
        for (int il =1;il <= taille ;il++)
         // l'intégrale n'est effectuée que si l'index est positif, sinon, il s'agit
         // d'une valeur figée
         if ((*index_Integ_vol_typeQuel)(il))
          {// différent choix
           switch (tab_integ(il).Const_Grandeur_pointee()->Type_enumGrandeurParticuliere())
           {case PARTICULIER_DDL_ETENDU:
              { Grandeur_Ddl_etendu& gr
                       = *((Grandeur_Ddl_etendu*) tab_integ(il).Grandeur_pointee()); // pour simplifier
                Ddl_etendu& ddl = *(gr.ConteneurDdl_etendu());// récup du ddl
                // on va utiliser la méthode Valeur_multi ce qui n'est pas optimal en vitesse
                // mais 1) c'est cohérent avec l'implantation existante
                //      2) identique avec les ddl déjà accessibles
                List_io<Ddl_enum_etendu> inter; // une liste intermédiaire
                inter.push_back(ddl.DdlEnumEtendu());
                bool absolue = true; // on se place systématiquement en absolu
                // calcul de la valeur et retour dans tab_d
                Tableau <double> tab_d(ElemMeca::Valeur_multi(absolue,TEMPS_tdt,inter,iteg,-cas));
                // on cumule l'intégrale
                ddl.Valeur() += tab_d(1) * poid_jacobien;
////---- debug
//cout << "\n debug: ElemMeca::Calcul_Integ_vol_et_temps(";
//cout << " tab_d(1)= "<< tab_d(1) << " ";ddl.Affiche();
//tab_noeud(1)->Affiche();
////---- fin debug
               
                break;
              }
            case PARTICULIER_VECTEUR_NOMMER:
              { Grandeur_Vecteur_Nommer& gr
                       = *((Grandeur_Vecteur_Nommer*) tab_integ(il).Grandeur_pointee()); // pour simplifier
                Fonction_nD* fct = gr.Fct(); // récupération de la fonction nD
                // on commence par récupérer les conteneurs des grandeurs à fournir
                List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
                List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
                bool absolue = true; // on se place systématiquement en absolu
                // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
                Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi(absolue,TEMPS_tdt,li_enu_scal,iteg,-cas));
                // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
                Valeurs_Tensorielles(absolue, TEMPS_tdt,li_quelc,iteg,-cas);
                // calcul de la valeur et retour dans tab_ret
                Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
               
                // on cumule l'intégrale
                Vecteur& V = gr.ConteneurVecteur();
                int taille = V.Taille();
                for (int i=1;i<=taille;i++)
                  V(i) += tab_ret(i) * poid_jacobien;
////----- debug
//cout << "\n debug *** ElemMeca::Grandeur_particuliere( ";
//cout << "\n li_enu_scal= "<< li_enu_scal << ", val_ddl_enum= " << val_ddl_enum;
//cout << "\n li_quelc= "<< li_quelc;
//V.Affiche();cout << " tab_ret= " <<  tab_ret << endl;;
////----- fin debug
                 break;
              }
                break;
            default:
             cout << " \n *** pas d'integration prevu pour la grandeur " << tab_integ(il);
             if (ParaGlob::NiveauImpression() > 0)
               cout << "\n ElemMeca::Calcul_Integ_vol_et_temps(..." << endl;
           };
         };
      };
      // 2) intégration de volume et en temps
      //List_io <TypeQuelconque> integ_vol_t_typeQuel,integ_vol_t_typeQuel_t;
      if (integ_vol_t_typeQuel != NULL)
      // on balaie les conteneurs
      { int taille = integ_vol_t_typeQuel->Taille();
        Tableau <TypeQuelconque> & tab_integ = *integ_vol_t_typeQuel; // pour simplifier
        // recup de l'incrément de temps
        double deltat=ParaGlob::Variables_de_temps().IncreTempsCourant();
        for (int il =1;il <= taille ;il++)
         // l'intégrale n'est effectuée que si l'index est positif, sinon, il s'agit
         // d'une valeur figée
         if ((*index_Integ_vol_t_typeQuel)(il))
          {// différent choix
          switch (tab_integ(il).Const_Grandeur_pointee()->Type_enumGrandeurParticuliere())
           {case PARTICULIER_DDL_ETENDU:
              { Grandeur_Ddl_etendu& gr
                       = *((Grandeur_Ddl_etendu*) tab_integ(il).Grandeur_pointee()); // pour simplifier
                Ddl_etendu& ddl = *(gr.ConteneurDdl_etendu());// récup du ddl
                // on va utiliser la méthode Valeur_multi ce qui n'est pas optimal en vitesse
                // mais 1) c'est cohérent avec l'implantation existante
                //      2) identique avec les ddl déjà accessibles
                List_io<Ddl_enum_etendu> inter; // une liste intermédiaire
                inter.push_back(ddl.DdlEnumEtendu());
                bool absolue = true; // on se place systématiquement en absolu
                // calcul de la valeur et retour dans tab_d
                Tableau <double> tab_d(ElemMeca::Valeur_multi(absolue,TEMPS_tdt,inter,iteg,-cas));
                // on cumule l'intégrale
                ddl.Valeur() += deltat * tab_d(1) * poid_jacobien;
                break;
              }
            case PARTICULIER_VECTEUR_NOMMER:
              { Grandeur_Vecteur_Nommer& gr
                       = *((Grandeur_Vecteur_Nommer*) tab_integ(il).Grandeur_pointee()); // pour simplifier
                Fonction_nD* fct = gr.Fct(); // récupération de la fonction nD
                // on commence par récupérer les conteneurs des grandeurs à fournir
                List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
                List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
                bool absolue = true; // on se place systématiquement en absolu
                // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
                Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi(absolue,TEMPS_tdt,li_enu_scal,iteg,-cas));
                // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
                Valeurs_Tensorielles(absolue, TEMPS_tdt,li_quelc,iteg,-cas);
                // calcul de la valeur et retour dans tab_ret
                Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
               
                // on cumule l'intégrale
                Vecteur& V = gr.ConteneurVecteur();
                int taille = V.Taille();
                for (int i=1;i<=taille;i++)
                  V(i) += deltat * tab_ret(i) * poid_jacobien;
                 break;
              }
                break;
            default:
             cout << " \n *** pas d'integration prevu pour la grandeur " << tab_integ(il);
             if (ParaGlob::NiveauImpression() > 0)
               cout << "\n ElemMeca::Calcul_Integ_vol_et_temps(..." << endl;
           };
         };
      };
   };

// au premier pti init éventuel des intégrales de volume et temps
void ElemMeca::Init_Integ_vol_et_temps()
{  // 1) intégration de volume uniquement
   if (integ_vol_typeQuel != NULL)
    {int taille = integ_vol_typeQuel->Taille();
     Tableau <TypeQuelconque> & tab_integ = *integ_vol_typeQuel; // pour simplifier
     for (int il =1;il <= taille ;il++)
      // l'intégrale n'est effectuée que si l'index est positif, sinon, il s'agit
      // d'une valeur figée
      if ((*index_Integ_vol_typeQuel)(il))
       {tab_integ(il).Grandeur_pointee()->InitParDefaut(); // on initialise le conteneur
       };
    };
   // 2) intégration de volume et en temps
   if (integ_vol_t_typeQuel != NULL)
    {int taille = integ_vol_t_typeQuel->Taille();
     Tableau <TypeQuelconque> & tab_integ = *integ_vol_t_typeQuel; // pour simplifier
     Tableau <TypeQuelconque> & tab_integ_t = *integ_vol_t_typeQuel_t; // ""
     for (int il =1;il <= taille ;il++)
      // l'intégrale n'est effectuée que si l'index est positif, sinon, il s'agit
      // d'une valeur figée
      if ((*index_Integ_vol_t_typeQuel)(il))
       {(*tab_integ(il).Grandeur_pointee()) = (*tab_integ_t(il).Grandeur_pointee());
       };
    };
 
};

// définition d'un repère d'anisotropie à un point d'intégration
BaseH  ElemMeca::DefRepereAnisotropie
            (int iteg,LesFonctions_nD*  lesFonctionsnD,const BlocGen & bloc)
{ // la définition du repère dépend du type de définition
  // (cf. la def du bloc dans LesMaillages::Completer .... dans LesMaillages.cc)
  if (bloc.Nom(4) == "par_fonction_nD_")
   {// on doit simplement appeler la fonction nD
    // Nom(5) est le nom de la fonction nD
    Fonction_nD * fct = lesFonctionsnD->Trouve(bloc.Nom(5));
    // on commence par récupérer les conteneurs des grandeurs à fournir
    List_io <Ddl_enum_etendu>& li_enu_scal = fct->Li_enu_etendu_scalaire();
    List_io <TypeQuelconque >& li_quelc = fct->Li_equi_Quel_evolue();
    bool absolue = true; // on se place systématiquement en absolu
    
    const Met_abstraite::Impli* ex_impli = NULL;
    const Met_abstraite::Expli_t_tdt* ex_expli_tdt = NULL;
    const Met_abstraite::Expli* ex_expli = NULL;
    // pour l'instant on ne peut s'occuper que des grandeurs à t au maximum: mais pas
    // à tdt
    bool premier_calcul = true;
//    def->ChangeNumInteg(iteg);
//    const Met_abstraite::Expli& ex = def->Cal_explicit_t(premier_calcul);
//    ex_expli = &ex;
//    // on va utiliser la méhode Valeur_multi_interpoler_ou_calculer
//    // pour les grandeurs strictement scalaire
//     Tableau <double> val_ddl_enum(Valeur_multi_interpoler_ou_calculer
//                 (absolue,TEMPS_t,li_enu_scal,*def,ex_impli,ex_expli_tdt,ex_expli)
//                                   );
//    // on utilise la méthode Valeurs_Tensorielles_interpoler_ou_calculer
//    // pour les Coordonnees et Tenseur
//    Valeurs_Tensorielles_interpoler_ou_calculer
//                  (absolue,TEMPS_t,li_quelc,*def,ex_impli,ex_expli_tdt,ex_expli);

    
    // on va utiliser la méhode Valeur_multi pour les grandeurs strictement scalaire
    int cas = 1; // on se met dans un cas normal avec utilisation de certaine partie de la métrique implicite
                 // a priori ce serait idem avec l'explicite
    Tableau <double> val_ddl_enum(ElemMeca::Valeur_multi(absolue,TEMPS_t,li_enu_scal,iteg,cas));
    // on utilise la méthode des grandeurs évoluées pour les Coordonnees et Tenseur
    Valeurs_Tensorielles(absolue, TEMPS_t,li_quelc,iteg,cas);

    // calcul de la valeur et retour dans tab_ret
    Tableau <double> & tab_ret = fct->Valeur_FnD_Evoluee(&val_ddl_enum,&li_enu_scal,&li_quelc,NULL,NULL);
    // on doit récupérer 6 composantes en 3D (pour le reste c'est actuellement en attente)
    int dim = ParaGlob::Dimension();
    if (dim != 3)
     { cout << "\n arret: le type de construction de repere: par_fonction_nD_ "
            << " n'est implante que pour la dimension 3 pour l'instant !!! "
            << " affaire a suivre ....(et se plaindre! ) ";
       Sortie(1);
     };
    int nb_composante = tab_ret.Taille();
    if (nb_composante != 6)
     { cout << "\n erreur dans la construction d'un repere d'orthotropie par_fonction_nD_ "
            << " la fonction nD ramene "<<nb_composante << " composante(s) "
            << " or il en faut 6 uniquement ! ";
       Sortie(1);
     };
    // arrivée ici, cela veut dire que tout est ok
    //  --- construction de la base dans I_a -------
    BaseH baseH(3,3);
    // premier vecteur:
    CoordonneeH& c1 = baseH.CoordoH(1);
    for (int i=1;i<4;i++)
      c1(i) = tab_ret(i);
    // on norme si c'est demandé
    if ( bloc.Nom(3) == "orthonorme_")
     {c1.Normer();
     }
    else
     { cout << "\n arret: le type de repere: "<<bloc.Nom(3)
            << " n'est pas encore implante !!! affaire a suivre ....(et se plaindre! ) ";
       Sortie(1);
     };
    // le deuxième vecteur:
    CoordonneeH& c2 = baseH.CoordoH(2);
    for (int i=1;i<4;i++)
      c2(i) = tab_ret(i+3);
    // on norme
    c2.Normer();
    // le troisième vecteur
    CoordonneeH& c3 = baseH.CoordoH(3);
    c3 = Util::ProdVec_coorH(c1, c2);
    
/*  ***** la suite est supprimée: elle correspond au calcul des coordonnées locales alpha_a^{.i}
    du repère d'anisotropie. Le pb est que le repère local g_i à t= 0 peut changer pendant le calcul
    par exemple dans le cas de l'utilisation des contraintes planes on va avoir un repère de travail
    qui change. Sans doute que ce sera un pb identique avec les Umat
    donc on passe en paramètre le repère en coordonnée absolue et au moment de l'utilisation
    les coordonnées locales seront calculées
 
  si c'est ok par la suite, il faudra supprimer la partie commentée qui suit
 
    //  --- maintenant on s'occupe de calculer la base en local -----
    // en fait il s'agit de définir les coordonnées locales par projection de la base
    // globale en local
    def->ChangeNumInteg(iteg);
    const Met_abstraite::InfoExp_t & ex_infoExp_t  = def->RemontExp_t();
    int nb_vec = ex_infoExp_t.giB_0->NbVecteur(); // nb vecteur local
    BaseH base_ret_H(3,3);
    // les coordonnées locales : théta^te  = V * g^te
    // soit O_a la base d'anisotropie: O_a = beta_a^{.i} g_i
    // ona beta_a^{.i} = O_a . g^i
    // et on stocke dans base_ret_H de la manière suivante
    // base_ret_H(a)(i) = beta_a^{.i}
    for (int a = 1;a <= 3; a++)
      {CoordonneeH& inter = base_ret_H.CoordoH(a);
       for (int i=1;i<nb_vec;i++)
       inter(i)= baseB.CoordoB(a) * (*ex_infoExp_t.giH_0)(i);
      };
    // dans le cas où nb_vec n'est pas égale à 3 il faut complèter la base
    switch (nb_vec)
      { case 3: break; // cas où c'est déjà ok
        case 2: // cas où l'élément est une surface, le troisième vecteur est la normale
         {CoordonneeH N_H = Util::ProdVec_coorH((*ex_infoExp_t.giH_0)(1),(*ex_infoExp_t.giH_0)(2));
          for (int a = 1;a <= 3; a++)
             base_ret_H.CoordoH(a)(3)= baseB.CoordoB(a) * N_H;
          break;
         }
        case 1: // cas où l'élément est un segment, il faut définir deux autres vecteurs
         {// on génère
  la suite est problèmatique car lorsque l'on changera de repère local, les coordonnées locales
  devraient changer donc on arrête
         
         CoordonneeH N_H = Util::ProdVec_coorH((*ex_infoExp_t.giH_0)(1),(*ex_infoExp_t.giH_0)(2));
          for (int a = 1;a <= 3; a++)
             base_ret_H.CoordoH(a)(3)= baseB.CoordoB(a) * N_H;
          break;
         }

  default:
    break;
}
    
    
    
    for (int i=1;i<4;i++)
      {CoordonneeH& inter = base_ret_H.CoordoH(i);
       for (int te = 1;te <= nb_vec; te++)
       inter(te)= baseB.CoordoB(i) * (*ex_infoExp_t.giH_0)(te);
      };
    return base_ret_H;
*/
//cout << "\n debug ElemMeca::DefRepereAnisotropie ";
//for (int a = 1;a < 4; a++)
//  {cout << "\n  baseH("<<a<<"): ";
//   baseH(a).Affiche();
//  };


    return baseH;
   }
  else if (bloc.Nom(4) == "Par_projection_et_normale_au_plan_")
   { cout << "\n arret: le type de construction de repere: "<<bloc.Nom(3)
          << " n'est pas encore implante !!! affaire a suivre ....(et se plaindre! ) ";
     Sortie(1);
   }
  else
   { cout << "\n **** erreur: type de construction de repere: "<<bloc.Nom(3)
          << " inconnu ?? ";
     Sortie(1);
   
   };
  return BaseH(); // pour faire taire le compilo
};

 
// calcul éventuel de la normale à un noeud
// ce calcul existe pour les éléments 2D, 1D axi, et aussi pour les éléments 1D
// qui possède un repère d'orientation
// en retour coor = la normale si coor.Dimension() est = à la dimension de l'espace
// si le calcul n'existe pas -->  coor.Dimension() = 0
// ramène un entier :
//    == 1 : calcul normal
//    == 0 : problème de calcul -> coor.Dimension() = 0
//    == 2 : indique que le calcul n'est pas licite pour le noeud passé en paramètre
//           c'est le cas par exemple des noeuds exterieurs pour les éléments SFE
//           mais il n'y a pas d'erreur, c'est seulement que l'élément n'est pas ad hoc pour
//           calculer la normale à ce noeud là
// temps: indique  à quel moment on veut le calcul
// pour des éléments particulier (ex: SFE) la méthode est surchargée
int ElemMeca::CalculNormale_noeud(Enum_dure temps,const Noeud& noe,Coordonnee& coor)
  {
     int retour = 1; // init du retour : on part d'un bon a priori
     Enum_type_geom enutygeom =  Type_geom_generique(this->Id_geometrie());
//     if ((enutygeom == LIGNE) || (enutygeom == SURFACE))
     if (enutygeom != SURFACE) // dans une première étape on ne s'occupe que des surfaces
        {coor.Libere(); // pas de calcul possible
         retour = 0;
        }
     else // sinon le calcul est possible
       { // on commence par repérer le noeud dans la numérotation locale
         int nuoe=0;
         int borne_nb_noeud=tab_noeud.Taille()+1;
         for (int i=1;i< borne_nb_noeud;i++)
           {Noeud& noeloc = *tab_noeud(i);
            if (   (noe.Num_noeud() == noeloc.Num_noeud())
                && (noe.Num_Mail() == noeloc.Num_Mail())
               )
              {nuoe = i; break;
              };
            };
          // on ne continue que si on a trouvé le noeud
          if (nuoe != 0)
           { ElemGeomC0& elemgeom = ElementGeometrique(); // récup de la géométrie
             // récup des coordonnées locales du noeuds
             const Coordonnee& theta_noeud = elemgeom.PtelemRef()(nuoe);
             // récup des phi et dphi au noeud
             const Vecteur & phi = elemgeom.Phi_point(theta_noeud);
             const Mat_pleine& dphi = elemgeom.Dphi_point(theta_noeud);
             switch (temps)
              {case TEMPS_0 :
                 {const BaseB& baseB = met->BaseNat_0(tab_noeud,dphi,phi);
                  coor = Util::ProdVec_coor( baseB.Coordo(1), baseB.Coordo(2));
                  coor.Normer();
                  break;
                 }
               case TEMPS_t :
                {const BaseB& baseB = met->BaseNat_t(tab_noeud,dphi,phi);
                 coor = Util::ProdVec_coor( baseB.Coordo(1), baseB.Coordo(2));
                 coor.Normer();
                 break;
                }
               case TEMPS_tdt :
                {const BaseB& baseB = met->BaseNat_tdt(tab_noeud,dphi,phi);
                 coor = Util::ProdVec_coor( baseB.Coordo(1), baseB.Coordo(2));
                 coor.Normer();
                 break;
                }
               default :
                cout << "\nErreur : valeur incorrecte du  temps demande = "
                     << Nom_dure(temps) << " !\n";
                cout << "\n ElemMeca::CalculNormale_noeud(Enum_dure temps... \n";
                retour = 0;
                Sortie(1);
              };
           }
          else
           {cout << "\n *** erreur le noeud demande num= "<<noe.Num_noeud()
                 << " du maillage "<< noe.Num_Mail()
                 << " ne fait pas parti de l'element "
                 << num_elt << " du maillage "<< noe.Num_Mail()
                 << " on ne peut pas calculer la normale au noeud !!"
                 << "\n ElemMeca::CalculNormale_noeud(...";
            retour = 0;
            Sortie(1);
           }
       };
     // retour
     return retour;
  };






