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

// contiend que les méthodes qui ne sont pas des templates

  #include "MathUtil.h"
  #include "ConstMath.h"

// ----- def des variables statiques ---------
    // ---- variables intermédiaires utilisées par la méthode Runge_Kutta_step23
double Algo_edp::AB1=1./6.,Algo_edp::AB2=2./3.,Algo_edp::AB3=1./6.
      ,Algo_edp::A2=1.,Algo_edp::aa2=0.5,Algo_edp::aa3=1.
      ,Algo_edp::bb21=0.5,Algo_edp::bb31=-1.,Algo_edp::bb32=2.
      ,Algo_edp::dA1=1./6,Algo_edp::dA2=-1./3.,Algo_edp::dA3=1./6.;
    // ---- variables intermédiaires utilisées par la méthode Runge_Kutta_step34
double Algo_edp::A_B1=229./1470.,Algo_edp::A_B3=1125./1813.,Algo_edp::A_B4=13718./81585.
      ,Algo_edp::A_B5=1./18.
      ,Algo_edp::A_1=79./490.,Algo_edp::A_3=2175./3626.,Algo_edp::A_4=2166./9065.
      ,Algo_edp::a_a2=2./7.,Algo_edp::a_a3=7./15.,Algo_edp::a_a4=35./38.,Algo_edp::a_a5=1.
      ,Algo_edp::b_b21=2./7.
      ,Algo_edp::b_b31=77./900.,Algo_edp::b_b32=343./900.
      ,Algo_edp::b_b41=805./1444.,Algo_edp::b_b42=-77175./54872.,Algo_edp::b_b43=97125./54872.
      ,Algo_edp::b_b51=79./490.,Algo_edp::b_b53=2175./3626.,Algo_edp::b_b54=2166./9065.
      ,Algo_edp::d_A1=0.,Algo_edp::d_A3=0.,Algo_edp::d_A4=0.,Algo_edp::d_A5=1./18.;
    // --- variables intermédiaires utilisées par la méthode Runge_Kutta_step45
double // d'abord les ai
       Algo_edp::a2=0.2,Algo_edp::a3=0.3,Algo_edp::a4=0.6,Algo_edp::a5=1.0,Algo_edp::a6=7./8.;
       // puis les bij
double       
Algo_edp::b21=0.2
,Algo_edp::b31=3.0/40.0,Algo_edp::b32=9.0/40.0
,Algo_edp::b41=0.3,Algo_edp::b42 = -0.9,Algo_edp::b43=1.2
,Algo_edp::b51 = -11.0/54.0, Algo_edp::b52=2.5,Algo_edp::b53 = -70.0/27.0,Algo_edp::b54=35.0/27.0
,Algo_edp::b61=1631.0/55296.0,Algo_edp::b62=175.0/512.0,Algo_edp::b63=575.0/13824.0
		      ,Algo_edp::b64=44275.0/110592.0,Algo_edp::b65=253.0/4096.0;
	   // puis les ci	      
double
Algo_edp::c1=37.0/378.0,Algo_edp::c3=250.0/621.0,Algo_edp::c4=125.0/594.0,Algo_edp::c6=512.0/1771.0;
       // et  c5-c_étoile_5
double Algo_edp::ce5 = -277.00/14336.0;
// initialisés dans le premier appel du construteur
double Algo_edp::ce1=0.,Algo_edp::ce3=0.,Algo_edp::ce4=0.,Algo_edp::ce6=0.;
		      
// pour la méthode Pilotage_kutta (cf.rkf45)
double Algo_edp::remin = 1.0E-12;
	      
		      
// -------- fin des variables statiques --------

    // CONSTRUCTEURS :
    // constructeur par défaut
Algo_edp::Algo_edp() :
  k2(1),k3(1),k4(1),k5(1),k6(1)//,f_inter(1)
 ,val_inter(1),der_inter(1),estime_erreur(1),coef(0.9)
 ,nb_cas_test(1),permet_affichage(0)
    { if (ce1 == 0.) // au premier appel on initialise
       {ce1=c1-2825.0/27648.0;
        ce3=c3-18575.0/48384.0;
		      ce4=c4-13525.0/55296.0;
		      ce6=c6-0.25;
       };
      if (d_A1==0.) 
      	{d_A1=A_B1-A_1;
        d_A3=A_B3-A_3;
        d_A4=A_B4-A_4;
        d_A5=A_B5;
      	};
      // initialisation des paramètres par défaut
      Init_param_val_defaut(); 
      // --- test des routines ---- (en fonctionnement normal: commenté)
//      Test_template(); 
    };
    // constructeur de copie 
