// FICHIER : PentaQComp.cp
// CLASSE : PentaQComp

// 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 <iostream>
using namespace std;  //introduces namespace std
#include <stdlib.h>
#include "Sortie.h"
#include "FrontSegQuad.h"
#include "FrontQuadQuad.h"
#include "FrontTriaQuad.h"
#include "GeomPentaQComp.h"

#include "PentaQComp.h"
//----------------------------------------------------------------
// def des donnees commune a tous les elements
// la taille n'est pas defini ici car elle depend de la lecture 
//----------------------------------------------------------------
//----------------------------------------------------------------

PentaMemb::DonnComPenta * PentaQComp::doCoPentaQComp = NULL;                                     
PentaMemb::UneFois  PentaQComp::uneFois; 
PentaQComp::NombresConstruirePentaQComp PentaQComp::nombre_V; 
PentaQComp::ConsPentaQComp PentaQComp::consPentaQComp;

// constructeur définissant les nombres (de noeud, de point d'integ ..)
// utilisé dans la construction des éléments
PentaQComp::NombresConstruirePentaQComp::NombresConstruirePentaQComp() 
 {  nbne    = 18; // le nombre de noeud de l'élément
    nbneSQ  = 9; // le nombre de noeud des facettes quadrangulaires
    nbneST  = 6; // le nombre de noeud des facettes triangulaires
    nbneAQ  = 3; // le nombre de noeud des aretes entres les faces triangulaires
    nbneAT  = 3; // le nombre de noeud des aretes des facettes triangulaires
    nbI     = 6; // nombre point d'intég pour le calcul méca pour l'élément
    nbiQ    = 3; // nombre point d'intég pour le calcul méca pour les triangles
    nbiT    = 4; // nombre point d'intég pour le calcul méca pour les quadrangles
    nbiEr   =18; // le nombre de point d'intégration pour le calcul d'erreur
    nbiV    = 6; // le nombre de point d'intégration pour le calcul de second membre volumique
    nbiSQ   = 4; // nombre point d'intég pour le calcul de second membre surfacique quadrangulaire
    nbiST   = 3; // nombre point d'intég pour le calcul de second membre surfacique triangulaire
    nbiAQ   = 2; // nB pt integ pour calcul second membre linéique des arête entre faces triangles
    nbiAT   = 2; // nB pt integ pour calcul second membre linéique des arête des triangles
    nbiMas = 18; // le nombre de point d'intégration pour le calcul de la matrice masse 
    nbiHour = 9; // éventuellement, le nombre de point d'intégration un blocage d'hourglass
  };

// =========================== constructeurs ==================


// Constructeur par defaut, le seul accepte en dimension different de 3
PentaQComp::PentaQComp () :
  PentaMemb(0,-3,QUADRACOMPL,PENTAEDRE)
{// on intervient seulement à partir du deuxième élément, 
 if (uneFois.nbelem_in_Prog == 0)
   { uneFois.nbelem_in_Prog++; // au premier passage on se contente d'incrémenter
    }
 else 	// sinon on construit     
  {nombre = & nombre_V; 
   tab_noeud.Change_taille(nombre->nbne);
   // calcul de doCoPentaQComp si c'est le premier passage
   ElemGeomC0* penta=NULL;ElemGeomC0* pentaEr=NULL; ElemGeomC0* pentaMas=NULL;
	ElemGeomC0* pentaeHourg=NULL; // pour le blocage d'hourglass
   if ( doCoPentaQComp == NULL)  
      {penta = new GeomPentaQComp(nombre->nbI);
      // pour le calcul d'erreur il faut au moins autant de points d'intégration que de noeuds
      pentaEr = new GeomPentaQComp(nombre->nbiEr);
      // idem pour le calcul de la matrice masse consistante
      pentaMas = new GeomPentaQComp(nombre->nbiMas);
		pentaeHourg = new GeomPentaQComp(nombre->nbiHour);
      }
   int dim = ParaGlob::Dimension();
   if (dim != 3)  // cas d'une dimension autre que trois
	 { if (ParaGlob::NiveauImpression() >= 7)
	     cout << "\nATTENTION -> dimension " << dim 
	          <<", pas de definition d\'elements pentaedriques lineaires "<< endl;
	   delete penta;delete pentaEr;delete pentaMas;delete pentaeHourg;
	   unefois = NULL;
	  }	  
   else 
     // après penta on défini les données relatives aux surfaces externes (frontières) 
    { unefois = & uneFois; // affectation du pointeur de la classe générique triangle
      doCoPentaQComp = PentaMemb::Init (penta,pentaEr,pentaMas,pentaeHourg);
      unefois->nbelem_in_Prog++;
     } 
  };
};  

