

// 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 "Hoffman1.h"
#include <cmath>

#include "ConstMath.h"
#include "ConstPhysico.h"

//==================== cas de la class de sauvegarde SaveResul ===================

// constructeur  par défaut à ne pas utiliser
Hoffman1::SaveCrista_Hoffman1::SaveCrista_Hoffman1() :
	I_Kcinetique(0.),I_Kcinetique_t(0.)
  { }; 

// le constructeur courant
Hoffman1::SaveCrista_Hoffman1::SaveCrista_Hoffman1
                        (const double & cristal_t,const double & cristal):
	I_Kcinetique(cristal) ,I_Kcinetique_t(cristal_t) 
	 {}; 

// constructeur de copie 
Hoffman1::SaveCrista_Hoffman1::SaveCrista_Hoffman1
                                         (const Hoffman1::SaveCrista_Hoffman1& sav ):
	I_Kcinetique(sav.I_Kcinetique) ,I_Kcinetique_t(sav.I_Kcinetique_t)
	 { }; 
	   
     //============= 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 Hoffman1::SaveCrista_Hoffman1::Lecture_base_info 
                                                 (istream& ent,const int )
  {  // ici toutes les données sont toujours a priori variables
     // ou en tout cas pour les méthodes appelées, elles sont gérées par le paramètre: cas
     string toto; ent >> toto;
     #ifdef MISE_AU_POINT	 	 
      if (toto != "S_R_Hoffman")
          { cout << "\n erreur en lecture du conteneur pour la loi thermo physique de Tait"
                 << " on attendait S_R_Hoffman et on a lue " << toto
                 << " \n Hoffman1::SaveCrista_Hoffman1::Lecture_base_info(..";
            Sortie(1);     
          }
     #endif
//     ent >> I_Kcinetique_t >> I_Kcinetique;
     ent >> I_Kcinetique_t ; I_Kcinetique = I_Kcinetique_t;
    }; 

// cas donne le niveau de sauvegarde
// = 1 : on sauvegarde tout
// = 2 : on sauvegarde uniquement les données variables (supposées comme telles)
void Hoffman1::SaveCrista_Hoffman1::Ecriture_base_info
                                                 (ostream& sort,const int )
   { // ici toutes les données sont toujours a priori variables
     // ou en tout cas pour les méthodes appelées, elles sont gérées par le paramètre: cas
//     sort << "\n S_R_Hoffman  " << I_Kcinetique_t << " " << I_Kcinetique << " ";
     sort << "\n S_R_Hoffman  " << I_Kcinetique_t << " " ;
	 }; 
	        
// affichage des infos
void Hoffman1::SaveCrista_Hoffman1::Affiche()	
   { cout << "\n S_R_Hoffman  I_Kcinetique_t= " << I_Kcinetique_t << " I_Kcinetique= " << I_Kcinetique << " ";
	 }; 
// idem sur un ostream
void Hoffman1::SaveCrista_Hoffman1::Affiche(ostream& sort)			        
   { sort << "\n S_R_Hoffman  I_Kcinetique_t= " << I_Kcinetique_t << " I_Kcinetique= " << I_Kcinetique << " ";
	 }; 	        

// Surcharge de l'operateur =  
CristaliniteAbstraite::SaveCrista& Hoffman1::SaveCrista_Hoffman1::operator= 
          (const CristaliniteAbstraite::SaveCrista& a)
 {  // récup de la bonne classe
    Hoffman1::SaveCrista_Hoffman1& b =  *((Hoffman1::SaveCrista_Hoffman1*) &a);
    I_Kcinetique = b.I_Kcinetique;
    I_Kcinetique_t = b.I_Kcinetique_t;
    return *this; 
  };

//==================== fin du cas de la class de sauvegarde SaveResul ============

