// FICHIER : SfeMembT.cc
// CLASSE : SfeMembT


// 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 <iostream>
#include <cmath>
using namespace std;  //introduces namespace std
#include <stdlib.h>
#include "Sortie.h"
#include "MathUtil.h"

#include "SfeMembT.h"
#include "TypeConsTens.h"
#include "FrontPointF.h"
#include "Loi_Umat.h"
#include "ExceptionsElemMeca.h"
#include "ExceptionsLoiComp.h"
#include "CharUtil.h"

// =========================== variables communes ===============================
// a priori les pointeurs de fonction pointent sur rien au debut
TenseurHH* SfeMembT::sig_bulk_pourSfe_HH = NULL;  // variable de travail pour le bulk
                    
// =========================== fin variables communes ===========================
// ---- definition du constructeur de la classe conteneur de donnees communes ------------	

SfeMembT::DonnComSfe::DonnComSfe (ElemGeomC0* elCentre,const GeomSeg& seg,const  DdlElement& tab
	                       ,DdlElement&  tabErr,DdlElement&  tab_Err1Sig,DdlElement&  t_ddlXi_C
	                       ,const Met_Sfe1&  met_gene,
	                        Tableau <Vecteur *> & resEr,Mat_pleine& raidEr,
	                        ElemGeomC0* elEr,GeomSeg& segEr,ElemGeomC0* elS,GeomSeg& segmS
	                       ,Vecteur&  residu_int,Mat_pleine&  raideur_int,
	                        Tableau <Vecteur* > & residus_extN,Tableau <Mat_pleine* >&  raideurs_extN,
	                        Tableau <Vecteur* > & residus_extA,Tableau <Mat_pleine* >&  raideurs_extA,
	                        Tableau <Vecteur* >&  residus_extS,Tableau <Mat_pleine* >&  raideurs_extS,
	                        Mat_pleine&  mat_masse,ElemGeomC0* elMas,const GeomSeg& segMas,int nbi,
	                        Tableau <EnuTypeCL> const & tabTypeCL,Tableau <Coordonnee3> const & vplan
	                        ,Tableau <int>& nMetddl,const Met_abstraite&  met_cent ) :
	          eleCentre(elCentre),segment(seg),tab_ddl(tab),tab_ddlErr(tabErr),tab_Err1Sig11(tab_Err1Sig)
	          ,tab_ddlXi_C(t_ddlXi_C),met_SfeMembT(met_gene)
	          ,matGeom(tab.NbDdl(),tab.NbDdl()),matInit(tab.NbDdl(),tab.NbDdl())
	          ,d_epsBB(tab.NbDdl()),d_sigHH(tab.NbDdl()),d2_epsBB(nbi)
	          ,resErr(resEr),raidErr(raidEr),eleEr(elEr),segmentEr(segEr),eleS(elS),segS(segmS)
	          ,residu_interne(residu_int),raideur_interne(raideur_int)
	          ,residus_externeN(residus_extN),raideurs_externeN(raideurs_extN)
	          ,residus_externeA(residus_extA),raideurs_externeA(raideurs_extA)
	          ,residus_externeS(residus_extS),raideurs_externeS(raideurs_extS)
	          ,matrice_masse(mat_masse),eleMas(elMas),segmentMas(segMas)
	          ,tabType_rienCL(tabTypeCL),vplan_rien(vplan)
	          ,nMetVTab_ddl(nMetddl),met_surf_cent(met_cent)
           ,sfematD(NULL),sferesD(NULL),noeud_a_prendre_en_compte(NULL)
	{ 
   int nbddl = tab.NbDdl();
   int dim_tens = met_gene.Nbvec_des_bases();
   for (int ni=1;ni<=nbi;ni++)
    {d2_epsBB(ni).Change_taille(nbddl);
     for (int i1=1; i1<= nbddl; i1++)
      for (int i2=1; i2<= nbddl; i2++)
       d2_epsBB(ni)(i1,i2) = NevezTenseurBB (dim_tens);
    };
//      int tailledeps = d_epsBB.Taille();
//	  for (int i=1;i<= tailledeps; i++)
	  for (int i=1;i<= nbddl; i++)
	         d_epsBB(i) = NevezTenseurBB (dim_tens);
//      int tailledsig = d_sigHH.Taille();
//	  for (int j=1;j<= tailledsig; j++)
	  for (int j=1;j<= nbddl; j++)
	         d_sigHH(j) = NevezTenseurHH (dim_tens);
	 };
// Il ne doit exister  qu'un exemplaire de la classe, donc au niveau du constructeur de copie les tableaux
// de pointeurs pointes sur les mêmes entités que la valeur passée en paramètre	              
SfeMembT::DonnComSfe::DonnComSfe(DonnComSfe& a) :
   eleCentre(a.eleCentre),segment(a.segment)
			,tab_ddl(a.tab_ddl),tab_ddlErr(a.tab_ddlErr),tab_Err1Sig11(a.tab_Err1Sig11)
   ,tab_ddlXi_C(a.tab_ddlXi_C),met_SfeMembT(a.met_SfeMembT),matGeom(a.matGeom)
   ,matInit(a.matInit),d2_epsBB(a.d2_epsBB),resErr(a.resErr),raidErr(a.raidErr)
   ,eleEr(a.eleEr),segmentEr(a.segmentEr),eleS(a.eleS),segS(a.segS)
	  ,d_epsBB(a.d_epsBB),d_sigHH(a.d_sigHH)
	  ,residu_interne(a.residu_interne),raideur_interne(a.raideur_interne)
	  ,residus_externeN(a.residus_externeN),raideurs_externeN(a.raideurs_externeN)
	  ,residus_externeA(a.residus_externeA),raideurs_externeA(a.raideurs_externeA)
	  ,residus_externeS(a.residus_externeS),raideurs_externeS(a.raideurs_externeS)
	  ,matrice_masse(a.matrice_masse),eleMas(a.eleMas),segmentMas(a.segmentMas)
	  ,tabType_rienCL(a.tabType_rienCL),vplan_rien(a.vplan_rien)
	  ,nMetVTab_ddl(a.nMetVTab_ddl),met_surf_cent(a.met_surf_cent)
   ,sfematD(NULL),sferesD(NULL),noeud_a_prendre_en_compte(NULL)

	       { int nbddl = tab_ddl.NbDdl();
	         int nbi=d2_epsBB.Taille();
	         for (int ni=1;ni<=nbi;ni++)
              for (int i1=1; i1<= nbddl; i1++)
               for (int i2=1; i2<= nbddl; i2++)
                 d2_epsBB(ni)(i1,i2) = NevezTenseurBB (*(a.d2_epsBB(ni)(i1,i2)));
//             int tailledeps = d_epsBB.Taille();
//	         for (int i=1;i<= tailledeps; i++)
	         for (int i=1;i<= nbddl; i++)
	            d_epsBB(i) = NevezTenseurBB (*(a.d_epsBB(i)));
//             int tailledsig = d_sigHH.Taille();
//	         for (int j=1;j<= tailledsig; j++)
	         for (int j=1;j<= nbddl; j++)
	            d_sigHH(j) = NevezTenseurHH (*(d_sigHH(j)));
             
          // cas d'une stabilisation éventuelle
          if (a.sfematD != NULL)
            sfematD = new Mat_pleine(*a.sfematD);
          if (a.sferesD != NULL)
            sferesD = new Vecteur (*a.sferesD);
          if (a.noeud_a_prendre_en_compte != NULL)
            noeud_a_prendre_en_compte = new Tableau<int> (*a.noeud_a_prendre_en_compte);
        };
				  
// certaines grandeurs pointées qui n'exitent qu'en un seul exemplaire 
// ne seront supprimées que par l'ordre destruction, 				    
SfeMembT::DonnComSfe::DonnComSfe::~DonnComSfe()
       { int nbddl = tab_ddl.NbDdl();
	 	      int nbi=d2_epsBB.Taille();
	 	      for (int ni=1;ni<=nbi;ni++)
            for (int i1=1; i1<= nbddl; i1++)
               for (int i2=1; i2<= nbddl; i2++)
                  delete d2_epsBB(ni)(i1,i2);
         int tailledeps = d_epsBB.Taille();
	        for (int i=1;i<= tailledeps; i++)
            delete d_epsBB(i);
         int tailledsig = d_sigHH.Taille();
	        for (int j=1;j<= tailledsig; j++)
            delete d_sigHH(j);
         // cas d'une stabilisation éventuelle
         if (sfematD != NULL)
           delete sfematD;
         if (sferesD != NULL)
           delete sferesD;
         if (noeud_a_prendre_en_compte != NULL)
           delete noeud_a_prendre_en_compte;

       };
	             
	            
// ---------- fin definition de la  classe conteneur de donnees communes ------------	
// -+-+ definition de la classe contenant tous les indicateurs qui sont modifiés une seule fois -+-+-+
SfeMembT::UneFois::UneFois () : // constructeur par défaut
	doCoMemb(NULL),CalResPrem_t(0),CalResPrem_tdt(0),CalimpPrem(0),dualSortSfe(0)
	,CalSMlin_t(0),CalSMlin_tdt(0),CalSMRlin(0)
	,CalSMsurf_t(0),CalSMsurf_tdt(0),CalSMRsurf(0)
	,CalSMvol_t(0),CalSMvol_tdt(0),CalSMvol(0)
	,CalDynamique(0),CalPt_0_t_tdt(0)
	,nbelem_in_Prog(0)
	        {};
SfeMembT::UneFois::~UneFois ()
	 {	  delete doCoMemb;
	 };   

// -+-+ fin definition de la classe contenant tous les indicateurs qui sont modifiés une seule fois -+-+-+
	            
// CONSTRUCTEURS :
	// Constructeur par defaut
SfeMembT::SfeMembT () :
   ElemMeca(),lesPtMecaInt(),donnee_specif(),areteTypeCL(NULL),vplan(NULL)
	,t_N_centre(),unefois(NULL),nombre(NULL)
      { lesPtIntegMecaInterne = &lesPtMecaInt; //  association avec le pointeur d'ElemMeca
      };   
// Constructeur fonction  d'un numero
// d'identification , d'identificateur d'interpolation et de geometrie 
SfeMembT::SfeMembT (int num_mail,int num_id,Enum_interpol id_interp_elt,Enum_geom id_geom_elt,string info) :
     ElemMeca(num_mail,num_id,id_interp_elt,id_geom_elt,info),lesPtMecaInt(),donnee_specif()
     ,areteTypeCL(NULL),vplan(NULL),t_N_centre(),unefois(NULL),nombre(NULL)
      { lesPtIntegMecaInterne = &lesPtMecaInt; //  association avec le pointeur d'ElemMeca
      };
		