// Constructeur  fonction  d'un numero
// d'identification 	
PentaQComp::PentaQComp (int num_mail,int num_id) :
  PentaMemb(num_mail,num_id,QUADRACOMPL,PENTAEDRE)
{// on intervient seulement à partir du deuxième élément, 
 if (uneFois.nbelem_in_Prog == 0)
   { uneFois.nbelem_in_Prog++; // au premier passage on se contente d'incrémenter
    }
 else 	// sinon on construit     
  {nombre = & nombre_V; 
   tab_noeud.Change_taille(nombre->nbne);
   // calcul de doCoPentaQComp si c'est le premier passage
   ElemGeomC0* penta=NULL;ElemGeomC0* pentaEr=NULL; ElemGeomC0* pentaMas=NULL;
	ElemGeomC0* pentaeHourg=NULL; // pour le blocage d'hourglass
   if ( doCoPentaQComp == NULL)  
      {penta = new GeomPentaQComp(nombre->nbI);
      // pour le calcul d'erreur il faut au moins autant de points d'intégration que de noeuds
      pentaEr = new GeomPentaQComp(nombre->nbiEr);
      // idem pour le calcul de la matrice masse consistante
      pentaMas = new GeomPentaQComp(nombre->nbiMas);
		pentaeHourg = new GeomPentaQComp(nombre->nbiHour);
      }
   #ifdef MISE_AU_POINT	 	 
     if (ParaGlob::Dimension() != 3) // cas d'une dimension autre que trois
      { if (ParaGlob::NiveauImpression() >= 2)
	     cout << "\n erreur de dimension dans PentaQComp, dim = " << ParaGlob::Dimension()
              << "\n alors que l'on doit avoir  3 !! " << endl;
       Sortie (1);
      }        
   #endif 
   // après penta on défini les données relatives aux surfaces externes (frontières) 
    { unefois = & uneFois; // affectation du pointeur de la classe générique triangle
      doCoPentaQComp = PentaMemb::Init (penta,pentaEr,pentaMas,pentaeHourg);
      unefois->nbelem_in_Prog++;
     } 
  };
};  

// Constructeur utile si  le numero de l'element et
// le tableau des noeuds sont connus
PentaQComp::PentaQComp (int num_mail,int num_id,const Tableau<Noeud *>& tab):
    PentaMemb(num_mail,num_id,QUADRACOMPL,PENTAEDRE,tab) 
{// on intervient seulement à partir du deuxième élément, 
 if (uneFois.nbelem_in_Prog == 0)
   { uneFois.nbelem_in_Prog++; // au premier passage on se contente d'incrémenter
    }
 else 	// sinon on construit     
  {nombre = & nombre_V; 
   if (tab_noeud.Taille() != nombre->nbne)
	  { cout << "\n erreur de dimensionnement du tableau de noeud \n";
	   cout << " PentaQComp::PentaQComp (double epaiss,int num_mail,int num_id,const Tableau<Noeud *>& tab)\n";
	   Sortie (1); }
   // calcul de doCoPentaQComp si c'est le premier passage
   ElemGeomC0* penta=NULL;ElemGeomC0* pentaEr=NULL; ElemGeomC0* pentaMas=NULL;
	ElemGeomC0* pentaeHourg=NULL; // pour le blocage d'hourglass
   if ( doCoPentaQComp == NULL)  
      {penta = new GeomPentaQComp(nombre->nbI);
      // pour le calcul d'erreur il faut au moins autant de points d'intégration que de noeuds
      pentaEr = new GeomPentaQComp(nombre->nbiEr);
      // idem pour le calcul de la matrice masse consistante
      pentaMas = new GeomPentaQComp(nombre->nbiMas);
		pentaeHourg = new GeomPentaQComp(nombre->nbiHour);
      }
   #ifdef MISE_AU_POINT	 	 
     if (ParaGlob::Dimension() != 3) // cas d'une dimension autre que trois
      { if (ParaGlob::NiveauImpression() >= 2)
	     cout << "\n erreur de dimension dans PentaQComp, dim = " << ParaGlob::Dimension()
              << "\n alors que l'on doit avoir  3 !! " << endl;
       Sortie (1);
      }        
   #endif 
   // après penta on défini les données relatives aux surfaces externes (frontières) 
    { unefois = & uneFois; // affectation du pointeur de la classe générique triangle
      bool sans_init_noeud = true;
      doCoPentaQComp = PentaMemb::Init (penta,pentaEr,pentaMas,pentaeHourg,sans_init_noeud);
      // construction du tableau de ddl spécifique à l'élément pour ses 
      ConstTabDdl(); 
      unefois->nbelem_in_Prog++;
     } 
  };
};  

