// FICHIER : HexaQ.cp
// CLASSE : HexaQ

// 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 "GeomHexaQuad.h"

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

HexaMemb::DonnComHexa * HexaQ::doCoHexa = NULL; 
HexaMemb::UneFois  HexaQ::uneFois; 
HexaQ::NombresConstruireHexaQ HexaQ::nombre_V; 
HexaQ::ConsHexaQ HexaQ::consHexaQ;

// constructeur définissant les nombres (de noeud, de point d'integ ..)
HexaQ::NombresConstruireHexaQ::NombresConstruireHexaQ() 
 {  nbne  = 20; // le nombre de noeud de l'élément
    nbneS = 8; // le nombre de noeud des facettes
    nbneA = 3; // le nombre de noeud des aretes
    nbi   = 8; // le nombre de point d'intégration pour le calcul mécanique
//    nbi   = 27; // le nombre de point d'intégration pour le calcul mécanique
    nbiEr = 27; // le nombre de point d'intégration pour le calcul d'erreur
    nbiV  = 8; // le nombre de point d'intégration pour le calcul de second membre volumique
    nbiS  = 4; // le nombre de point d'intégration pour le calcul de second membre surfacique
    nbiA  = 2; // le nombre de point d'intégration pour le calcul de second membre linéique
    nbiMas = 27; // le nombre de point d'intégration pour le calcul de la matrice masse 
	 nbiHour = 27; // éventuellement, le nombre de point d'intégration un blocage d'hourglass
  }; 

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


// Constructeur par defaut, le seul accepte en dimension different de 3
HexaQ::HexaQ () :
  HexaMemb(0,-3,QUADRATIQUE,HEXAEDRE)
 { nombre = & nombre_V; 
   tab_noeud.Change_taille(nombre->nbne);
   // 20 noeuds,8 pt d'integration
   // calcul de doCoHexaQ egalement si c'est le premier passage
   // après hexa, le nombre de point d'intégration de surface pour le second membre
   // ici 4 et 8 noeuds pour les éléments de surface
   ElemGeomC0* hexa=NULL;ElemGeomC0* hexaEr=NULL; ElemGeomC0* hexaMas=NULL;
   if ( doCoHexa == NULL)  
      {hexa = new GeomHexaQuad(nombre->nbi);
      // pour le calcul d'erreur il faut plus de pt d'intégration pour éviter la singularité
      // de la matrice de raideur -> 27
      hexaEr = new GeomHexaQuad(nombre->nbiEr);
      // idem pour les calculs de matrices masses consitstantes
      hexaMas = new GeomHexaQuad(nombre->nbiMas);
      }
   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 hexaedriques quadratiques "<< endl;
	   delete hexa;delete hexaEr;delete hexaMas;
	   unefois = NULL;
	  }	  
   else 
    { unefois = & uneFois; // affectation du pointeur de la classe générique triangle
      doCoHexa = HexaMemb::Init (hexa,hexaEr,hexaMas,NULL);
      unefois->nbelem_in_Prog++;
    } 
};

// Constructeur  fonction  d'un numero
// d'identification 	
HexaQ::HexaQ (int num_mail,int num_id) :
  HexaMemb(num_mail,num_id,QUADRATIQUE,HEXAEDRE)
 { nombre = & nombre_V; 
   tab_noeud.Change_taille(nombre->nbne);
   // 20 noeuds,8 pt d'integration
   // calcul de doCoHexa egalement si c'est le premier passage
   // après hexa, le nombre de point d'intégration de surface pour le second membre
   // ici 4 et 4 noeuds pour les éléments de surface
   ElemGeomC0* hexa=NULL;ElemGeomC0* hexaEr=NULL; ElemGeomC0* hexaMas=NULL;
   if ( doCoHexa == NULL)  
      {hexa = new GeomHexaQuad(nombre->nbi);
      // pour le calcul d'erreur il faut plus de pt d'intégration pour éviter la singularité
      // de la matrice de raideur -> 27
      hexaEr = new GeomHexaQuad(nombre->nbiEr);}
      // idem pour les calculs de matrices masses consitstantes
      hexaMas = new GeomHexaQuad(nombre->nbiMas);
   #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 HexaQ, dim = " << ParaGlob::Dimension()
              << "\n alors que l'on doit avoir  3 !! " << endl;
       Sortie (1);
      }        
   #endif 
    { unefois = & uneFois; // affectation du pointeur de la classe générique triangle
      doCoHexa = HexaMemb::Init (hexa,hexaEr,hexaMas,NULL);
      unefois->nbelem_in_Prog++;
     } 
};