// Constructeur fonction  d'un numero de maillage et d'identification,
// du tableau de connexite des noeuds, d'identificateur d'interpolation et de geometrie
SfeMembT::SfeMembT (int num_mail,int num_id,Enum_interpol id_interp_elt,Enum_geom id_geom_elt,
                     const Tableau<Noeud *>& tab,string info) :
 ElemMeca(num_mail,num_id,tab,id_interp_elt,id_geom_elt,info),lesPtMecaInt(),donnee_specif()
 ,areteTypeCL(NULL),vplan(NULL),t_N_centre(),unefois(NULL),nombre(NULL)
      { lesPtIntegMecaInterne = &lesPtMecaInt; //  association avec le pointeur d'ElemMeca
      };
// Constructeur de copie
SfeMembT::SfeMembT (const SfeMembT& SfeM) :
 ElemMeca (SfeM)
 ,lesPtMecaInt(SfeM.lesPtMecaInt)
 ,donnee_specif(SfeM.donnee_specif),unefois(SfeM.unefois),nombre(SfeM.nombre)
 ,areteTypeCL(SfeM.areteTypeCL),vplan(SfeM.vplan),t_N_centre(SfeM.t_N_centre)
   { lesPtIntegMecaInterne = &lesPtMecaInt; //  association avec le pointeur d'ElemMeca
		   // --- cas des différentes  puissances et ... ---
     SfeMembT::DonnComSfe* CoSfe = unefois->doCoMemb; // pour simplifier l'écriture
     residu = &(CoSfe->residu_interne); // residu local
     raideur = &(CoSfe->raideur_interne); // raideur locale
         // --- cas de la dynamique -----
     mat_masse =  &(CoSfe->matrice_masse);
     	// --- cas des efforts externes concernant noeuds ------
     res_extN = &(CoSfe->residus_externeN); // pour les résidus et second membres
     raid_extN= &(CoSfe->raideurs_externeN);// pour les raideurs
     	// --- cas des efforts externes concernant les aretes ------
     res_extA = &(CoSfe->residus_externeA); // pour les résidus et second membres
     raid_extA= &(CoSfe->raideurs_externeA);// pour les raideurs
     	// --- cas des efforts externes concernant les faces  ------
     res_extS= &(CoSfe->residus_externeS); // pour les résidus et second membres
     raid_extS= &(CoSfe->raideurs_externeS); // pour les raideurs
     // pour les conditions limites on les crée si besoin
     if (areteTypeCL != (&(unefois->doCoMemb->tabType_rienCL)))
     	{ // si ça ne pointe pas sur la valeur par défaut, alors c'est qu'il y a
     	  // des CL particulières, on les crée donc à l'identique
     	  areteTypeCL = new Tableau <EnuTypeCL> (*(SfeM.areteTypeCL));
     	  vplan = new Tableau <Coordonnee3> (*(SfeM.vplan));
     	};
  };
	
// DESTRUCTEUR :
SfeMembT::~SfeMembT ()
{   /*  // on va libérer les déformations d'arrête et de surface qui sont un peu particulières
  if (defArete.Taille() != 0)
   if (defArete(1) != NULL) 
    { delete defArete(1);
      // on remet à null les pointeurs pour un appel correcte du destructeur d'elemMeca
      for (int ia=1;ia<= 12; ia++)
        defArete(ia) = NULL; 
     }    
  if (defSurf.Taille() != 0)
   if (defSurf(1) != NULL) 
    { delete defSurf(1);
      // on remet à null les pointeurs pour un appel correcte du destructeur d'elemMeca
      for (int ia=1;ia<= 12; ia++)
        defSurf(ia) = NULL; 
     }  */
  // concernant tous les éléments qui sont définit qu'une seule fois, leur destruction est effectuée avec la méthode destruction
  // appelée par les classes descendantes	   
  LibereTenseur();
 };


// Lecture des donnees de la classe sur fichier
void SfeMembT::LectureDonneesParticulieres
    (UtilLecture * entreePrinc,Tableau<Noeud  *> * tabMaillageNoeud)
  { int nb;int nbnte = nombre->nbnte;
    tab_noeud.Change_taille(nbnte);
    for (int i=1; i<= nbnte; i++)
     { *(entreePrinc->entree) >> nb;
       if ((entreePrinc->entree)->rdstate() == 0) 
       // pour mémoire ici on a 
           /*       enum io_state
                    {  badbit   = 1<<0, // -> 1 dans rdstate()
                       eofbit   = 1<<1, // -> 2 
                       failbit  = 1<<2, // -> 4 
                       goodbit  = 0     // -> O 
                     };*/
          tab_noeud(i) = (*tabMaillageNoeud)(nb); // lecture normale
     #ifdef ENLINUX
       else  if ((entreePrinc->entree)->fail())
               // on a atteind la fin de la ligne et on appelle un nouvel enregistrement 
          {   entreePrinc->NouvelleDonnee();  // lecture d'un nouvelle enregistrement
               *(entreePrinc->entree) >> nb;
              tab_noeud(i) = (*tabMaillageNoeud)(nb); // lecture normale
              }
     #else
 /*     #ifdef SYSTEM_MAC_OS_X_unix
       else  if ((entreePrinc->entree)->fail())
               // on a atteind la fin de la ligne et on appelle un nouvel enregistrement 
          {   entreePrinc->NouvelleDonnee();  // lecture d'un nouvelle enregistrement
               *(entreePrinc->entree) >> nb;
              tab_noeud(i) = (*tabMaillageNoeud)(nb); // lecture normale
              }
      #else */
       else  if ((entreePrinc->entree)->eof())
        // la lecture est bonne mais on a atteind la fin de la ligne
        {  tab_noeud(i) = (*tabMaillageNoeud)(nb); // lecture
           // si ce n'est pas la fin de la lecture on appelle un nouvel enregistrement 
           if (i != nbnte) entreePrinc->NouvelleDonnee();  // lecture d'un nouvelle enregistrement
         }
  //    #endif  
     #endif  
       else // cas d'une erreur de lecture
        { cout << "\n erreur de lecture inconnue  ";
          entreePrinc->MessageBuffer("** lecture des données particulières **");
          cout << "SfeMembT::LecDonPart"; 
          Affiche(2);
          Sortie (1);
        }
     };
    // construction du tableau de ddl des noeuds de SfeMembT
    ConstTabDdl(); 
	   // construction du tableau des noeuds du centre
	   t_N_centre.Change_taille(nombre->nbnce);
    for (int i=1;i<= nombre->nbnce;i++)
	    t_N_centre(i)=tab_noeud(i);
  };
  		
// calcul d'un point dans l'élément réel en fonction des coordonnées dans l'élément de référence associé
// temps: indique si l'on veut les coordonnées à t = 0, ou t ou tdt
// 1) cas où l'on utilise la place passée en argument
Coordonnee & SfeMembT::Point_physique(const Coordonnee& ,Coordonnee & co,Enum_dure )
 { cout << "\n methode non encore implantee !!"
        << "\n Coordonnee & SfeMembT::Point_physique(...";
   Sortie(1);         
   // d) retour
   return co;  
  };
  
// 3) cas où l'on veut les coordonnées aux 1, 2 ou trois temps selon la taille du tableau t_co
void SfeMembT::Point_physique(const Coordonnee& ,Tableau <Coordonnee> & )
 { cout << "\n methode non encore implantee !!"
        << "\n void SfeMembT::Point_physique(...";
   Sortie(1);         
  };
 
// vérification que la courbure ne soit pas anormale au temps enu
// inf_normale : indique en entrée le det mini pour la courbure en locale
// retour 1:  si tout est ok,
//        0:  une courbure trop grande a été détecté
// retour = -1 : il y a un pb inconnue dans le calcul
int SfeMembT::Test_courbure_anormale3(Enum_dure enu,double inf_normale)
 { SfeMembT::DonnComSfe* CoSfe = unefois->doCoMemb;         // pour simplifier l'écriture
   if (unefois->CalimpPrem == 0)
    { unefois->CalimpPrem = 1;
      Tableau<Enum_variable_metrique> tab(20);
      tab(1) = igiB_0; tab(2) = igiB_t; tab(3) = igiB_tdt; tab(4) = igijBB_0;
      tab(5) = igijBB_t;tab(6) = igijBB_tdt; tab(7) = igijHH_tdt; tab(8) = id_giB_tdt;
      tab(9) = id_gijBB_tdt ;tab(10) = igiH_tdt;tab(11) =  id_giH_tdt;
      tab(12) = id_gijHH_tdt;tab(13) = id_jacobien_tdt;tab(14) = id2_gijBB_tdt; 
      tab(15) = id_gijBB_t ;tab(16) = id_gijBB_tdt ;tab(17) = idMtdt ;
      tab(18) = igradVBB_tdt; tab(19) = iVtdt; tab(20) = idVtdt;
      CoSfe->met_SfeMembT.PlusInitVariables(tab) ;
    };
   // la variable de retour initialisée à 1 par défaut
   int retour = 1;
   // on gère les exceptions éventuelles en mettant le bloc sous surveillance
   try
     { // puis les indices de boucle
       int indice = 1;
       int nbtotsurf = CoSfe->eleCentre->Nbi();
       int nbtotepais =(CoSfe->segment).Nbi();
       for (int nisur =1; nisur<= nbtotsurf; nisur++)  // boucle sur les pt d'integ de surface
        {for (int niepais =1; niepais<= nbtotepais; niepais++, indice++) // pt d'integ d'epaisseur
          {((DeformationSfe1*) def)->ChangeNumIntegSfe1(nisur,niepais);
           ((DeformationSfe1*) def)->Mise_a_jour_data_specif(tabSaveDefDon(indice));
           retour *= ((DeformationSfe1*) def)->Test_courbure_anormale(enu,inf_normale);
           if (!retour)
             break; // on arrête dès qu'un pb est repéré
          }; // fin boucle sur les points d'intégration
         if (!retour)
          break; // on arrête dès qu'un pb est repéré
        };
     }
   catch (ErrSortieFinale)
        // cas d'une direction voulue vers la sortie
        // on relance l'interuption pour le niveau supérieur
      { ErrSortieFinale toto;
        throw (toto);
      }
   catch ( ... )
      { if (ParaGlob::NiveauImpression() > 3)
          {cout << "\n warning: erreur generee par un element lors du test de verification de la courbure ";
          };
        retour = -1;
      }
   return retour;
 };