Hoffman1::Hoffman1():
  n(ConstMath::Pi),G0(ConstMath::Pi),N01(ConstMath::Pi)
  ,N02(ConstMath::Pi),Ustar(ConstMath::Pi),Tinf(ConstMath::Pi)
  ,Kg(ConstMath::Pi),Tm(ConstMath::Pi),alphaP(ConstMath::Pi)
  ,betaP(ConstMath::Pi),X_inf(ConstMath::Pi)
  ,algo_integ(new Algo_Integ1D(3)) // par défaut on utilise 3 points de Gauss
  ,Te_t(),Te_tdt(),Pr_t(),Pr_tdt() // variables de travail
    { };
    
// constructeur de copie
Hoffman1::Hoffman1(const Hoffman1& co ):
  n(co.n),G0(co.G0),N01(co.N01)
  ,N02(co.N02),Ustar(co.Ustar),Tinf(co.Tinf)
  ,Kg(co.Kg),Tm(co.Tm),alphaP(co.alphaP)
  ,betaP(co.betaP),X_inf(co.X_inf)
  ,algo_integ(new Algo_Integ1D(*(co.algo_integ)))
  ,Te_t(),Te_tdt(),Pr_t(),Pr_tdt() // variables de travail
    {};
    
// Lecture des donnees de la classe sur fichier
 void Hoffman1::LectureDonneesLoiCrista(UtilLecture * entreePrinc,LesCourbes1D& ,LesFonctions_nD& )
{   // ---- lecture  du tag d'entête  -----
    string nom;
    *(entreePrinc->entree) >> nom; // lecture du tag 
    if(nom != "coefficientsLoiHoffman_")
         { cout << "\n erreur en lecture du tag du debut des parametres "
                << " on attendait la chaine : coefficientsLoiHoffman_ et on a lue: " << nom;
           cout << "\n Hoffman1::LectureDonneesLoiCrista(  "
                << "(UtilLecture * entreePrinc) " << endl ;
           entreePrinc->MessageBuffer("** erreur1 de lecture des parametres de la loi Hoffman1 **");
           throw (UtilLecture::ErrNouvelleDonnee(-1));
           Sortie(1);          
          };
    entreePrinc->NouvelleDonnee(); // prepa du flot de lecture
    
    // --- lecture de l'ensemble des paramètres  ----
    MotCle motCle; // ref aux mots cle
    while (strstr(entreePrinc->tablcar,"fin_coefficientsLoiHoffman_")==0) 
       {
        // si on a  un mot clé global dans la ligne courante c-a-d dans tablcar --> erreur
        if ( motCle.SimotCle(entreePrinc->tablcar))
         { cout << "\n erreur de lecture des parametre on n'a pas trouve le mot cle "
                << " fin_coefficientsLoiHoffman_ et par contre la ligne courante contient un mot cle global  ";
           entreePrinc->MessageBuffer("** erreur2  de lecture des parametres de la loi Hoffman1 **");
           throw (UtilLecture::ErrNouvelleDonnee(-1));
           Sortie(1);     
         };
         
        // lecture d'un mot clé
        *(entreePrinc->entree) >> nom;
        
        if ((entreePrinc->entree)->rdstate() == 0) 
          {} // 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) >>nom;
           }
        #else
        else  if ((entreePrinc->entree)->eof())
          // la lecture est bonne mais on a atteind la fin de la ligne
          { if(nom != "fin_coefficientsLoiHoffman_")
              {entreePrinc->NouvelleDonnee(); *(entreePrinc->entree) >> nom;};
          } 
        #endif  
        else // cas d'une erreur de lecture
         { cout << "\n erreur de lecture inconnue  ";
           entreePrinc->MessageBuffer("** erreur3  de lecture des parametres de la loi Hoffman1 **");
           throw (UtilLecture::ErrNouvelleDonnee(-1));
           Sortie(1);     
         };
        // dans le cas où le fait d'avoir lue une nouvelle ligne fait que l'on a fini la lecture on stop 
        if (nom == "fin_coefficientsLoiHoffman_") 
          {break;}
        // sinon c'est ok et  on passe en revue tous les paramètres
        else if(nom == "n=")            {*(entreePrinc->entree) >> n;}
        else if (nom == "G0=")     {*(entreePrinc->entree) >> G0;}
        else if (nom == "N01=")    {*(entreePrinc->entree) >> N01;}
        else if (nom == "N02=")    {*(entreePrinc->entree) >> N02;}
        else if (nom == "Ustar=")  {*(entreePrinc->entree) >> Ustar;}
        else if (nom == "Tinf=")   {*(entreePrinc->entree) >> Tinf;}
        else if (nom == "Kg=")     {*(entreePrinc->entree) >> Kg;}
        else if (nom == "Tm=")     {*(entreePrinc->entree) >> Tm;}
        else if (nom == "alphaP=") {*(entreePrinc->entree) >> alphaP;}
        else if (nom == "betaP=")  {*(entreePrinc->entree) >> betaP;}
        else if (nom == "X_inf=")  {*(entreePrinc->entree) >> X_inf;}
        else
         { cout << "\n erreur: lecture d'un identificateur inconnue, on a lue:  " << nom ;
           entreePrinc->MessageBuffer("** erreur4  de lecture des parametres de la loi Hoffman1 **");
           throw (UtilLecture::ErrNouvelleDonnee(-1));
           Sortie(1);     
         };
        
       }; //-- fin du while
     
    entreePrinc->NouvelleDonnee(); // prepa du flot de lecture
   };