Algo_edp::Algo_edp(const Algo_edp& a) :
  k2(a.k2),k3(a.k3),k4(a.k4),k5(a.k5),k6(a.k6)//,f_inter(a.f_inter)
  ,val_inter(a.val_inter),der_inter(a.der_inter),estime_erreur(a.estime_erreur),coef(a.coef)
  ,estime_prec_erreur(a.estime_prec_erreur)
  ,nb_cas_test(1),permet_affichage(0)
    { if (ce1 == 0.) // au premier appel on initialise
       {ce1=c1-2825.0/27648.0;
        ce3=c3-18575.0/48384.0;
		      ce4=c4-13525.0/55296.0;
		      ce6=c6-0.25;
       };
      // initialisation des paramètres par défaut
      Init_param_val_defaut(); 
    };
       
    // DESTRUCTEUR :
Algo_edp::~Algo_edp()
   {}
    
    // METHODES PUBLIQUES :
        // initialisation de tous les paramètres à leurs valeurs par défaut
void Algo_edp::Init_param_val_defaut()        
 {  nbMaxiAppel = 3000; }; 

// explication erreur
// affiche à l'écran l'explication de l'erreur de Pilotage_kutta
void Algo_edp::Affiche_Explication_erreur_RG(int type_erreur)
  { switch (type_erreur)
     {case 2  : cout << " calcul correct "; break;
      case 3  : cout << " la precision relative demandee est trop petite "; break;
      case 4  : cout << " nombre maxi d'appels de la fonction, atteints "; break;
      case 6  : cout << " integration impossible, due aux precisions demandees, on doit augmenter ces precisions ";break;
      case 8  : cout << " cas_kutta ne correspond pas a une routine de base existante ";break;
      case 9  : cout << " calcul correct, mais la derniere valeur a t, n'a pu etre calculee, "
                     << " c'est la valeur a t - quelque chose qui est renvoyee "; break;
      case 0  : cout << " erreur inconnue ";break;
      default : cout << " type d'erreur inconnue ";break;
     }
  };


//==================================== méthodes privées ==============================

    // on défini une fonction a intégrer
Vecteur& Algo_edp::FoncDeriv(const double & t,const Vecteur& f,Vecteur& df,int& erreur)
	{ switch (nb_cas_test)
		{case 1: 
		  	{df = f; // juste pour dimensionner
		  	 df(1)=cos(t);
      erreur = 0;
		  	}break;
		 case 2: 
		  	{df = f; // juste pour dimensionner
		  	 df(1)=cos(t);
      erreur = 0;
		  	}break; 	
		 case 3: // y" + y=0 -> y'=f, f'=-y,  solution cos 
		  	{df(1) = f(2); // juste pour dimensionner
		  	 df(2) = -f(1); 
      erreur = 0;
		  	}break; 	
		 case 4: // deux fonctions sin cos indépendantes
		  	{df(1) = cos(t); // juste pour dimensionner
		  	 df(2) = -sin(t); 
      erreur = 0;
		  	}break;
   default:
     erreur = 1;
     cout << "\n cas de test non implemente !! nb_cas_test="<<nb_cas_test
          << "\n lgo_edp::FoncDeriv(..." << endl;
     Sortie(1);
		};
	  return df;	
	};
// on défini une fonction de vérification d'intégrité
void Algo_edp::FoncVerif_fonc(const double & ,const Vecteur& f,int& erreur)
	{// ici la fonction ne vérifie quasiment rien
  switch (nb_cas_test)
		{case 1: 
		  	{erreur = 0;
		  	}break;
		 case 2: 
		  	{erreur = 0;
		  	}break; 	
		 case 3: // y" + y=0 -> y'=f, f'=-y,  solution cos 
		  	{erreur = 0;
		  	}break; 	
		 case 4: // deux fonctions sin cos indépendantes
		  	{erreur = 0;
		  	}break;
   default:
     erreur = 1;
     cout << "\n cas de test non implemente !! nb_cas_test="<<nb_cas_test
          << "\n lgo_edp::FoncVerif_fonc(..." << endl;
     Sortie(1);
		};
	};

    // méthode test