// récupération des valeurs au numéro d'ordre = iteg pour les grandeurs enu
// ici il s'agit de grandeurs scalaire, 
// absolue: indique si oui ou non on sort les tenseurs dans la base absolue ou une base particulière
Tableau <double> SfeMembT::Valeur_a_diff_temps(bool absolue,Enum_dure enu_t,const List_io<Ddl_enum_etendu>& enu,int iteg)
    { // appel de la procedure de elem meca
	  int cas;       
      if (!(unefois->dualSortSfe ) && (unefois->CalimpPrem ))
        { cas=1;unefois->dualSortSfe += 1;
         } 
      else if ((unefois->dualSortSfe ) && (unefois->CalimpPrem ))       
         { cas = 11;}
      else if (!(unefois->dualSortSfe ) && (unefois->CalResPrem_tdt ))       
        { cas=2;unefois->dualSortSfe += 1;
         }         
      else if ((unefois->dualSortSfe ) && (unefois->CalResPrem_tdt ))       
         { cas = 12;}
      // sinon problème
      else
        { cout << "\n warning: les grandeurs ne sont pas calculees : il faudrait au moins un pas de calcul"
               << " pour inialiser les conteneurs des tenseurs  resultats ";
          if (ParaGlob::NiveauImpression() >= 4)     
            cout << "\n cas non prévu, unefois->dualSortSfe= " << unefois->dualSortSfe
               << " unefois->CalimpPrem= " << unefois->CalimpPrem
               << "\n SfeMembT::Valeur_a_diff_temps(Enum_dure enu_t...";
          Sortie(1);
         };       
      return ElemMeca::Valeur_multi(absolue,enu_t,enu,iteg,cas);
    };
    
// récupération des valeurs au numéro d'ordre = iteg pour les grandeurs enu
// ici il s'agit de grandeurs tensorielles, le retour s'effectue dans la liste
// de conteneurs quelconque associée
// absolue: indique si oui ou non on sort les tenseurs dans la base absolue ou une base particulière
void SfeMembT::ValTensorielle_a_diff_temps(bool absolue,Enum_dure enu_t,List_io<TypeQuelconque>& enu,int iteg)
    { // appel de la procedure de elem meca
	  int cas;       
      if (!(unefois->dualSortSfe ) && (unefois->CalimpPrem ))
        { cas=1;unefois->dualSortSfe += 1;
         } 
      else if ((unefois->dualSortSfe ) && (unefois->CalimpPrem ))       
         { cas = 11;}
      else if (!(unefois->dualSortSfe ) && (unefois->CalResPrem_tdt ))       
        { cas=2;unefois->dualSortSfe += 1;
         }         
      else if ((unefois->dualSortSfe ) && (unefois->CalResPrem_tdt ))       
         { cas = 12;}
      // sinon problème
      else
        { cout << "\n warning: les grandeurs ne sont pas calculees : il faudrait au moins un pas de calcul"
               << " pour inialiser les conteneurs des tenseurs  resultats ";
          if (ParaGlob::NiveauImpression() >= 4)     
            cout << "\n cas non prévu, unefois->dualSortSfe= " << unefois->dualSortSfe
               << " unefois->CalimpPrem= " << unefois->CalimpPrem
               << "\n SfeMembT::ValTensorielle_a_diff_temps(Enum_dure enu_t...";
          Sortie(1);
         };       
      ElemMeca::Valeurs_Tensorielles(absolue,enu_t,enu,iteg,cas);
    };
      
// Calcul du residu local à t ou tdt en fonction du booleen atdt
Vecteur* SfeMembT::CalculResidu (bool atdt,const ParaAlgoControle & pa)
  {   SfeMembT::DonnComSfe* CoSfe = unefois->doCoMemb;         // pour simplifier l'écriture
      Tableau <TenseurBB *>& d_epsBB = (CoSfe->d_epsBB);//             "
      energie_totale.Initialisation_differenciee(energie_totale_t); // init de l'énergie totale sur l'élément
      TenseurHH* sigHH_t_1 = (*lesPtIntegMecaInterne)(1).SigHH_t(); // simplification d'écriture
      if (ElemMeca::Bulk_visco_actif())  
          { if (sig_bulk_pourSfe_HH == NULL)  {sig_bulk_pourSfe_HH = NevezTenseurHH(*sigHH_t_1);}
            else if (sig_bulk_pourSfe_HH->Dimension() != sigHH_t_1->Dimension())
               {delete sig_bulk_pourSfe_HH; sig_bulk_pourSfe_HH = NevezTenseurHH(*sigHH_t_1);};           
           };               
      // dimensionnement de la metrique
      if (!atdt)
       {if( !(unefois->CalResPrem_t ))
        { unefois->CalResPrem_t += 1;
         Tableau<Enum_variable_metrique> tab(10);
         tab(1) = igiB_0; tab(2) = igiB_t; tab(3) = igijBB_0;tab(4) = igijBB_t;
         tab(5) = igijHH_t; tab(6) = id_giB_t; tab(7) = id_gijBB_t ;
         tab(8) = id_gijBB_t ;tab(9) = igradVBB_t; tab(10) = iVt;
         CoSfe->met_SfeMembT.PlusInitVariables(tab) ;
         };}  
      else 
       {if( !(unefois->CalResPrem_tdt ))
        {unefois->CalResPrem_tdt += 1;
         Tableau<Enum_variable_metrique> tab(11);
         tab(1) = igiB_0; tab(2) = igiB_tdt; tab(3) = igijBB_0;tab(4) = igijBB_tdt;
         tab(5) = igijHH_tdt; tab(6) = id_giB_tdt; tab(7) = id_gijBB_tdt ;
         tab(8) = id_gijBB_t ;tab(9) = id_gijBB_tdt ;
         tab(10) = igradVBB_tdt;tab(11) = iVtdt; 
         CoSfe->met_SfeMembT.PlusInitVariables(tab) ;
         };};  
      // dimensionnement du residu
      int nbddl = CoSfe->tab_ddl.NbDdl();
      if ( residu == NULL)
          residu = new Vecteur(nbddl); // cas du premier passage
      else
          for (int i =1; i<= nbddl; i++)  // cas des autres passages
             (*residu)(i) = 0.;             // on initialise a zero
      double jacobien; // def du jacobien
      int indice = 1;
      int nbtotsurf =CoSfe->eleCentre->Nbi();        
      int nbtotepais =(CoSfe->segment).Nbi();        
      // préparation du calcul de l'épaisseur finale (qui dépend de tous les points d'intégration)
      double somme_epaisseurs_finales = 0;        
      // on utilise une épaisseur intermédiaire pour la métrique
      // de manière à utiliser l'épaisseur de l'incrément précédent
      Epai  epais_pour_metrique;

      // on doit boucler à l'extérieur sur les pt de surface et à l'intérieur sur les pt d'épaisseurs
      // pour profiter du fait que une fois un pt calculer, on n'a pas à recalculer la courbure !!!!!
      for (int nisur =1; nisur<= nbtotsurf; nisur++)  // boucle sur les pt d'integ de surface
       for (int niepais =1; niepais<= nbtotepais; niepais++, indice++) // pt d'integ d'epaisseur


        { if ((donnee_specif.epais != NULL) // on change les épaisseurs si elles sont stockées
              && (!Loi_rien(loiComp->Id_comport()))
             )
            { epais_pour_metrique = *(donnee_specif.epais);
              epais_pour_metrique.epaisseur_tdt = epais_pour_metrique.epaisseur_t;
//              ((DeformationSfe1*) def)->Change_epaisseur(*donnee_specif.epais);  // au niveau de l'élément
              ((DeformationSfe1*) def)->Change_epaisseur(epais_pour_metrique);
            };
          ((DeformationSfe1*) def)->ChangeNumIntegSfe1(nisur,niepais);
          ((DeformationSfe1*) def)->Mise_a_jour_data_specif(tabSaveDefDon(indice));
          
          PtIntegMecaInterne & ptIntegMeca = (*lesPtIntegMecaInterne)(indice);
          TenseurHH & sigHH = *(ptIntegMeca.SigHH());
          TenseurBB & DepsBB_ = *(ptIntegMeca.DepsBB());
          EnergieMeca& energ =  tab_energ(indice);
          EnergieMeca& energ_t =  tab_energ_t(indice);
          double poids = CoSfe->eleCentre->Wi(nisur) * (CoSfe->segment).Wi(niepais) ;
          CompThermoPhysiqueAbstraite::SaveResul* sTP=NULL;  // les données spécifique thermo physiques
          if (loiTP != NULL) {sTP = tabSaveTP(indice);};         // au pt d'integ si elles existes 
          if ((loiComp->Id_comport()==LOI_VIA_UMAT)   // cas de l'utilisation d'une loi umat
              || (loiComp->Id_comport()==LOI_VIA_UMAT_CP))
            ((Loi_Umat*) loiComp)->Mise_a_jour_nbe_nbptinteg(this->Num_elt(),indice);
          double epaisseur_tdt = 0.; // ne sert que pour les éléments 2D avec épaisseurs stockées à l'élément
          double epaisseur_pris_en_compte = 0.; // celle qui sera réellement prise en compte

          // -- on gère les exceptions éventuelles en mettant le bloc sous surveillance
          try // ce qui permet de donner les numéros d'éléments et de pti
           {if (atdt)
               { const Met_abstraite::Expli_t_tdt& ex = loiComp->Cal_explicit_tdt 
                               (tabSaveDon(indice), *def,CoSfe->tab_ddl,ptIntegMeca,d_epsBB,jacobien
                                 ,sTP,loiTP,dilatation,energ,energ_t,premier_calcul_meca_impli_expli);
                 // examen du cas où on désire utiliser la méthode d'atténuation des hautes fréquences avec le bulk viscosity
                 if (Bulk_visco_actif()) 
                    ModifContrainteBulk(TEMPS_tdt,*ex.gijHH_tdt,sigHH,DepsBB_ ,(*sig_bulk_pourSfe_HH));
                 // on tient compte de la variation d'épaisseur si l'épaisseur est stockée dans l'élément
                 // et que l'on n'a pas une loi rien
                 if ((donnee_specif.epais != NULL) && (!Loi_rien(loiComp->Id_comport())))
                  { switch (loiComp->Comportement_3D_CP_DP_1D())
                     { case COMP_CONTRAINTES_PLANES :
                        // en contrainte plane on recalcule l'épaisseur
                        { CalEpaisseurAtdtExp_et_vol_pti(donnee_specif.epais->epaisseur0,pa,donnee_specif.epais->epaisseur_t
                                                     ,ptIntegMeca,epaisseur_tdt,ex);
//------ le 7 nov 2016: changement car je ne veux pas utiliser la variation d'épaisseur
// qui est calculée dans les lois. C'est sans doute possible, bien qu'avec les lois additives et mélange
// ce n'est pas sûr que cela veuille dire quelque chose. Donc on essaie de sans passer
// De plus c'est cohérent avec les éléments membranes ...

// ---- remodif le 1 avril 2017 car la méthode précédente fait que l'élément converge très mal !!
// je ne sais pas trop pourquoi (avec une loi de déformation plane, cela converge immédiatement !)
//                   // en fait, on récupère la variation d'épaisseur calculée via la condition de contrainte plane
//                   // on utilise uniquement la variation d'épaisseur calculée à la surface médiane ??? pour l'instant
//                   epaisseur_tdt = loiComp->HsurH0(tabSaveDon(nisur)) * donnee_specif.epais->epaisseur0;

// il faudrait peut-être utiliser finalement la variation d'épaisseur calculée par la loi, comme pour les éléments
// membranes qui ont été modifiées pour tenir compte de la différence de def entre géométrique et la def méca
// Par exemple lors de critère de rupture, on aura le même pb qu'avec les plis
//*** à faire
                          epaisseur_pris_en_compte = donnee_specif.epais->epaisseur_tdt;
                          break;
                        }
                      case COMP_DEFORMATIONS_PLANES :
                        // en deformation plane, l'épaisseur ne change pas
                        {  epaisseur_tdt = donnee_specif.epais->epaisseur0;
                           ptIntegMeca.Volume_pti() *= donnee_specif.epais->epaisseur0;
                           epaisseur_pris_en_compte = donnee_specif.epais->epaisseur0;
                           break;
                        }
                      default :
                      cout << "\nErreur : cas de loi de comportement : " << Nom_comp((loiComp->Id_comport())) 
                         << " non prevu pour le calcul le l'epaisseur avec l'element SFE ";
                        cout << "\n SfeMembT::Calcul_implicit(.... \n";
                        Sortie(1);
                     };
                  };
                 somme_epaisseurs_finales += epaisseur_tdt; //pour le calcul final
                }               
            else   
               { const Met_abstraite::Expli& ex = loiComp->Cal_explicit_t
                              (tabSaveDon(indice), *def,CoSfe->tab_ddl,ptIntegMeca,d_epsBB,jacobien
                               ,sTP,loiTP,dilatation,energ,energ_t,premier_calcul_meca_impli_expli);
                 // examen du cas où on désire utiliser la méthode d'atténuation des hautes fréquences avec le bulk viscosity
                 if (Bulk_visco_actif())
                   ModifContrainteBulk(TEMPS_t,*ex.gijHH_t,sigHH,DepsBB_ ,(*sig_bulk_pourSfe_HH));
                 // pour l'instant on expédie un message, car il y a des chances que cette partie disparaisse !!
                 cout << "\n attention *** calcul non operationnelle en explicite t, on ne tient pas compte de la"
                     << " variation d'epaisseur !! (a modifier ) "; 
               };
           }
          catch (ErrNonConvergence_loiDeComportement excep)
           // cas d'une d'une erreur survenue à cause d'une non convergence pour la résolution
           // d'une loi de comportement incrémentale
            { cout << "\n erreur de loi de comportement element= " << this->Num_elt()
                   << " point d'integration de surface= "<<nisur << " d'epaisseur= "<< niepais << endl;
              throw excep;
            }
          catch (ErrSortieFinale)
               // cas d'une direction voulue vers la sortie
               // on relance l'interuption pour le niveau supérieur
             { ErrSortieFinale toto;
               throw (toto);
             }
          catch ( ... )
            { cout << "\n erreur inconnue de loi de comportement, element= " << this->Num_elt()
                   << " point d'integration de surface= "<<nisur << " d'epaisseur= "<< niepais << endl;
              throw (Err_inconnue_ElemMeca());
            };

          if ((jacobien <= 0.)|| (std::isinf(jacobien))||( std::isnan(jacobien))) // vérif du jacobien
            { if ((std::isinf(jacobien))||( std::isnan(jacobien)))
                // on met un message quelque soit le niveau d'impression
                { cout << "\n **********  attention on a trouve un jacobien infini ou nan !! = ("<<jacobien<<")********* " << endl; };
              if (ParaGlob::NiveauImpression() >= 1)
               { cout << "\n **********  attention on a trouve un jacobien  negatif!! ("<<jacobien<<")********* " << endl; };
            };
          if ((jacobien <= 0.) && (pa.JabobienNegatif() != 0)) // vérif du jacobien  et traitement adéquate si besoin
              { switch (pa.JabobienNegatif())
                { case 1:
                  { cout << "\n on annulle sa contribution. Element nb: " <<  Num_elt() << " ni_surf: " << nisur
                         << " ni_ep: " << niepais;
                    break;
                   }
                // dans les autres cas on ne fait rien   
                };       
              }
          else
           { // on prend en compte toutes les  variations
            // dans le cas d'un élément 3D, épaisseur est stockée aux noeuds, donc ici la variable
            // épaisseur_tdt ne devrait pas exister, en particulier le jacobien intègre déjà l'épaisseur
            // pour éviter des tests et une recopie du code on laisse epaisseur_tdt, avec une valeur 1
            // ce qui fait qu'elle n'intervient pas 
            if (donnee_specif.epais == NULL) epaisseur_pris_en_compte = 1.;
            // il ne faut pas interpoler l'énergie à t, car elle était associée à un volume qui a changé
            // donc il ne faut considérer que l'accroissement d'énergie
            energie_totale.Ajout_differenciee(energ,energ_t,(poids * jacobien * epaisseur_pris_en_compte));
//            energie_totale += (energ-energ_t) * (poids * jacobien * epaisseur_tdt);
//            energie_totale += energ * (poids * jacobien * epaisseur_tdt);

            //test si le jacobien due aux gi finaux est très différent du jacobien de la facette
            //test si le jacobien due aux gi finaux est très différent du jacobien de la facette
            Delta_Jacobien_anormal(tabSaveDefDon(indice),nisur,niepais);
            
            for (int j =1; j<= nbddl; j++)
				          {int jloc =  CoSfe->nMetVTab_ddl(j); // adressage indirecte
                 (*residu)(jloc) -=  (sigHH && (*(d_epsBB(j)))) * jacobien * poids * epaisseur_pris_en_compte;
                 volume += jacobien * poids  * epaisseur_tdt; // là on prend la vrai épaisseur
				          };
           };
          if (ElemMeca::Bulk_visco_actif()) // dans le cas du bulk on retire la contrainte du bulk, qui est non physique
           { sigHH -= (*sig_bulk_pourSfe_HH); }  // ceci pour la sauvegarde  ou autre utilisation       
 
        }; // fin boucle sur les points d'intégration
      // on calcul éventuellement l'épaisseur moyenne finale si c'est un élément 2D
      if ((donnee_specif.epais != NULL)&&(!Loi_rien(loiComp->Id_comport()))) // cas 2D
         donnee_specif.epais->epaisseur_tdt = somme_epaisseurs_finales / (indice - 1);  
      // liberation des tenseurs intermediaires
      LibereTenseur();   
      return residu;  
     };