// affichage de la loi
void Hoffman1::Affiche() const
 { cout << "\n# ----- loi de Hoffman1 permettant le calcul de K(T,P)  -------------------------------"
        << "\n     n= "      << n      << " G0= "    << G0    << " N01= " << N01 << " N02= " << N02 << " "
        << "\n     Ustar= "  << Ustar  << " Tinf= "  << Tinf  << " Kg= "  << Kg  << " Tm= "  << Tm << " "
        << "\n     alphaP= " << alphaP << " betaP= " << betaP << " X_inf= " << X_inf << " " ;
	}; 
    
// affichage et definition interactive des paramètres 
void Hoffman1::Info_commande_LoisCrista(UtilLecture& entreePrinc)
 {  ofstream & sort = *(entreePrinc.Commande_pointInfo()); // pour simplifier
 
    n= 3;             G0= 2.83e2;       N01= 0.156;  N02= 15.1;
    Ustar= 6250.;     Tinf= 243.;       Kg= 5.5e5;   Tm= 483.; 
    alphaP= 0.283e-6; betaP= -2.08e-16; X_inf= 0.684;     
    
    sort << "\n# ----- loi de Hoffman1 permettant le calcul de K(T,P)  ------------------------------"
         << "\n# |la loi necessite 11 coefficients  n,G0,N01,N02,Ustar,Tinf,Kg,Tm,alphaP,betaP,X_inf|"
         << "\n# |   le calcul de K(T,P) s'effectue alors suivant la formule:                       |"
         << "\n# |   K(T,P) = G0*((4.0/3.0*PI*N0)**(1/3)                                            |"
         << "\n# |         *exp(- Ustar/(R*(Tcorr - Tinf)))*exp(- Kg/(Tcorr*(Tm - Tcorr)))          |"   
         << "\n# |   avec les relations:                                                            |"
         << "\n# |       DTcorr_Pres = P*(alphaP + betaP*P);                                        |"
         << "\n# |       Tcorr       = T - DTcorr_Pres + 273.;                                      |"
         << "\n# |       N0          = exp(N01*(Tm - Tcorr) + N02);                                 |"
         << "\n# |   et:  P la pression relative, T la temperature celsius                          |"
         << "\n# -----------------------------------------------------------------------------------";
         
    sort << "\n#  definition des coefficients, ils peuvent être donnés dans un ordre quelconque et "
         << "\n#  sur une ou plusieurs lignes "
         << "\n\n  coefficientsLoiHoffman_ "
         << "\n     n= "      << n      << " G0= "    << G0    << " N01= " << N01 << " N02= " << N02 << " "
         << "\n     Ustar= "  << Ustar  << " Tinf= "  << Tinf  << " Kg= "  << Kg  << " Tm= "  << Tm << " "
         << "\n     alphaP= " << alphaP << " betaP= " << betaP << " X_inf= " << X_inf << " " ;

    sort << "\n     fin_coefficientsLoiHoffman_       \n "; 
    sort << "\n#   la derniere ligne doit contenir uniquement le mot cle:     fin_coefficientsLoiHoffman_ "
         << endl;     
  };  		  	  