PentaQComp::PentaQComp (const PentaQComp& PentaQraM) :
 PentaMemb (PentaQraM)
 
// Constructeur de copie
// a priori si on utilise le constructeur de copie, donc il y a déjà un élément
// par contre a priori on ne doit pas faire une copie du premier élément 
{ if (uneFois.nbelem_in_Prog == 1)
   { cout << "\n **** erreur pour l'element PentaQComp, le constructeur de copie ne doit pas etre utilise"
	       << " pour le premier element !! " << endl;
	  Sortie (1);
    }
 else 	    
 {   unefois = & uneFois; // affectation du pointeur de la classe générique 
      // ce qui est relatif à l'initialisation est déjà effectué dans elem_meca et PentaMemb
     unefois->nbelem_in_Prog++;
  };	
};

PentaQComp::~PentaQComp ()
// Destruction effectuee dans PentaMemb
{ if (unefois != NULL) 
    {unefois->nbelem_in_Prog--;
     Destruction();
     }
};
                         
// renseignement d'un élément complet à partir d'un élément incomplet de même type
// retourne les nouveaux noeuds construit à partir de l'interpolation incomplète.
// dans le cas l'élément n'est pas concerné, retourne une liste vide
// ramène également une liste de même dimension contenant les bornes en numéros de noeuds
// entre lesquelles il faut définir les nouveaux numéros de noeuds si l'on veut conserver
// une largeur de bande optimisée du même type
// nbnt+1: est le premier numéro de noeud utilisable pour les nouveaux noeuds
list <Noeud *> PentaQComp::Construct_from_imcomplet(const Element & elem,list <DeuxEntiers> & li_bornes,int nbnt)
 { list <Noeud *> li_ret; // la liste de retour
   // ----- construction de l'élément,
   // 1) définition des noeuds venant de l'élément incomplet
   const Tableau<Noeud *>& e_tab_noeud = elem.Tab_noeud_const(); 
   int nbne = nombre-> nbne; int etaille=e_tab_noeud.Taille();
   tab_noeud.Change_taille(nbne);
   // les premiers noeuds sont ceux de l'incomplet 
   for (int i=1;i<=etaille;i++)
     tab_noeud(i)=e_tab_noeud(i);
   // 2) définition des noeuds centraux des différentes faces
   //    en fait il s'agit des noeuds de 16 à 18
   const int num_inf=16; const int num_sup=18;
   int idmail = tab_noeud(1)->Num_Mail(); // récup du numéro de maillage associé au noeud
   int dim =  ParaGlob::Dimension();
   
   PentaMemb::DonnComPenta* doCoPenta = unefois->doCoMemb; // pour simplifier l'écriture   
   // récupération des coordonnées des noeuds locaux
   Tableau <Coordonnee > const & ptlocal = doCoPenta->pentaed->PtelemRef(); 
   // on boucle sur les noeuds supplémentaires : de 16 à 18
   int ib=1; // indice sup pour l'attribution d'un bon numéro
   for (int ine=num_inf;ine<=num_sup;ine++,ib++)
    { // déf d'un noeud  initialisée aux coordonnées du premier noeud pour l'instant 		  
      Noeud * ptr = new Noeud(*tab_noeud(1)); 
      ptr->Change_num_noeud(nbnt+ib); // attribution d'un bon numéro
      tab_noeud(ine)=ptr; // enregistrement dans les noeuds de l'élément
     } 
   // ajout du tableau de ddl des noeuds, ce qui permet d'activer les ddl XI à t si ce n'est 
   // pas encore fait 
   ConstTabDdl();
   // definition  des coordonnées des nouveaux noeuds
   Coordonnee co(dim); // les coordonnées physique
   Tableau <Coordonnee> t_co; // les coordonnées physique
   // test pour savoir si le calcul à tdt est activé ou pas
   if (tab_noeud(1)->Tdt())
    // cas où l'on travailles à 0 t et tdt
    { for (int ine1=num_inf;ine1<=num_sup;ine1++)
       { t_co.Change_taille(3);
         ((PentaMemb&)elem).Point_physique(ptlocal(ine1),t_co);
         tab_noeud(ine1)->Change_coord0(t_co(1));tab_noeud(ine1)->Change_coord1(t_co(2));
         tab_noeud(ine1)->Change_coord2(t_co(3));
        } 
     }
   else
    // on regarde si on a les coordonnées à t qui sont définis
    {if (tab_noeud(1)->En_service(X1))
      // cas où les coordonnees à t sont définis
      {for (int ine2=num_inf;ine2<=num_sup;ine2++)
       {t_co.Change_taille(2);
        ((PentaMemb&)elem).Point_physique(ptlocal(ine2),t_co);
        tab_noeud(ine2)->Change_coord0(t_co(1));tab_noeud(ine2)->Change_coord1(t_co(2));
        }
       }
     else 
      // cas où seules les coordonnées à 0 sont définis 
      // en fait ici n'arrive jamais !! mais mis pour pas oublier
      {for (int ine3=num_inf;ine3<=num_sup;ine3++)
       { ((PentaMemb&)elem).Point_physique(ptlocal(ine3),co,TEMPS_0);
         tab_noeud(ine3)->Change_coord0(t_co(1));
        } 
       }
     }   
   // 3) enregistrement des noeuds
   for (int ine4=num_inf;ine4<=num_sup;ine4++) li_ret.push_back(tab_noeud(ine4)); 
   // 4) remplissage de li_bornes 
   // recup de la connection des noeuds des faces par rapport a ceux de l'element
   Tableau<Tableau<int> > const & nonf = doCoPenta->pentaed->Nonf(); 
   // initialisation de li_bornes
   li_bornes.erase(li_bornes.begin(),li_bornes.end());
   // On travaille d'abord sur les faces de l'élément
   // par rapport au quadratique incomplet, le noeud 16 est sur la face 3, 17 sur la face 5
   // et 18 sur la face 2
   // par rapport au quadratique incomplet, 21 est au centre de  la face 1,
   // 22 sur la face 3, 23 sur la face 5, 24 sur la face 6
   // 25 sur la face 2, 26 sur la face 4, 27 au centre de l'élément
   // on définit un adressage indirecte des faces de manière à faire ensuite une boucle sur les faces
   Tableau <int> t_i(3); t_i(1)=3; t_i(2)=5;t_i(3)=2;  
   //   // on définit un inf et sup de numéro pour le noeud central, qui sera traité en même temps que les faces
   //   int inf_nc=e_tab_noeud(1)->Num_noeud();int sup_nc=inf_nc;
   for (int ine5=num_inf,ib=1;ine5<=num_sup;ine5++,ib++)
     {  // on cherche le maxi et le mini des numéros des noeuds de la face adoc
        //  tout d'abord initialisation
        const Tableau<int>&  nofa = nonf(t_i(ib));  int nbnoeudface=nofa.Taille() ; // en fait ici 9
        int inf=tab_noeud(nofa(1))->Num_noeud(); int sup = inf;
        for (int i=2;i<nbnoeudface;i++)  // on balaie de 2 à 8
         { int num= e_tab_noeud(nofa(i))->Num_noeud();
           if (inf > num) inf = num; if (sup < num) sup = num;
          }
        li_bornes.push_back(DeuxEntiers(inf,sup)); 
       }
   // 5) retour
   return li_ret;
  };
                         