// Calcul du residu local et de la raideur locale,
//  pour le schema implicite
Element::ResRaid  SfeMembT::Calcul_implicit (const ParaAlgoControle & pa)
 { SfeMembT::DonnComSfe* CoSfe = unefois->doCoMemb;         // pour simplifier l'écriture
   Tableau <TenseurBB *>& d_epsBB = (CoSfe->d_epsBB);//             "
   Tableau <TenseurHH *>& d_sigHH = (CoSfe->d_sigHH);//             "
   energie_totale.Initialisation_differenciee(energie_totale_t); // init de l'énergie totale sur l'élément
   E_elem_bulk_tdt = E_elem_bulk_t; P_elem_bulk = 0.; // init pour l'énergie et la puissance associées au bulk
   // init éventuel de la contrainte de bulk viscosity
   TenseurHH* sigHH_t_1 = (*lesPtIntegMecaInterne)(1).SigHH_t(); // simplification d'écriture
   if (ElemMeca::Bulk_visco_actif())  
    { if (sig_bulk_pourSfe_HH == NULL)  {sig_bulk_pourSfe_HH = NevezTenseurHH(*sigHH_t_1);}
      else if (sig_bulk_pourSfe_HH->Dimension() != sigHH_t_1->Dimension())
               {delete sig_bulk_pourSfe_HH; sig_bulk_pourSfe_HH = NevezTenseurHH(*sigHH_t_1);};           
    };
    
   // init éventuel des intégrales de volume et temps
   Init_Integ_vol_et_temps();
   // --- init pour l'init de la stabilisation de membrane-biel éventuelle
   const Met_abstraite::Impli* ex_final=NULL; // contiendra après les boucles la dernière métrique
   Mise_a_jour_A_calculer_force_stab(); // permettra ensuite de savoir où le calcul doit être fait

   bool cald_Dvirtuelle = false;
   if (unefois->CalimpPrem == 0)
    { unefois->CalimpPrem = 1;
      Tableau<Enum_variable_metrique> tab(20);
      tab(1) = igiB_0; tab(2) = igiB_t; tab(3) = igiB_tdt; tab(4) = igijBB_0;
      tab(5) = igijBB_t;tab(6) = igijBB_tdt; tab(7) = igijHH_tdt; tab(8) = id_giB_tdt;
      tab(9) = id_gijBB_tdt ;tab(10) = igiH_tdt;tab(11) =  id_giH_tdt;
      tab(12) = id_gijHH_tdt;tab(13) = id_jacobien_tdt;tab(14) = id2_gijBB_tdt; 
      tab(15) = id_gijBB_t ;tab(16) = id_gijBB_tdt ;tab(17) = idMtdt ;
      tab(18) = igradVBB_tdt; tab(19) = iVtdt; tab(20) = idVtdt;
      CoSfe->met_SfeMembT.PlusInitVariables(tab) ;
      // on ne calcul la dérivée de la déformation virtuelle qu'une fois
      // car elle est constante dans le temps et indépendante des coordonnées
      cald_Dvirtuelle=true; 
    };
   // dimensionnement du residu
   int nbddl = CoSfe->tab_ddl.NbDdl();
   if ( residu == NULL)
       residu = new Vecteur(nbddl); // cas du premier passage
   else
       for (int i =1; i<= nbddl; i++)  // cas des autres passages
          (*residu)(i) = 0.;             // on initialise a zero
   // dimensionnement de la raideur
   if ( raideur == NULL)
       raideur = new Mat_pleine(nbddl,nbddl); // cas du premier passage
   else
       for (int i =1; i<= nbddl; i++)  // cas des autres passages
         for (int j=1; j<= nbddl; j++) //
           (*raideur)(i,j) = 0.;       // on initialise a zero
   double jacobien; // def du jacobien
   Vecteur d_jacobien_tdt; 
   int indice = 1;
   int nbtotsurf = CoSfe->eleCentre->Nbi();        
   int nbtotepais =(CoSfe->segment).Nbi(); 
   // préparation du calcul de l'épaisseur finale (qui dépend de tous les points d'intégration)
   double somme_epaisseurs_finales = 0;
   // on utilise une épaisseur intermédiaire pour la métrique
   // de manière à utiliser l'épaisseur de l'incrément précédent
   Epai  epais_pour_metrique;
//--debug
//if (num_elt ==  89)
// { cout << "\n SfeMembT::Calcul_implicit debug ";
//   cout << endl;
// };

   // on doit boucler à l'extérieur sur les pt de surface et à l'intérieur sur les pt d'épaisseurs
   // pour profiter du fait que une fois un pt calculer, on n'a pas à recalculer la courbure !!!!!
// ne sert plus**   int nbint = nbtotsurf*nbtotepais; // nb total de pti
   for (int nisur =1; nisur<= nbtotsurf; nisur++)  // boucle sur les pt d'integ de surface
    for (int niepais =1; niepais<= nbtotepais; niepais++, indice++) // pt d'integ d'epaisseur
     {if ((donnee_specif.epais != NULL) // on change les épaisseurs si elles sont stockées
          && (!Loi_rien(loiComp->Id_comport()))
          )
            { epais_pour_metrique = *(donnee_specif.epais);
              epais_pour_metrique.epaisseur_tdt = epais_pour_metrique.epaisseur_t;
//              ((DeformationSfe1*) def)->Change_epaisseur(*donnee_specif.epais);  // au niveau de l'élément
              ((DeformationSfe1*) def)->Change_epaisseur(epais_pour_metrique);
            };
      ((DeformationSfe1*) def)->ChangeNumIntegSfe1(nisur,niepais);
      ((DeformationSfe1*) def)->Mise_a_jour_data_specif(tabSaveDefDon(indice));
      
      
      PtIntegMecaInterne & ptIntegMeca = (*lesPtIntegMecaInterne)(indice);
      TenseurHH & sigHH = *(ptIntegMeca.SigHH());
      TenseurBB & DepsBB_tdt = *(ptIntegMeca.DepsBB());
      EnergieMeca& energ =  tab_energ(indice);
      EnergieMeca& energ_t =  tab_energ_t(indice);
      double poids = (CoSfe->eleCentre->Wi(nisur) * (CoSfe->segment).Wi(niepais)) * 0.5 ;
      CompThermoPhysiqueAbstraite::SaveResul* sTP=NULL;  // les données spécifique thermo physiques
      if (loiTP != NULL) {sTP = tabSaveTP(indice);};         // au pt d'integ si elles existes 
      if ((loiComp->Id_comport()==LOI_VIA_UMAT)   // cas de l'utilisation d'une loi umat
         || (loiComp->Id_comport()==LOI_VIA_UMAT_CP))
         ((Loi_Umat*) loiComp)->Mise_a_jour_nbe_nbptinteg(this->Num_elt(),indice);

      // -- on gère les exceptions éventuelles en mettant le bloc sous surveillance
      const Met_abstraite::Impli* ex_pointe = NULL; // c'est pour construire ex
      try // ce qui permet de donner les numéros d'éléments et de pti
       { ex_pointe = &loiComp->Cal_implicit(tabSaveDon(indice), *def,CoSfe->tab_ddl
                                               ,ptIntegMeca,d_epsBB,jacobien,d_jacobien_tdt
                                               ,d_sigHH,pa,sTP,loiTP,dilatation,energ,energ_t
                                               ,premier_calcul_meca_impli_expli);
       }
      catch (ErrNonConvergence_loiDeComportement excep)
       // cas d'une d'une erreur survenue à cause d'une non convergence pour la résolution
       // d'une loi de comportement incrémentale
        { cout << "\n erreur de loi de comportement element= " << this->Num_elt()
                 << " point d'integration de surface= "<<nisur << " d'epaisseur= "<< niepais << endl;
          throw excep;
        }
      catch (ErrSortieFinale)
           // cas d'une direction voulue vers la sortie
           // on relance l'interuption pour le niveau supérieur
         { ErrSortieFinale toto;
           throw (toto);
         }
      catch ( ... )
        { cout << "\n erreur inconnue de loi de comportement, element= " << this->Num_elt()
                 << " point d'integration de surface= "<<nisur << " d'epaisseur= "<< niepais << endl;
          throw (Err_inconnue_ElemMeca());
        };
      const Met_abstraite::Impli& ex=(*ex_pointe);
      ex_final = ex_pointe; // stockage pour la stabilisation éventuelle de membraneBiel

      // on calcul éventuellement la dérivée de la vitesse de déformation virtuelle
      Tableau2 <TenseurBB *>& d2_epsBB =  (CoSfe->d2_epsBB)(indice); // pour simplifier
      
      if (cald_Dvirtuelle)  
           def->Cal_var_def_virtuelle(false,d2_epsBB);
      // examen du cas où on désire utiliser la méthode d'atténuation des hautes fréquences avec le bulk viscosity
      if (Bulk_visco_actif()) 
           ModifContrainteBulk(TEMPS_tdt,*ex.gijHH_tdt,sigHH,DepsBB_tdt,(*sig_bulk_pourSfe_HH) );
      double rap=1.; // pour le calcul éventuel du rapport de jacobien actuel
      if (pa.MaxiVarJacobien() > 1.) // cas de la demande du calcul du rapport de jacobien 
         {if (Dabs(jacobien) > (*(ex.jacobien_0))) {rap = Dabs(jacobien) / (*(ex.jacobien_0));}
          else                                     {rap = (*(ex.jacobien_0)) / Dabs(jacobien) ;}
          };  
      // on tient compte de la variation d'épaisseur pour ce point d'integ
      double epaisseur_tdt = 0.; // ne sert que pour les éléments 2D avec épaisseurs stockées à l'élément
      double epaisseur_pris_en_compte = 0.; // celle qui sera réellement prise en compte
      if ((donnee_specif.epais != NULL) && (!Loi_rien(loiComp->Id_comport())) )
 		     { switch (loiComp->Comportement_3D_CP_DP_1D()) 
            { case COMP_CONTRAINTES_PLANES :
					            {
                   // en contrainte plane on recalcule l'épaisseur
                   CalEpaisseurAtdtImp_et_vol_pti(donnee_specif.epais->epaisseur0
                                     ,pa,donnee_specif.epais->epaisseur_t
                                     ,ptIntegMeca,epaisseur_tdt,ex);
//------ le 7 nov 2016: changement car je ne veux pas utiliser la variation d'épaisseur
// qui est calculée dans les lois. C'est sans doute possible, bien qu'avec les lois additives et mélange
// ce n'est pas sûr que cela veuille dire quelque chose. Donc on essaie de sans passer
// De plus c'est cohérent avec les éléments membranes ...

// ---- remodif le 1 avril 2017 car la méthode précédente fait que l'élément converge très mal !!
// je ne sais pas trop pourquoi (avec une loi de déformation plane, cela converge immédiatement !)
//                   // en fait, on récupère la variation d'épaisseur calculée via la condition de contrainte plane
//                   // on utilise uniquement la variation d'épaisseur calculée à la surface médiane ??? pour l'instant
//                   epaisseur_tdt = loiComp->HsurH0(tabSaveDon(nisur)) * donnee_specif.epais->epaisseur0;
// ************$la ligne qui suit est uniquement pour faire passer qq calcul ---> mais c'est faux !!!
// ************$la ligne qui suit est uniquement pour faire passer qq calcul ---> mais c'est faux !!!
// ************$la ligne qui suit est uniquement pour faire passer qq calcul ---> mais c'est faux !!!
                   
//     epaisseur_tdt = donnee_specif.epais->epaisseur0;
 // on utilise l'épaisseur de l'incrément précédent
                   epaisseur_pris_en_compte = donnee_specif.epais->epaisseur_tdt;
					              break;
                 }
              case COMP_DEFORMATIONS_PLANES :
                 // en deformation plane, l'épaisseur ne change pas
                 {  epaisseur_tdt = donnee_specif.epais->epaisseur0;
                    ptIntegMeca.Volume_pti() *= donnee_specif.epais->epaisseur0;
                    epaisseur_pris_en_compte = epaisseur_tdt;
                    break;
                 }
              default :
                 cout << "\nErreur : cas de loi de comportement : " << Nom_comp((loiComp->Id_comport())) 
                    << " non prevu pour le calcul le l'epaisseur avec l'element SFE ";
                 cout << "\n SfeMembT::Calcul_implicit(.... \n";
                 Sortie(1);
            };
        };
		    somme_epaisseurs_finales += epaisseur_tdt; //pour le calcul final

//--------- pour le débug --------
//      Deformation & defS = *defSurf(1); // pour simplifier l'écriture
//      int nbddls = ddlS.NbDdl();


//------- pour le débug fin -------


      if ((jacobien <= 0.)|| (std::isinf(jacobien))||( std::isnan(jacobien))) // vérif du jacobien
          { if ((std::isinf(jacobien))||( std::isnan(jacobien)))
                 // on met un message quelque soit le niveau d'impression
                 { cout << "\n **********  attention on a trouve un jacobien infini ou nan !! = ("<<jacobien<<")********* " << endl; };
               if (ParaGlob::NiveauImpression() >= 1)
                 { cout << "\n **********  attention on a trouve un jacobien  negatif!! ("<<jacobien<<")********* " << endl; };
          };
      if ((jacobien <= 0.) && (pa.JabobienNegatif()!= 0)) // vérif du jacobien  et traitement adéquate si besoin
          { cout << "\n ParaGlob::NiveauImpression() = "<<ParaGlob::NiveauImpression() << " jacobien= " << jacobien << endl;
               switch (pa.JabobienNegatif())
               { case 1:   
                   { cout << "\n on annulle sa contribution. Element nb: " <<  Num_elt()  << " ni_surf: " << nisur
                          << " ni_ep: " << niepais;
                     break;
                    }
                 case 2:
                   { // on génère une exception
                     throw ErrJacobienNegatif_ElemMeca();break;
                    }
                 // dans les autres cas on ne fait rien   
               };       
          }
      else if ((pa.MaxiVarJacobien() > 1.) && ( rap > pa.MaxiVarJacobien()))
          { if (ParaGlob::NiveauImpression() >= 3)
                { cout << "\n ***  attention la variation maximale du jacobien est atteinte  !! *** " 
                       << "\n ele= "<<  Num_elt() << " ni_surf: " << nisur
                       << " ni_ep: " << niepais << " jacobien= " << jacobien << " jacobien_0= " 
                       << (*(ex.jacobien_0)) << endl;
                };
               // on génère une exception
               throw ErrVarJacobienMini_ElemMeca();break;
          }
      else 
       { // on prend en compte toutes les  variations
       
         // dans le cas d'un élément 3D, épaisseur est stockée aux noeuds, donc ici la variable
         // épaisseur_tdt ne devrait pas exister, en particulier le jacobien intègre déjà l'épaisseur
         // pour éviter des tests et une recopie du code on laisse epaisseur_tdt, avec une valeur 1
         // ce qui fait qu'elle n'intervient pas
         if (donnee_specif.epais == NULL) epaisseur_pris_en_compte = 1.;
         
         // on continue que s'il s'agit d'une loi différente de rien
         if (!Loi_rien(loiComp->Id_comport()))
           {// il ne faut pas interpoler l'énergie à t, car elle était associée à un volume qui a changé
            // donc il ne faut considérer que l'accroissement d'énergie
            energie_totale.Ajout_differenciee(energ,energ_t,(poids * jacobien * epaisseur_pris_en_compte));
//         energie_totale += (energ-energ_t) * (poids * jacobien * epaisseur_tdt);
//         energie_totale += energ * (poids * jacobien * epaisseur_tdt);
         // l'ordre des ddl vient de la métrique: tous le xi, puis les épaisseurs, par contre dans le stockage
         // local on doit avoir noeud1: ddlxi puis epais, noeud2: idem etc pour les noeuds centraux, puis ddl xi
         // pour les noeuds externes. Si pas de ddl epais, tous les noeuds sont traités de la même manière
         // on utilise un adressage indirecte
////--- debug
//if (Dabs(epaisseur_pris_en_compte - donnee_specif.epais->epaisseur0) > 0.01)
// {cout << "\n debug SfeMembT::Calcul_implicit: epai= "
//       << epaisseur_pris_en_compte << " epai_0= "<< donnee_specif.epais->epaisseur0 << endl;
// };
//// --- fin debug

            for (int j =1; j<= nbddl; j++)  // 1ere boucle sur les ddl
              { int jloc =  CoSfe->nMetVTab_ddl(j); // adressage indirecte           
               (*residu)(jloc) -= ((*(d_epsBB(j))) && sigHH ) * poids * jacobien* epaisseur_pris_en_compte;
/*//--debug
if ((*residu)(jloc) > 10.)
 { cout << "\n SfeMembT::Calcul_implicit debug ";
   cout << "\n (*ex.giB_0)(1): " ;(*ex.giB_0)(1).Affiche();
   cout << " (*ex.giB_0)(2): " ;(*ex.giB_0)(2).Affiche();
   cout << " (*ex.giH_0)(1): " ;(*ex.giH_0)(1).Affiche();
   cout << " (*ex.giH_0)(2): " ;(*ex.giH_0)(2).Affiche();
   cout << "\n (*ex.giBB_0): " ;(*ex.gijBB_0).Ecriture(cout);
   cout << "\n (*ex.giHH_0): " ;(*ex.gijHH_0).Ecriture(cout);
  
   cout << "\n (*(d_epsBB(j))): " ;(*(d_epsBB(j))).Ecriture(cout);
   cout << "\n sigHH: " ;sigHH.Ecriture(cout);
   cout << endl;

   ex_pointe = &loiComp->Cal_implicit(tabSaveDon(indice), *def,CoSfe->tab_ddl
   ,ptIntegMeca,d_epsBB,jacobien,d_jacobien_tdt
   ,d_sigHH,pa,sTP,loiTP,dilatation,energ,energ_t
   ,premier_calcul_meca_impli_expli);
  
   Sortie(1);
 };
//--fin debug */
               //test si le jacobien due aux gi finaux est très différent du jacobien de la facette
               //test si le jacobien due aux gi finaux est très différent du jacobien de la facette
               Delta_Jacobien_anormal(tabSaveDefDon(indice),nisur,niepais);

               volume += jacobien * poids * epaisseur_tdt; // là on prend la vraie épaisseur
               for (int i =1; i<= nbddl; i++)  // 2ere boucle sur les ddl
                  { int iloc =  CoSfe->nMetVTab_ddl(i); // adressage indirecte
                    (*raideur)(jloc,iloc) +=  ( ((*d_sigHH(i)) && (*(d_epsBB(j)))) * jacobien 
                                       +  (sigHH && (*((CoSfe->d2_epsBB)(indice)(j,i)))) * jacobien
                                       +  (sigHH && (*(d_epsBB(j)))) * d_jacobien_tdt(i)
                                       ) * poids * epaisseur_pris_en_compte;

// on utilise toujour la variation de D, donc c'est maintenant directement intégré dans l'expression précédente, et la suite est commentée
//                   // on intègre la variation seconde si c'est demandé
//                   if (pa.Var_D())
//                      (*raideur)(jloc,iloc) +=  (sigHH && (*((CoSfe->d2_epsBB)(indice)(j,i)))) 
//                                            * jacobien * poids * epaisseur_tdt;                    
// ---- pour test débug
//                    if (i < 10) 
//                       (*raideur)(j,i) +=  (sigHH && (*(d_epsBB(j)))) * d_jacobien_tdt(i) * poids * donnee_specif.epaisseur_tdt;                 
                  };
              };
            };
           // --- cas éventuel d'une stabilisation membrane-biel ---
           // ici il s'agit de la contribution  précise à chaque pti
           if (pt_StabMembBiel != NULL)
             if (!(pt_StabMembBiel->Aa_calculer()))
               { double poids_vol = poids * epaisseur_pris_en_compte;
                 Cal_implicit_StabMembBiel
                    (nisur,*ex_final,nbtotsurf,poids_vol,unefois->doCoMemb->noeud_a_prendre_en_compte);
               };
          };// fin du if sur la valeur non négative du jacobien
////--debug
//for (int i = 1; i<= 18; i++)
// { if ((*residu)(i) != 0. )
//     { cout << "\n SfeMembT::Calcul_implicit debug ";
//       cout << endl;
//     };
////if (num_elt ==  176)
//// { cout << "\n SfeMembT::Calcul_implicit debug ";
////   cout << endl;
// };
////--fin debug
      if (ElemMeca::Bulk_visco_actif()) // dans le cas du bulk on retire la contrainte du bulk, qui est non physique
        { sigHH -= (*sig_bulk_pourSfe_HH); }  // ceci pour la sauvegarde  ou autre utilisation
 
     }; // fin boucle sur les points d'intégration
  #ifdef MISE_AU_POINT
  if (ParaGlob::NiveauImpression() > 9)
   { cout << "\n Raideur et second membre locaux: ? (o/n) "; string rep(" ");
     // procédure de lecture avec prise en charge d'un retour chariot
     rep = lect_return_defaut(false,"n");

     if ((rep == "0")||(rep == "o"))
       {cout << "\n Raideur et second membre locaux: elem= " << Num_elt_const()
             << ", maillage= " << Num_maillage();
        cout << "\n raideur: ";
        raideur->Affiche();
        cout << "\n second membre: " << (*residu);
       };
   };
  #endif
  // --- intervention dans le cas d'une stabilisation d'hourglass
  // stabilisation pour un calcul implicit
  if (type_stabHourglass)
      Cal_implicit_hourglass();
  // --- cas éventuel d'une stabilisation membrane-biel ---
  // ici il s'agit soit du calcul approché d'initialisation, soit de la fin du calcul après la boucle
  // modif: 10 janvier 2019 non c'est le calcul correct une fois la raideur calculée
  if (pt_StabMembBiel != NULL)
   {if (pt_StabMembBiel->Aa_calculer())
          {Cal_implicit_StabMembBiel
             (0,*ex_final, nbtotsurf,volume,unefois->doCoMemb->noeud_a_prendre_en_compte);}
    else  {Cal_implicit_StabMembBiel
             (-1,*ex_final, nbtotsurf,volume,unefois->doCoMemb->noeud_a_prendre_en_compte);} ;
   };
  #ifdef MISE_AU_POINT
  if (ParaGlob::NiveauImpression() > 9)
   { if ((type_stabHourglass)||(pt_StabMembBiel != NULL))
      {cout << "\n apres stabilisation: Raideur et second membre locaux: ? (o/n) "; string rep(" ");
       // procédure de lecture avec prise en charge d'un retour chariot
       rep = lect_return_defaut(false,"n");

       if ((rep == "0")||(rep == "o"))
         {cout << "\n Raideur et second membre locaux: elem= " << Num_elt_const()
               << ", maillage= " << Num_maillage();
          cout << "\n raideur: ";
          raideur->Affiche();
          cout << "\n second membre: " << (*residu);
         };
     };
   };
  #endif

////--debug
//for (int i = 1; i<= 18; i++)
// { if ((*residu)(i) != 0. )
//     { cout << "\n SfeMembT::Calcul_implicit debug ";
//       cout << endl;
//     };
////if (num_elt ==  176)
//// { cout << "\n SfeMembT::Calcul_implicit debug ";
////   cout << endl;
// };
////--fin debug


   // on calcul éventuellement l'épaisseur moyenne finale si c'est un élément 2D
   if ((donnee_specif.epais != NULL) && (!Loi_rien(loiComp->Id_comport())) )// cas 2D
      donnee_specif.epais->epaisseur_tdt = somme_epaisseurs_finales / (indice - 1);  
   // liberation des tenseurs intermediaires
   LibereTenseur();   
   Element::ResRaid el;
   el.res = residu; 
   el.raid = raideur;       
   return el;
  };
		