// calcul de la fonction K(T,P)
double  Hoffman1::fct_KT(const double& P, const double& T ) const
	{ // calcul des variables intermédiaires
	  double DTcorr_Pres = P*(alphaP + betaP*P);
	  double Tcorr       = T - DTcorr_Pres + 273.;
	  double N0          = exp(N01*(Tm - Tcorr) + N02);
	  // calcul de KT
	  double KT = G0*( pow((4.0/3.0*ConstMath::Pi*N0),1./3.)) 
                    * exp(- Ustar/(ConstPhysico::R *(Tcorr - Tinf))) * exp(- Kg/(Tcorr*(Tm - Tcorr)));
      // retour
      return KT;              
	};
	
// calcul du taux de cristalinité
// P_t, P : pression à t et, t+deltat (= actuelle)
// T_t, T : température à t et, t+deltat (=actuelle)
double  Hoffman1::Cristalinite(const double& P_t, const double& T_t,
                       CristaliniteAbstraite::SaveCrista * save_donTP
                       ,const double& P, const double& T,Enum_dure temps)
  { saveCrista =  save_donTP; // mise à jour au cas où des méthodes attachées utiliseraient le pointeur de la 
                              // classe générique
    Hoffman1::SaveCrista_Hoffman1* saveCristaHoffman1 = ((Hoffman1::SaveCrista_Hoffman1*) save_donTP);
    
    // on calcul par intégration avec la formule de Nakamura, la valeur de la cristalinité courante
    // pour cela on considère une intégration explicite en 1 pas de temps à partir de la forme différentielle
    // de l'équation cf. formule 9.12 de la thèse de Mikaël, page 139
    // on pourrait également utiliser une méthode de Runge, ce qui est possible si l'on veut plus de précision
//    double cris = saveCristaHoffman1->I_Kcinetique_t; // pour simplifier
    // recup de l'incrément de temps
	double deltat=ParaGlob::Variables_de_temps().IncreTempsCourant();
	double temps_courant = ParaGlob::Variables_de_temps().TempsCourant();

    // calcul de l'accroissement de la cristalinité sur le pas de temps par linéarisation de la forme différentielle
// non ne fonctionne pas car initialement la cristalinité = 0, et avec une formule purement explicite
// ce qui est le cas ici, la valeur du delta_cris = 0 donc la fonction n'arrive jamais à décoller !!
//    double delta_cris = deltat * (n * fct_KT(P,T) * (1.-cris)  
//                                    * pow( log( 1./(1.-cris) )
//                                           ,(n-1.)/n 
//                                          )
//                                   );
//    // mise à jour de la cristalinité
//    saveCristaHoffman1->cristalinite = cris + delta_cris;

     // utilisation d'une intégration  avec la méthode de Gauss
     // deux cas suivant que l'on calcul a t ou tdt
     double crista = 0.;
     if (temps == TEMPS_tdt)
     	{// dans ce cas on fait un calcul avec cumul à partir du temps précédent et du résultat
     	 // sauvegardé, qui est supposé à t
     	 // on renseigne les variables qui servent pour le calcul de la fonction a intégrer
     	 Te_t=T_t; Te_tdt=T;Pr_t=P_t; Pr_tdt=P;
     	 // on intègre la constante cinétique
         saveCristaHoffman1->I_Kcinetique = saveCristaHoffman1->I_Kcinetique_t +
                algo_integ ->IntegGauss(*this,&Hoffman1::fct_KT_,deltat);
//  cout << "\n I_Kcinetique= " << saveCristaHoffman1->I_Kcinetique;  //----- débug       
         // on calcul la cristalinité  
         crista = X_inf *(1.-exp(-pow(saveCristaHoffman1->I_Kcinetique,n)));    
     	}
     else // sinon pour l'instant ce n'est pas implanté, car il faut réfléchir
          // à la méthodologie d'intégration, pas difficile mais il vaut mieux le faire au moment
          // ou cela se présentera
        { cout << " \n   attention, cas non implante !!! pour l'instant (le demander si pb) "
               << " cas d'un calcul a TEMPS_t "
               << "\n Hoffman1::Cristalinite(..." ;
          Sortie(1);
        };
      
//--- debug
//  cout << fct_KT(P,T) << " ";
//  cout << delta_cris << " ";
//--- fin debug
                                                                    
    // retour
    return crista;
	};

	