// Constructeur utile si  le numero de l'element et
// le tableau des noeuds sont connus
HexaQ::HexaQ (int num_mail,int num_id,const Tableau<Noeud *>& tab):
    HexaMemb(num_mail,num_id,QUADRATIQUE,HEXAEDRE,tab) 
{  nombre = & nombre_V; 
   if (tab_noeud.Taille() != nombre->nbne)
	  { cout << "\n erreur de dimensionnement du tableau de noeud \n";
	   cout << " HexaQ::HexaQ (int num_mail,int num_id,const Tableau<Noeud *>& tab)\n";
	   Sortie (1); }
   // 20 noeuds,8 pt d'integration
   // calcul de doCoHexa egalement si c'est le premier passage
   // après hexa, le nombre de point d'intégration de surface pour le second membre
   // ici 4 et 4 noeuds pour les éléments de surface
   ElemGeomC0* hexa=NULL;ElemGeomC0* hexaEr=NULL; ElemGeomC0* hexaMas=NULL;
   if ( doCoHexa == NULL)  
      {hexa = new GeomHexaQuad(nombre->nbi);
      // pour le calcul d'erreur il faut plus de pt d'intégration pour éviter la singularité
      // de la matrice de raideur -> nombre->nbiEr
      hexaEr = new GeomHexaQuad(nombre->nbiEr);}
      // idem pour les calculs de matrices masses consitstantes
      hexaMas = new GeomHexaQuad(nombre->nbiMas);
   #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 HexaQ, dim = " << ParaGlob::Dimension()
              << "\n alors que l'on doit avoir  3 !! " << endl;
       Sortie (1);
      }        
   #endif 
    { unefois = & uneFois; // affectation du pointeur de la classe générique triangle
      bool sans_init_noeud = true;
      doCoHexa = HexaMemb::Init (hexa,hexaEr,hexaMas,NULL,sans_init_noeud);
      // construction du tableau de ddl spécifique à l'élément pour ses 
      ConstTabDdl(); 
      unefois->nbelem_in_Prog++;
     } 
};

HexaQ::HexaQ (const HexaQ& HexaQraM) :
 HexaMemb (HexaQraM)
 
// Constructeur de copie
  {  ElemGeomC0* hexa=NULL;ElemGeomC0* hexaEr=NULL; ElemGeomC0* hexaMas=NULL;
     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 HexaMemb
     unefois->nbelem_in_Prog++;
   };	

HexaQ::~HexaQ ()
// Destruction effectuee dans HexaMemb
{ if (unefois != NULL) 
    {unefois->nbelem_in_Prog--;
     Destruction();
     }
};

// renseignement d'un élément quadratique incomplet à partir d'un élément linéaire de même type
// retourne les nouveaux noeuds construit à partir de l'interpolation linéaire.
// dans le cas où 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 *> HexaQ::Construct_from_lineaire(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 linéaire
   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'élément linéaire 
   for (int i=1;i<=etaille;i++)
     tab_noeud(i)=e_tab_noeud(i);
   // 2) définition des noeuds milieux des différentes arrêtes
   //    en fait il s'agit des noeuds de 9 à 20
   const int num_inf=9; const int num_sup=20;
   int idmail = tab_noeud(1)->Num_Mail(); // récup du numéro de maillage associé au noeud
   int dim =  ParaGlob::Dimension();
   
   HexaMemb::DonnComHexa* doCoHexa = unefois->doCoMemb; // pour simplifier l'écriture   
   // récupération des coordonnées des noeuds locaux
   Tableau <Coordonnee > const & ptlocal = doCoHexa->hexaed->PtelemRef(); 
   // on boucle sur les noeuds supplémentaires : de 9 à 20
   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);
         ((HexaMemb&)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);
        ((HexaMemb&)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++)
       { ((HexaMemb&)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 arrêtes par rapport a ceux de l'element
   Tableau<Tableau<int> > const & nons = doCoHexa->hexaed->NonS(); 
   // initialisation de li_bornes
   li_bornes.erase(li_bornes.begin(),li_bornes.end());
   // on boucle sur les arrêtes 
   // par rapport au linéaire, 9 est au centre de  l'arrête 1,
   // 10 sur l'arrête 2, etc.. donc ici on peut faire un adressage directe, néanmoins
   // dans le cas où les arrêtes n'ont pas le même ordre que les noeuds supplémentaires
   // il faut faire un adressage indirecte: voir l'exemple de HexaQcom pour la fonction
   //  Construct_from_imcomplet
   int inf_nc=e_tab_noeud(1)->Num_noeud();int sup_nc=inf_nc; // init
   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   l'arrête a doc
        //  tout d'abord initialisation
        const Tableau<int>&  nosa = nons(ib);  int nbnoeudarrete=nosa.Taille() ; // en fait ici 2
        // on regarde le premier noeud de l'arrête
        int inf=tab_noeud(nosa(1))->Num_noeud(); int sup = inf;
        // puis le dernier
        int num= tab_noeud(nosa(nbnoeudarrete))->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 HexaQ::AfficheVarDual(ostream& sort, Tableau<string>& nom)
  { // affichage de l'entête de l'element
    sort << "\n******************************************************************";
    sort << "\n Element HexaQ (hexaedre triquadratique "<<nombre->nbi<<"  pts d'integration) ";
    sort << "\n******************************************************************";
    // appel de la procedure de elem meca
    if (!(uneFois.dualSortHexa) && (uneFois.CalimpPrem))
        { VarDualSort(sort,nom,nombre->nbi,1);
          uneFois.dualSortHexa += 1;
         } 
    else if ((uneFois.dualSortHexa) && (uneFois.CalimpPrem))       
         VarDualSort(sort,nom,nombre->nbi,11);
    else if (!(uneFois.dualSortHexa) && (uneFois.CalResPrem_tdt))       
        { VarDualSort(sort,nom,nombre->nbi,2);
          uneFois.dualSortHexa += 1;
         }         
    else if ((uneFois.dualSortHexa) && (uneFois.CalResPrem_tdt))       
         VarDualSort(sort,nom,nombre->nbi,12);
      // sinon on ne fait rien     
  };