// Calcul de la matrice masse pour l'élément
// celle-ci est calculée uniquement pour l'élément centrale et en considérant
// que la masse volumique est constante dans l'épaisseur
Mat_pleine * SfeMembT::CalculMatriceMasse (Enum_calcul_masse type_calcul_masse) 
 {    SfeMembT::DonnComSfe* CoSfe = unefois->doCoMemb; // pour simplifier l'écriture
      // dimensionement de la métrique si nécessaire
      if (!(unefois->CalDynamique ))
       { unefois->CalDynamique += 1;
         Tableau<Enum_variable_metrique> tab(5);
         tab(1) = igiB_0;  tab(2) = igiB_tdt; tab(3) = igijBB_0;
         tab(4) = igijBB_tdt; tab(5) = igradVmoyBB_t; 
         CoSfe->met_SfeMembT.PlusInitVariables(tab) ;
         // on vérifie le bon dimensionnement de la matrice
         if (type_calcul_masse == MASSE_CONSISTANTE)
           // dans le cas où la masse est consistante il faut la redimensionner
          { int nbddl = CoSfe->tab_ddl.NbDdl();
            (CoSfe->matrice_masse).Initialise (nbddl,nbddl,0.);
           } 
        };
      if ((donnee_specif.epais != NULL) // on change les épaisseurs si elles sont stockées
          && (!Loi_rien(loiComp->Id_comport()))
          )
              ((DeformationSfe1*) def)->Change_epaisseur(*donnee_specif.epais);  // au niveau de l'élément
       // appel de la routine générale 
////************ non, ce n'est pas correcte pour les éléments 3D, il faut revoir le calcul de la masse
//// en utilisant d(OM)/d_ddl au lieu de phi^r       
       ElemMeca::Cal_Mat_masse (CoSfe->tab_ddl,type_calcul_masse,
                            nombre->nbisMas,CoSfe->eleMas->TaPhi(),nombre->nbnce
                            ,CoSfe->eleMas->TaWi());
       if ((donnee_specif.epais != NULL) // prise en compte de l'épaisseur finale, si elle est stockée
           && (!Loi_rien(loiComp->Id_comport()))
          )
           (*mat_masse) *= donnee_specif.epais->epaisseur_tdt; // au niveau de l'élément, sinon on est en 3D, donc c'est
                                                        // intégré dans le jacobien                    
       return mat_masse;                     
  };
		