// calcul du taux de cristalinité à tdt à partir des grandeurs à tdt
// P : pression à  t+deltat (= actuelle)
// T : température à  t+deltat (=actuelle)
double  Hoffman1::Cristalinite(CristaliniteAbstraite::SaveCrista * save_donTP
                         ,const double& , const double& )
  { saveCrista =  save_donTP; // mise à jour au cas où des méthodes attachées utiliseraient le pointeur de la 
                              // classe générique
    Hoffman1::SaveCrista_Hoffman1* saveCristaHoffman1 = ((Hoffman1::SaveCrista_Hoffman1*) save_donTP);
    
    // on calcul la cristalinité pour la valeur de la constante cinétique actuelle
    double crista = X_inf *(1.-exp(-pow(saveCristaHoffman1->I_Kcinetique,n)));    
                                                                    
    // retour
    return crista;
	};

//----- lecture écriture de restart spécifique aux données de la classe -----
// 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 Hoffman1::Lecture_don_base_info(istream& ent,const int cas ,LesCourbes1D& ,LesFonctions_nD& )
 { // ici on ne lit que dans le cas = 1
   if (cas == 1)
   	{ string nom;
   	  ent >> nom;
   	  if (nom != "Cristallinite_Hoffman1")
   	   { cout << "\n erreur en lecture des donnees de la loi Hoffman1, on attendait le mot cle: "
   	          << " Cristallinite_Hoffman1 et on a lu: " << nom ;
   	     Sortie(1); 
   	   };
   	  // lecture des données
      ent >> nom >> n >> nom >> G0 >> nom >> N01 >> nom >> N02;
      ent >> nom >> Ustar >> nom >> Tinf >> nom >> Kg >> nom >> Tm ;
      ent >> nom >> alphaP >> nom >> betaP >> nom >> X_inf;
   	};
 };	
 
// cas donne le niveau de sauvegarde
// = 1 : on sauvegarde tout
// = 2 : on sauvegarde uniquement les données variables (supposées comme telles)
void Hoffman1::Ecriture_don_base_info(ostream& sort ,const int cas) const 
 { // ici on ne sauvegarde que dans le cas = 1
   if (cas == 1)
   	{ sort << "\n Cristallinite_Hoffman1 "
           << "\n     n= "      << n      << " G0= "    << G0    << " N01= " << N01 << " N02= " << N02 << " "
           << "\n     Ustar= "  << Ustar  << " Tinf= "  << Tinf  << " Kg= "  << Kg  << " Tm= "  << Tm << " "
           << "\n     alphaP= " << alphaP << " betaP= " << betaP << " X_inf= " << X_inf << " " ;
   	};
 };	
    
// --------------------------- METHODES PROTEGEES : ---------------------

// calcul de la fonction K(t) en fonction d'un paramètre qui varie de -1 à 1 et des variables
// définit par ailleurs: Te_t,Te_tdt,Pr_t,Pr_tdt
double  Hoffman1::fct_KT_(const double& thetai )
  { // la pression et la température sont interpolées linéairement sur le pas de temps
    double P = 0.5 * ((1.-thetai)*Pr_t + (1.+thetai)*Pr_tdt);
    double T = 0.5 * ((1.-thetai)*Te_t + (1.+thetai)*Te_tdt);
	return fct_KT(P,T);	
//    return 0.1;  //--- débug
	};