void Algo_edp::Test_template()
	{ int nb_max_test = 4; // nombre de test actuellement existant
	  // def de la taille du problème et des différentes constantes
	  int taille=0;
	  Vecteur val_initiale(0),der_initiale(0),val_finale(0),der_finale(0);
   // affichage de l'entête
   cout << "\n ----------- test de Pilotage kutta ------------- ";
   //boucle sur les différentes routines de base
   for (int cas_kutta=3;cas_kutta<=5;cas_kutta++)
	  // boucle sur les différents tests
	  for (nb_cas_test=1; nb_cas_test<= nb_max_test;nb_cas_test++)
    { cout << "\n \n    test " << nb_cas_test;
      // def de la taille
      switch (nb_cas_test)
       {case 1: case 2 : {taille = 1; break;}
        case 3: {taille = 2;break;}
        case 4: {taille = 2;break;}
       };
      // def des différentes variables
      double tdebut=0.2,tfin=3.3,erreurAbsolue=1.e-5,erreurRelative=1.e-6;
      double dernierTemps,dernierdeltat; // infos de retour
      int nombreAppelF,nb_step;          // infos de retour
      double erreur_maxi_global;         // infos de retour
      // dimensionement des vecteurs
      val_initiale.Change_taille(taille);
      der_initiale.Change_taille(taille);
      val_finale.Change_taille(taille);
      der_finale.Change_taille(taille);
      // init des valeurs initiales de la fonction
      int nb_boucle = 1; // combien de boucle on veut faire
      switch (nb_cas_test)
       {case 1:{val_initiale(1)=sin(tdebut);nb_boucle = 3;break;}
        case 2:{tfin = - tfin;
                 val_initiale(1)=sin(tdebut);break;}
        case 3:{tdebut = 0.; tfin = 3.* ConstMath::Pi;
                 val_initiale(1)=1.;val_initiale(2)=0.;
                 nb_boucle = 9;
                 break;}
        case 4:{ tdebut = 0.; tfin = 3.* ConstMath::Pi;
                 val_initiale(1)=sin(tdebut);val_initiale(2)=cos(tdebut);
                 nb_boucle = 9;
                 break;}
       };
      int erreur = 0; // init par défaut
      Algo_edp::FoncDeriv(tdebut,val_initiale,der_initiale,erreur);
      if (erreur) // gestion d'une erreur éventuelle
       { cout << "\n erreur 1 dans le calcul de FoncDeriv, le test n'est pas correct "
              << "\n Algo_edp::Test_template() " << endl;
         Sortie(1);
       };
      // premier appel directe de kutta
      double deltat = tfin-tdebut;
      switch (cas_kutta)
        {case 3: Runge_Kutta_step23(*this,& Algo_edp::FoncDeriv,& Algo_edp::FoncVerif_fonc
                                     ,val_initiale,der_initiale,tdebut,deltat
                                     ,val_finale,estime_erreur); break;
         case 4: Runge_Kutta_step34(*this,& Algo_edp::FoncDeriv,& Algo_edp::FoncVerif_fonc
                                     ,val_initiale,der_initiale,tdebut,deltat
                                     ,val_finale,estime_erreur); break;
         case 5: Runge_Kutta_step45(*this,& Algo_edp::FoncDeriv,& Algo_edp::FoncVerif_fonc
                                     ,val_initiale,der_initiale,tdebut,deltat
                                     ,val_finale,estime_erreur); break;            	
        };
      switch (nb_cas_test)
       {case 1:
          {cout << "\n --- appel directe de kutta test 1 --- "
                << "\n valeur finale "<< val_finale(1) << " exacte " << sin(tfin)
                << " erreur " << (val_finale(1)-sin(tfin)) << " estime " << estime_erreur(1);
           break;
           }
        case 2:
          {cout << "\n --- appel directe de kutta test 2 --- "
                << "\n valeur finale "<< val_finale(1) << " exacte " << sin(tfin)
                << " erreur " << (val_finale(1)-sin(tfin)) << " estime " << estime_erreur(1);
           break;
           }
       };
      // appel de la fonction maitre
      double tdeb=tdebut; double intertemps=(tfin-tdebut)/nb_boucle;
      double tfi=tdeb;
     
      for (int iboucle = 1; iboucle <= nb_boucle; iboucle++)		
       { tdeb=tfi; tfi+=intertemps;
         Algo_edp::FoncDeriv(tdeb,val_initiale,der_initiale,erreur);
         if (erreur) // gestion d'une erreur éventuelle
          { cout << "\n erreur dans le calcul de FoncDeriv, le test n'est pas correct "
                 << "\n Algo_edp::Test_template() " << endl;
            Sortie(1);
          };
         int conver=this->Pilotage_kutta
                        (cas_kutta,*this,& Algo_edp::FoncDeriv,& Algo_edp::FoncVerif_fonc
                                  ,val_initiale,der_initiale
                                  ,tdeb,tfi,erreurAbsolue,erreurRelative
                                  ,val_finale,der_finale,dernierTemps,dernierdeltat
                                  ,nombreAppelF,nb_step,erreur_maxi_global);
         // affichage des informations
         switch (nb_cas_test)
          {case 1:
             {cout << "\n iter= " << iboucle << " valeur finale "<< val_finale(1) << " exacte " << sin(tfi)
                   << " erreur " << (val_finale(1)-sin(tfi)) << " estime " << erreur_maxi_global
                   << " nb step " << nb_step << " nombre d'appel " << nombreAppelF
                   << " dernier temps " << dernierTemps << " dernier deltat " << dernierdeltat;
              break;
              }
           case 2:
             {cout << "\n iter= " << iboucle << " valeur finale "<< val_finale(1) << " exacte " << sin(tfi)
                   << " erreur " << (val_finale(1)-sin(tfi)) << " estime " << erreur_maxi_global
                   << " nb step " << nb_step << " nombre d'appel " << nombreAppelF
                   << " dernier temps " << dernierTemps << " dernier deltat " << dernierdeltat;
              break;
              }
           case 3 :
             {cout << "\n iter= " << iboucle << " valfin1 "<< val_finale(1) << " exact " << cos(tfi)
                   << " valfin2 "<< val_finale(2) << " exact " << -sin(tfi)
                   << " err1 " << (val_finale(1)-cos(tfi)) 
                   << " err2 " << (val_finale(2)+sin(tfi)) << " estime " << erreur_maxi_global
                   << " nb step " << nb_step << " nbappel " << nombreAppelF
                   << " dertemps " << dernierTemps << " derdeltat " << dernierdeltat;
              break;
              }
           case 4 :  
             {cout << "\n iter= " << iboucle << " valfin1 "<< val_finale(1) << " exact " << sin(tfi)
                   << " valfin2 "<< val_finale(2) << " exact " << cos(tfi)
                   << " err1 " << (val_finale(1)-sin(tfi)) 
                   << " err2 " << (val_finale(2)-cos(tfi)) << " estime " << erreur_maxi_global
                   << " nb step " << nb_step << " nbappel " << nombreAppelF
                   << " dertemps " << dernierTemps << " derdeltat " << dernierdeltat;
              break;
              }
          };
         // prépa de la boucle suivante
         val_initiale=val_finale; der_initiale=der_finale;		
       };
		};
   // fin programmé
   Sortie(1);                    
    
	};


//**********************************************************************
//
//  Purpose:
//
//    TIMESTAMP prints the current YMDHMS date as a time stamp.
//
//  Example:
//
//    May 31 2001 09:45:54 AM
//
//  Modified:
//
//    24 September 2003
//
//  Author:
//
//    John Burkardt
//
//  Parameters:
//
//    None
//
void Algo_edp::timestamp ( void )

{
#define TIME_SIZE 40

  static char time_buffer[TIME_SIZE];
  const struct tm *tm;
  size_t len;
  time_t now;

  now = time ( NULL );
  tm = localtime ( &now );

  len = strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm );

  cout << time_buffer << "\n";

  return;
#undef TIME_SIZE
};