//============= lecture écriture dans base info ==========
	
// cas donne le niveau de la récupération
// = 1 : on récupère tout
// = 2 : on récupère uniquement les données variables (supposées comme telles)
void SfeMembT::Lecture_base_info
    (ifstream& ent,const Tableau<Noeud  *> * tabMaillageNoeud,const int cas) 
{// tout d'abord appel de la lecture de la classe elem_meca
 ElemMeca::Lecture_bas_inf(ent,tabMaillageNoeud,cas);
 // traitement du cas particulier de l'élément centrale
 string nom;
 switch (cas)
  { case 1 : // ------- on récupère tout -------------------------
     { // construction du tableau de ddl des noeuds de l'élément centrale
       ConstTabDdl(); 
       // récup contraintes et déformation
       lesPtMecaInt.Lecture_base_info(ent,cas);
       
       // les données spécifiques
       ent >> nom;
       if (nom == "epaisStockeDansElement")
         { // cas où les épaisseurs sont stockées dans l'élément
           if (donnee_specif.epais == NULL) donnee_specif.epais = new Epai;
           ent >>  nom >> donnee_specif.epais->epaisseur0
               >>  nom >> donnee_specif.epais->epaisseur_t
               >>  nom >> donnee_specif.epais->epaisseur_tdt;
         }
       else 
       	 // cas d'un stockage au niveau des noeuds
       	 { if (donnee_specif.epais != NULL) delete donnee_specif.epais;};
       // CL éventuelles : 
       ent >> nom ;
       bool existe_CL; ent >> existe_CL;
       if (existe_CL)
	        {if (areteTypeCL != (&(unefois->doCoMemb->tabType_rienCL)))
           { // si les tableaux n'existent pas on les crée
             if (areteTypeCL == (&(unefois->doCoMemb->tabType_rienCL)))
                 areteTypeCL = new Tableau <EnuTypeCL> (3);
             if (vplan == (&(unefois->doCoMemb->vplan_rien)))
                 vplan = new Tableau <Coordonnee3> (3);
             // et on récupère les infos
             ent >> nom; (*areteTypeCL)(1) = Id_nomTypeCL(nom); ent >> (*vplan)(1);
             ent >> nom; (*areteTypeCL)(2) = Id_nomTypeCL(nom); ent >> (*vplan)(2);
             ent >> nom; (*areteTypeCL)(3) = Id_nomTypeCL(nom); ent >> (*vplan)(3);
           }
         }
       else
        {// on supprime les tableaux locaux éventuels et on pointe vers les valeurs par défaut
         if (areteTypeCL != (&(unefois->doCoMemb->tabType_rienCL))) delete areteTypeCL;
         areteTypeCL = (&(unefois->doCoMemb->tabType_rienCL));
         if (vplan != (&(unefois->doCoMemb->vplan_rien))) delete vplan;
         vplan = (&(unefois->doCoMemb->vplan_rien));
        };	
       break;
	    }
    case  2 : // ----------- lecture uniquement de se qui varie --------------------
     { // récup contraintes et déformation
       lesPtMecaInt.Lecture_base_info(ent,cas);
       // les données spécifiques
       bool avecEpaisDansElement = true;
       ent >> avecEpaisDansElement;
       if (avecEpaisDansElement)
       	{ if (donnee_specif.epais == NULL) donnee_specif.epais = new Epai;
       	  ent >> nom >> donnee_specif.epais->epaisseur_tdt;	
       	  donnee_specif.epais->epaisseur_t = donnee_specif.epais->epaisseur_tdt;
       	};

       // CL éventuelles : on ne les lit pas car elles sont généré éventuellement 
       // à chaque fois que l'on met les CL, sinon elles ne changent pas 
	      break;
     }
    default :
     { cout << "\nErreur : valeur incorrecte du type de lecture !\n";
       cout << "SfeMembT::::Lecture_base_info(ifstream& ent,const "
            << "Tableau<Noeud  *> * tabMaillageNoeud,const int cas)"
            << " cas= " << cas << endl;
       Sortie(1);
     }
   }
  };
  