// affichage dans la sortie transmise, des variables duales "nom"
// aux differents points d'integration
// dans le cas ou nom est vide, affichage de "toute" les variables
void PentaQComp::AfficheVarDual(ostream& sort, Tableau<string>& nom)
 {  // affichage de l'entête de l'element
    sort << "\n********************************************************************************";
    sort << "\n Element PentaQComp (pentaedre trilquadratique complet " << nombre->nbI << " pt d'integration) ";
    sort << "\n********************************************************************************";
    // appel de la procedure de elem meca
    if (!(uneFois.dualSortPenta) && (uneFois.CalimpPrem))
        { VarDualSort(sort,nom,nombre->nbI,1);
          uneFois.dualSortPenta += 1;
         } 
    else if ((uneFois.dualSortPenta) && (uneFois.CalimpPrem))       
         VarDualSort(sort,nom,nombre->nbI,11);
    else if (!(uneFois.dualSortPenta) && (uneFois.CalResPrem_tdt))       
        { VarDualSort(sort,nom,nombre->nbI,2);
          uneFois.dualSortPenta += 1;
         }         
    else if ((uneFois.dualSortPenta) && (uneFois.CalResPrem_tdt))       
         VarDualSort(sort,nom,nombre->nbI,12);
      // sinon on ne fait rien     
  };               	
      
