// 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 "ElemThermi.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"


 // -------------------- 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 ElemThermi::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 ElemThermi::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) =   (ElemThermi*) 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 	ElemThermi::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) =   (ElemThermi*) 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 	ElemThermi::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) =   (ElemThermi*) 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 	ElemThermi::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);
		break;
	 }	

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

// stabilisation pour un calcul implicit
void ElemThermi::Cal_implicit_hourglass()
{ 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));

//---- 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))
		 {// 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
		  (*(resraid1.raid)) -= (*(resraid2.raid));
		  (*(resraid1.res)) -= (*(resraid2.res));
		  // on tiend compte du facteur de modération
		  (*(resraid1.raid)) *= coefStabHourglass;
		  (*(resraid1.res)) *= coefStabHourglass;
		  // on sauvegarde la raideur
		  raid_hourglass_transitoire = new Mat_pleine((*(resraid1.raid)));
		  // 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
		  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));
		  // on tiend compte du facteur de modération
		  (*(resHourglass1)) *= coefStabHourglass;
		  // on met à jour le résidu de l'élément principal
		  (*residu) += (*(resHourglass1));
		  // pour la partie raideur: on met à jour la raideur de l'élément principal
		  (*raideur) +=  (*raid_hourglass_transitoire);
		  };

//---- 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 ElemThermi::Cal_implicit_hourglass() \n";
			Sortie(1);
  };
		
};

// stabilisation pour un calcul explicit
void ElemThermi::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_NON_DEFINIE : 
			// on ne fait rien
	 break;
	default :
			cout << "\n*** Erreur : cas de gestop, d'hourglass non defini !\n";
			cout << "\n ElemThermi::Cal_implicit_hourglass() \n";
			Sortie(1);
  };
		
};
         
		  
// récupération de l'energie d'hourglass éventuelle
double ElemThermi::Energie_Hourglass()
{ double enerHourglass = 0.;
  switch (type_stabHourglass)
  {case STABHOURGLASS_PAR_COMPORTEMENT :
	 { // on récupère les énergies stockées à l'élément
	   const EnergieThermi& energieTotale = tab_elHourglass(1)->EnergieTotaleElement();
		enerHourglass = coefStabHourglass * (energieTotale.EnergieElastique() 
							 + energieTotale.DissipationPlastique() + energieTotale.DissipationVisqueuse()); 
	 };
	 break;
  };
  return enerHourglass;
};

// 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 ElemThermi::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 ElemThermi::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 & ElemThermi::Frontiere_elethermi(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;
 };

 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  ElemThermi::Frontiere_points(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_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  ElemThermi::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 = posi_tab_front_point = 0;}         
           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 = posi_tab_front_point = tail_fa; // après les surfaces
                 };
         };
     // 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  ElemThermi::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 = el.NbFe(); // nombre potentiel de faces
  if (num > tail_fa)
      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 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 = posi_tab_front_point = tail_fa; // après les surfaces
                 };
         };
           // --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 é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 ElemThermi::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 ElemThermi::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 ElemThermi::CalculNormale_noeud(...";
               retour = 0;
               Sortie(1);
              }
          };
        // retour
        return retour;
     };