// cas donne le niveau de sauvegarde
// = 1 : on sauvegarde tout
// = 2 : on sauvegarde uniquement les données variables (supposées comme telles)
void SfeMembT::Ecriture_base_info(ofstream& sort,const int cas) 
{// tout d'abord appel de l'écriture de la classe elem_meca
 ElemMeca::Ecriture_bas_inf(sort,cas);
 // traitement du cas particulier de l'élément central
 switch (cas)
  { case 1 : // ------- on sauvegarde tout -------------------------
     { 
       // des tenseurs déformation et contrainte,
       lesPtMecaInt.Ecriture_base_info(sort,cas);
       // les données spécifiques
       if (donnee_specif.epais != NULL)
         { sort << "\n epaisStockeDansElement " << " epaisseur0= " << donnee_specif.epais->epaisseur0
                << " epaisseur_t= " << donnee_specif.epais->epaisseur_t
                << " epaisseur_tdt= " << donnee_specif.epais->epaisseur_tdt;
         }
       else { sort << "\n epaisStockeAuxNoeuds ";	};
       // CL éventuelles : 
       sort << " CL= ";
	      if (areteTypeCL != (&(unefois->doCoMemb->tabType_rienCL)))
        { // si ça ne pointe pas sur la valeur par défaut, alors c'est qu'il y a
          // des CL particulières,
          sort << " 1 ";
          sort << NomTypeCL((*areteTypeCL)(1)) << " " << (*vplan)(1)
               << NomTypeCL((*areteTypeCL)(2)) << " " << (*vplan)(2)
               << NomTypeCL((*areteTypeCL)(3)) << " " << (*vplan)(3);
        }
	      else
   	   	{sort << " 0 ";
	   	   };
            
	      break;
	    }
    case  2 : // ----------- sauvegarde uniquement de se qui varie --------------------
     { // des tenseurs déformation et contrainte, 
       lesPtMecaInt.Ecriture_base_info(sort,cas);
       // les données spécifiques
       if (donnee_specif.epais != NULL)
        { sort << "\n 1 epaisseur_tdt= " << donnee_specif.epais->epaisseur_tdt;}
       else
        { sort << "\n 0 ";}; 
       // CL éventuelles : on ne les sort pas car elles sont généré éventuellement 
       // à chaque fois que l'on met les CL, sinon elles ne changent pas 
	      break;
	    }
    default :
	    { cout << "\nErreur : valeur incorrecte du type d'écriture !\n";
	      cout << "SfeMembT::::Ecriture_base_info(ofstream& sort,const int cas)"
			         << " cas= " << cas << endl;
	      Sortie(1);
	    }
  };
};

//------- calcul d'erreur, remontée des contraintes -------------------

// calcul du résidu et de la matrice de raideur pour le calcul d'erreur
// en fait on ne calculera l'erreur que sur la contrainte sur toute la surface
// mais sur le premier pt d'integ d'épaisseur
Element::Er_ResRaid    SfeMembT::ContrainteAuNoeud_ResRaid()
    {  SfeMembT::DonnComSfe* CoSfe = unefois->doCoMemb;         // pour simplifier l'écriture
       // dimensionnement de la metrique
       if(!( unefois->CalResPrem_t ))
          {unefois->CalResPrem_t += 1;
           Tableau<Enum_variable_metrique> tab(10);
           tab(1) = igiB_0; tab(2) = igiB_t; tab(3) = igijBB_0;tab(4) = igijBB_t;
           tab(5) = igijHH_t; tab(6) = id_giB_t; tab(7) = id_gijBB_t ;
           tab(8) = id_gijBB_t ;tab(9) = igradVBB_t; tab(10) = iVt;
           CoSfe->met_SfeMembT.PlusInitVariables(tab) ;
          };  
       // appel du programme général
       int tabn_taille = tab_noeud.Taille();
       ElemMeca:: SigmaAuNoeud_ResRaid(tabn_taille,
                                        CoSfe->eleCentre->TaPhi(),
                                        CoSfe->eleCentre->TaWi(),
                                        CoSfe-> resErr,CoSfe->raidErr,
                                        CoSfe->eleEr->TaPhi(),
                                        CoSfe->eleEr->TaWi());
     
       // --- on tient compte de l'épaisseur à t, supposée déjà calculée ou récupérée
       // dans le cas d'un élément 3D, épaisseur est stockée aux noeuds, donc ici la variable
       // épaisseur_t ne devrait pas exister, en particulier le jacobien intègre déjà l'épaisseur
       // pour éviter des tests et une recopie du code on laisse epaisseur_t, avec une valeur 1
       // ce qui fait qu'elle n'intervient pas
       double epaisseur = 0.; // init
       if (donnee_specif.epais == NULL) {epaisseur = 1.;}
       else {epaisseur = donnee_specif.epais->epaisseur_t;};
       // on tient compte de l'épaisseur à t, supposée déjà calculée ou récupérée
       int taille_resErr=CoSfe->resErr.Taille();
       for (int i=1;i<= taille_resErr;i++)
          (*CoSfe-> resErr(i)) *= epaisseur;
       CoSfe->raidErr *= epaisseur;

       return    (Element::Er_ResRaid( &(CoSfe-> resErr),&(CoSfe->raidErr)));
    } ;

// calcul du résidu  pour le calcul d'erreur ramenée aux noeuds
Element::Er_ResRaid   SfeMembT::ErreurAuNoeud_ResRaid()
    {  SfeMembT::DonnComSfe* CoSfe = unefois->doCoMemb;         // pour simplifier l'écriture
       // dimensionnement de la metrique
       if(!( unefois->CalResPrem_t ))
       { unefois->CalResPrem_t += 1;
         Tableau<Enum_variable_metrique> tab(10);
         tab(1) = igiB_0; tab(2) = igiB_t; tab(3) = igijBB_0;tab(4) = igijBB_t;
         tab(5) = igijHH_t; tab(6) = id_giB_t; tab(7) = id_gijBB_t ;
         tab(8) = id_gijBB_t ;tab(9) = igradVBB_t; tab(10) = iVt;
         CoSfe->met_SfeMembT.PlusInitVariables(tab) ;
        };  
       // appel du programme général
       int tabn_taille = tab_noeud.Taille();
       ElemMeca::Cal_ErrAuxNoeuds(tabn_taille,
                                   CoSfe->eleCentre->TaPhi(),CoSfe->eleCentre->TaWi()
                                   ,CoSfe-> resErr );
     
       // --- on tient compte de l'épaisseur à t, supposée déjà calculée ou récupérée
       // dans le cas d'un élément 3D, épaisseur est stockée aux noeuds, donc ici la variable
       // épaisseur_t ne devrait pas exister, en particulier le jacobien intègre déjà l'épaisseur
       // pour éviter des tests et une recopie du code on laisse epaisseur_t, avec une valeur 1
       // ce qui fait qu'elle n'intervient pas
       double epaisseur = 0.; // init
       if (donnee_specif.epais == NULL) {epaisseur = 1.;}
       else {epaisseur = donnee_specif.epais->epaisseur_t;};
       int taille_resErr=CoSfe->resErr.Taille();
       for (int i=1;i<= taille_resErr;i++)
          (*CoSfe-> resErr(i)) *= epaisseur;
       CoSfe->raidErr *= epaisseur;

       return    (Element::Er_ResRaid( &(CoSfe-> resErr),&(CoSfe->raidErr)));
     } ;
     
// actualisation des ddl et des grandeurs actives de t+dt vers t      
void SfeMembT::TdtversT()
 { lesPtMecaInt.TdtversT(); // contrainte
   if (donnee_specif.epais != NULL)
      donnee_specif.epais->epaisseur_t = donnee_specif.epais->epaisseur_tdt;
   int taille = lesPtMecaInt.NbPti();
   for (int ni=1;ni<= taille; ni++)
     { if (tabSaveDon(ni) != NULL)  tabSaveDon(ni)->TdtversT();
       if (tabSaveTP(ni) != NULL)  tabSaveTP(ni)->TdtversT();
       if (tabSaveDefDon(ni) != NULL)  tabSaveDefDon(ni)->TdtversT();
       }
   ElemMeca::TdtversT_(); // appel de la procédure mère
  };
// actualisation des ddl et des grandeurs actives de t vers tdt      
void SfeMembT::TversTdt()
 { lesPtMecaInt.TversTdt(); // contrainte
   if (donnee_specif.epais != NULL)
      donnee_specif.epais->epaisseur_tdt = donnee_specif.epais->epaisseur_t;
   int taille = lesPtMecaInt.NbPti();
   for (int ni=1;ni<= taille; ni++)
    { if (tabSaveDon(ni) != NULL) tabSaveDon(ni)->TversTdt();
      if (tabSaveTP(ni) != NULL) tabSaveTP(ni)->TversTdt();
      if (tabSaveDefDon(ni) != NULL) tabSaveDefDon(ni)->TversTdt();
      }
   ElemMeca::TversTdt_(); // appel de la procédure mère
  };

// calcul de l'erreur sur l'élément.
// en fait on ne calculera l'erreur que sur la contrainte sur toute la surface
// mais sur le premier pt d'integ d'épaisseur
void SfeMembT::ErreurElement(int type,double& errElemRelative
                            ,double& numerateur, double& denominateur) 
    {  SfeMembT::DonnComSfe* CoSfe = unefois->doCoMemb;         // pour simplifier l'écriture
       // dimensionnement de la metrique
       if(!( unefois->CalResPrem_t ))
       { unefois->CalResPrem_t += 1;
         Tableau<Enum_variable_metrique> tab(10);
         tab(1) = igiB_0; tab(2) = igiB_t; tab(3) = igijBB_0;tab(4) = igijBB_t;
         tab(5) = igijHH_t; tab(6) = id_giB_t; tab(7) = id_gijBB_t ;
         tab(8) = id_gijBB_t ;tab(9) = igradVBB_t; tab(10) = iVt;
         CoSfe->met_SfeMembT.PlusInitVariables(tab) ;
        };  
       // appel du programme général
        ElemMeca::Cal_ErrElem(type,errElemRelative,numerateur,denominateur,
                              tab_noeud.Taille(),CoSfe->eleCentre->TaPhi(),
                              CoSfe->eleCentre->TaWi(),
                              CoSfe->eleEr->TaPhi(),CoSfe->eleEr->TaWi());
     } ;

// mise à jour de la boite d'encombrement de l'élément, suivant les axes I_a globales
// en retour coordonnées du point mini dans retour.Premier() et du point maxi dans .Second()
// la méthode est différente de la méthode générale car il faut prendre en compte l'épaisseur de l'élément
const DeuxCoordonnees& SfeMembT::Boite_encombre_element(Enum_dure temps)
  { int tab_taille= tab_noeud.Taille();
    // on commence par calculer la boite d'encombrement pour l'élément médian
    Element::Boite_encombre_element( temps);
    // ensuite on augment sytématiquement dans toutes directions d'une valeur h/2 majorée
    // h étant l'épaisseur maxi constatée
    double epais_maxi=0.; // init
    if (donnee_specif.epais != NULL)
      { // cas où les épaisseurs sont stockées sur l'élément
        epais_maxi = donnee_specif.epais->epaisseur_tdt;
       }
    else
      { // cas où les épaisseurs sont stockées aux noeuds
        for (int ine=1;ine<=nombre->nbnce;ine++) // balayage des  noeuds
           epais_maxi = MaX(epais_maxi,tab_noeud(ine)->Valeur_tdt(EPAIS)); 
      };
    
    double hSur2maj = epais_maxi * 0.5
                         * (ParaGlob::param->ParaAlgoControleActifs().Extra_boite_prelocalisation()-1.);
    // ajout d'un extra dans toutes les directions
    hSur2maj += ParaGlob::param->ParaAlgoControleActifs().Ajout_extra_boite_prelocalisation();
    // mise à jour
    boite_encombre.Premier().Ajout_meme_valeur(-hSur2maj);	// le min
	   boite_encombre.Second().Ajout_meme_valeur(hSur2maj);    // le max
    // retour
    return boite_encombre;
   };

// ramène le nombre de points d'intégration de surface correspondant à un type énuméré
// ramène 0 si l'élément n'est pas une plaque ou coque
int SfeMembT::NbPtIntegSurface(Enum_ddl ddl ) const 
 { switch (ddl)
 	{ case ERREUR : return nombre->nbisEr; break;
 	  default : return nombre->nbis; break;
 	};
 };
// ramène le nombre de points d'intégration en épaisseur correspondant à un type énuméré
// ramène 0 si l'élément n'est pas une plaque ou coque
int SfeMembT::NbPtIntegEpaiss(Enum_ddl ddl) const 
 { switch (ddl)
 	{ case ERREUR : return nombre->nbieEr; break;
 	  default : return nombre->nbie; break;
 	};
 };
// ramene l'element geometrique de surface correspondant au ddl passé en paramètre
// ou null si ce n'est pas définie, dans ce cas si l'élément géométrique de surface est 2D
// cela signifie qu'il faut se référer à l'élément générique: ElementGeometrie(...
ElemGeomC0* SfeMembT::ElementGeometrieSurface(Enum_ddl ddl ) const 
 { switch (ddl)
 	{ case ERREUR : return (unefois->doCoMemb->eleEr); break;
 	  default : return (unefois->doCoMemb->eleCentre); break;
 	};
 };
// ramene l'element geometrique d'épaisseur correspondant au ddl passé en paramètre
// ou null si ce n'est pas définie, dans ce cas si l'élément géométrique de surface est 2D
// cela signifie que tout est constant (identique) dans l'épaisseur
ElemGeomC0* SfeMembT::ElementGeometrieEpaiss(Enum_ddl ddl ) const 
 { switch (ddl)
 	{ case ERREUR : return &(unefois->doCoMemb->segmentEr); break;
 	  default : return &(unefois->doCoMemb->segment); break;
 	};
 };

// retourne un numero d'ordre d'un point le plus près ou est exprimé la grandeur enum
// par exemple un point d'intégration, mais n'est utilisable qu'avec des méthodes particulières
// par exemple CoordPtInteg, ou Valeur_a_diff_temps
// car le numéro d'ordre peut-être différent du numéro d'intégration au sens classique 
// temps: dit si c'est à 0 ou t ou tdt
int SfeMembT::PointLePlusPres(Enum_dure temps,Enum_ddl enu, const Coordonnee& M) 
  { int iret;
    // récupération des éléments géométriques correspondant à Enu
	ElemGeomC0* elSur = this->ElementGeometrieSurface(enu);
	ElemGeomC0* elEpa = this->ElementGeometrieEpaiss(enu);	
	// récupération des tableaux des fonctions d'interpolations
	const Tableau <Vecteur> & tabphiS =   elSur->TaPhi();
	const Tableau < Mat_pleine > & tabDphiS =   elSur->TaDphi();
	const Tableau <Vecteur> & tabphiH =   elEpa->TaPhi();
	
	// on boucle sur les pt d'integ pour avoir le point le plus près
	int nbptSur = tabphiS.Taille();
	int nbptEpa = tabphiH.Taille();
	
	Coordonnee P; iret=1; double dist= ConstMath::tresgrand;
	
	int ipt=1;
	for (int iptH = 1;iptH <= nbptEpa;iptH++)
	 for (int iptS = 1; iptS <= nbptSur;iptS++,ipt++)
	 { switch (temps)
	   { case TEMPS_0 : P = ((Met_Sfe1*) met)->PointSfe1M_0
	              (tab_noeud,tabphiS(iptS),tabDphiS(iptS),donnee_specif.epais,tabphiH(iptH)); break;
	     case TEMPS_t : P = ((Met_Sfe1*) met)->PointSfe1M_t
	              (tab_noeud,tabphiS(iptS),tabDphiS(iptS),donnee_specif.epais,tabphiH(iptH)); break;
	     case TEMPS_tdt : P = ((Met_Sfe1*) met)->PointSfe1M_tdt
	              (tab_noeud,tabphiS(iptS),tabDphiS(iptS),donnee_specif.epais,tabphiH(iptH)); break;
	    }
	   double di=(M-P).Norme();
	   if (di < dist) { dist = di; iret = ipt;}; 
     }
    return iret; // retour
   }; 
		         
// recuperation des coordonnées du point de numéro d'ordre iteg pour 
// la grandeur enu
// temps: dit si c'est à 0 ou t ou tdt
// si erreur retourne erreur à true
Coordonnee SfeMembT::CoordPtInteg(Enum_dure temps,Enum_ddl enu,int iteg,bool& erreur)
  { Coordonnee ptret;  // le retour
    // récupération des éléments géométriques correspondant à Enu
	ElemGeomC0* elSur = this->ElementGeometrieSurface(enu);
	ElemGeomC0* elEpa = this->ElementGeometrieEpaiss(enu);	
	// récupération des tableaux des fonctions d'interpolations
	const Tableau <Vecteur> & tabphiS =   elSur->TaPhi();
	const Tableau < Mat_pleine > & tabDphiS =   elSur->TaDphi();
	const Tableau <Vecteur> & tabphiH =   elEpa->TaPhi();
	// def du numéro d'integ en surface et épaisseur
	int nbptSur = tabphiS.Taille();
	int nbptEpa = tabphiH.Taille();
	int iptS = iteg / nbptEpa +1; // division entière
	int iptH = iteg - (iptS-1) * nbptEpa ; 
    // calcul du point
	if ((iteg < 0) || (iteg > (nbptSur*nbptEpa)))
	 { erreur = false;}
	else
	 { switch (temps)
	   { case TEMPS_0 : ptret = ((Met_Sfe1*) met)->PointSfe1M_0
	              (tab_noeud,tabphiS(iptS),tabDphiS(iptS),donnee_specif.epais,tabphiH(iptH)); break;
	     case TEMPS_t : ptret = ((Met_Sfe1*) met)->PointSfe1M_t
	              (tab_noeud,tabphiS(iptS),tabDphiS(iptS),donnee_specif.epais,tabphiH(iptH)); break;
	     case TEMPS_tdt : ptret = ((Met_Sfe1*) met)->PointSfe1M_tdt
	              (tab_noeud,tabphiS(iptS),tabDphiS(iptS),donnee_specif.epais,tabphiH(iptH)); break;
	    }
     }
    return ptret; // retour
   }; 
