619 lines
32 KiB
C++
Executable file
619 lines
32 KiB
C++
Executable file
// 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/>.
|
|
|
|
#ifndef Algo_edp_deja_inclus
|
|
#include "Algo_edp.h"
|
|
#endif
|
|
|
|
// --- include pour rkf45 ---
|
|
# include <cstdlib>
|
|
# include <iostream>
|
|
# include <iomanip>
|
|
# include <cmath>
|
|
# include <ctime>
|
|
// --- fin include pour rkf45 ---
|
|
|
|
// contient que les méthodes nécessitant les templates, qui doivent donc être
|
|
//incluses dans le .h
|
|
|
|
#ifndef Algo_edp_deja_inclus
|
|
#include "MathUtil.h"
|
|
#include "ConstMath.h"
|
|
static double remin = 1.0E-12;
|
|
|
|
|
|
// ----- cas d'un système d'edp du premier ordre---- ,
|
|
// résolution par runge kutta imbriqué --> estimation d'erreur
|
|
// il y a 3 calcul de la fonction dérivée
|
|
// 2 et 3 ième ordre imbriquée
|
|
// calcul de la solution à t+dt en fonction de la solution à t,
|
|
// en entrée:
|
|
// *Ptder_fonc : pointeur de la fonction de calcul de la dérivées
|
|
// en entrée: t et f : temps et la valeur de la fonction a t
|
|
// en sortie: df : dérivée de la fonction a t
|
|
// erreur : si diff de 0, indique qu'il y a eu une erreur
|
|
// *Ptverif_fonc : pointeur de fonction, pour tester val_finale après la prédiction explicite finale
|
|
//
|
|
// val_initiale : valeur des fonctions en t0
|
|
// der_initiale : valeur de la dérivée pour t0
|
|
// t0 : temps initiale
|
|
// deltat : incrément de temps demandé
|
|
// en sortie :
|
|
// val_finale : valeur des fonctions à tdt
|
|
// estime_erreur : estimation d'erreur pour chaque fonction
|
|
template <class T>
|
|
void Algo_edp::Runge_Kutta_step23(T& instance
|
|
,Vecteur& (T::*Ptder_fonc) (const double & t,const Vecteur& f,Vecteur& df,int& erreur)
|
|
,void (T::*Ptverif_fonc) (const double & t,const Vecteur& f,int& erreur_final)
|
|
,const Vecteur& val_initiale,const Vecteur& der_initiale
|
|
,const double& t0,const double& deltat
|
|
,Vecteur& val_finale,Vecteur& estime_erreur)
|
|
{// récup de la dimension
|
|
int taille = val_initiale.Taille();
|
|
int erreur=0; // init de l'indicateur d'erreur dans le calcul de la fonction
|
|
// vérif de la dimmension
|
|
if (taille != k2.Taille())
|
|
{ k2.Change_taille(taille);k3.Change_taille(taille);
|
|
// val_finale.Change_taille(taille);
|
|
};
|
|
// -- premier incrément
|
|
val_finale = val_initiale + (deltat * bb21) * der_initiale;
|
|
//// --- debug ---
|
|
//cout << "\n Algo_edp::Runge_Kutta_step23(.. avant appel "
|
|
// << "\n val_initiale= ";val_initiale.Affiche();
|
|
//cout << " der_initiale= "; der_initiale.Affiche();
|
|
//cout << " val_finale= "; val_finale.Affiche();
|
|
//cout << "\n t0= "<< t0 << ", deltat= "<< deltat<< ", t0+aa2*deltat= "<< (t0+aa2*deltat);
|
|
//cout << endl;
|
|
//// --- fin debug ---
|
|
// appel de la dérivée pour le premier incrément
|
|
(instance.*Ptder_fonc) (t0+aa2*deltat,val_finale,k2,erreur); k2 *= deltat;
|
|
//// --- debug ---
|
|
//cout << "\n Algo_edp::Runge_Kutta_step23(.. après appel "
|
|
// << "\n k2= ";k2.Affiche();
|
|
//cout << endl;
|
|
//// --- fin debug ---
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// -- deuxième incrément
|
|
val_finale = val_initiale + (deltat*bb31) * der_initiale + bb32 * k2;
|
|
// appel de la dérivée pour le deuxième incrément (aa3=1.)
|
|
(instance.*Ptder_fonc) (t0+deltat,val_finale,k3,erreur); k3 *= deltat;
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
|
|
// somme pondéré des incréments
|
|
val_finale = val_initiale + (deltat*AB1)*der_initiale+AB2*k2+AB3*k3;
|
|
// estimation de l'erreur comme différence entre les méthodes du quatrième
|
|
// et cinquième ordre
|
|
estime_erreur = (deltat*dA1)*der_initiale+dA2*k2+dA3*k3;
|
|
// on vérifie que la solution finale est licite
|
|
(instance.*Ptverif_fonc) (t0+deltat,val_finale,erreur);
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
|
|
//// --- debug ---
|
|
//cout << "\n Algo_edp::Runge_Kutta_step23(.. à la fin, tout c'est bien passée "
|
|
// << "\n val_finale= ";val_finale.Affiche();
|
|
//cout << " estime_erreur= "; estime_erreur.Affiche();
|
|
//cout << "\n t0+deltat= "<< t0+deltat ;
|
|
//cout << endl;
|
|
//// --- fin debug ---
|
|
}
|
|
|
|
// résolution par runge kutta imbriqué --> estimation d'erreur
|
|
// il y a 4 calcul de la fonction dérivée
|
|
// 3 et 4 ième ordre imbriquée (Fehlberg)
|
|
// calcul de la solution à t+dt en fonction de la solution à t,
|
|
// en entrée:
|
|
// *Ptder_fonc : pointeur de la fonction de calcul de la dérivées
|
|
// en entrée: t et f : temps et la valeur de la fonction a t
|
|
// en sortie: df : dérivée de la fonction a t
|
|
// erreur : si diff de 0, indique qu'il y a eu une erreur
|
|
// *Ptverif_fonc : pointeur de fonction, pour tester val_finale après la prédiction explicite finale
|
|
//
|
|
// val_initiale : valeur des fonctions en t0
|
|
// der_initiale : valeur de la dérivée pour t0
|
|
// t0 : temps initiale
|
|
// deltat : incrément de temps demandé
|
|
// en sortie :
|
|
// val_finale : valeur des fonctions à tdt
|
|
// estime_erreur : estimation d'erreur pour chaque fonction
|
|
template <class T>
|
|
void Algo_edp::Runge_Kutta_step34(T& instance
|
|
,Vecteur& (T::*Ptder_fonc) (const double & t,const Vecteur& f,Vecteur& df,int& erreur)
|
|
,void (T::*Ptverif_fonc) (const double & t,const Vecteur& f,int& erreur_final)
|
|
,const Vecteur& val_initiale,const Vecteur& der_initiale
|
|
,const double& t0,const double& deltat
|
|
,Vecteur& val_finale,Vecteur& estime_erreur)
|
|
{// récup de la dimension
|
|
int taille = val_initiale.Taille();
|
|
int erreur=0; // init de l'indicateur d'erreur dans le calcul de la fonction
|
|
// vérif de la dimmension
|
|
if (taille != k2.Taille())
|
|
{ k2.Change_taille(taille);k3.Change_taille(taille);
|
|
k4.Change_taille(taille),k5.Change_taille(taille);
|
|
// val_finale.Change_taille(taille);
|
|
};
|
|
// -- premier incrément
|
|
val_finale = val_initiale + (deltat * b_b21) * der_initiale;
|
|
// appel de la dérivée pour le premier incrément
|
|
(instance.*Ptder_fonc) (t0+a_a2*deltat,val_finale,k2,erreur); k2 *= deltat;
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// -- deuxième incrément
|
|
val_finale = val_initiale + (deltat * b_b31) * der_initiale + b_b32* k2;
|
|
// appel de la dérivée pour le deuxième incrément
|
|
(instance.*Ptder_fonc) (t0+a_a3*deltat,val_finale,k3,erreur); k3 *= deltat;
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// -- troisième incrément
|
|
val_finale = val_initiale + (deltat*b_b41)*der_initiale+b_b42*k2+b_b43*k3;
|
|
// appel de la dérivée pour le troisième incrément
|
|
(instance.*Ptder_fonc) (t0+a_a4*deltat,val_finale,k4,erreur); k4 *= deltat;
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// -- quatrième incrément
|
|
val_finale = val_initiale + (deltat*b_b51)*der_initiale+b_b53*k3+b_b54*k4;
|
|
// appel de la dérivée pour le quatrième incrément
|
|
(instance.*Ptder_fonc) (t0+a_a5*deltat,val_finale,k5,erreur); k5 *= deltat;
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// somme pondéré des incréments
|
|
val_finale = val_initiale + (deltat*A_B1)*der_initiale+A_B3*k3+A_B4*k4+A_B5*k5;
|
|
// on vérifie que la solution finale est licite
|
|
(instance.*Ptverif_fonc) (t0+deltat,val_finale,erreur);
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// estimation de l'erreur comme différence entre les méthodes du quatrième
|
|
// et cinquième ordre
|
|
estime_erreur = (deltat*d_A1)*der_initiale+d_A3*k3+d_A4*k4+d_A5*k5;
|
|
}
|
|
|
|
// résolution par runge kutta imbriqué --> estimation d'erreur
|
|
// il y a 5 calcul de la fonction dérivée
|
|
// l'algorithme s'appuis sur la présentation de numerical recipes
|
|
// fifth-order Cash-Karp Runge-Kutta
|
|
// calcul de la solution à t+dt en fonction de la solution à t,
|
|
// en entrée:
|
|
// *Ptder_fonc : pointeur de la fonction de calcul de la dérivées
|
|
// en entrée: t et f : temps et la valeur de la fonction a t
|
|
// en sortie: df : dérivée de la fonction a t
|
|
// erreur : si diff de 0, indique qu'il y a eu une erreur
|
|
// *Ptverif_fonc : pointeur de fonction, pour tester val_finale après la prédiction explicite finale
|
|
//
|
|
// val_initiale : valeur des fonctions en t0
|
|
// der_initiale : valeur de la dérivée pour t0
|
|
// t0 : temps initiale
|
|
// deltat : incrément de temps demandé
|
|
// en sortie :
|
|
// val_finale : valeur des fonctions à tdt
|
|
// estime_erreur : estimation d'erreur pour chaque fonction
|
|
template <class T>
|
|
void Algo_edp::Runge_Kutta_step45(T& instance
|
|
,Vecteur& (T::*Ptder_fonc) (const double & t,const Vecteur& f,Vecteur& df,int& erreur)
|
|
,void (T::*Ptverif_fonc) (const double & t,const Vecteur& f,int& erreur_final)
|
|
,const Vecteur& val_initiale,const Vecteur& der_initiale
|
|
,const double& t0,const double& deltat
|
|
,Vecteur& val_finale,Vecteur& estime_erreur)
|
|
{// récup de la dimension
|
|
int taille = val_initiale.Taille();
|
|
int erreur=0; // init de l'indicateur d'erreur dans le calcul de la fonction
|
|
// vérif de la dimmension
|
|
if (taille != k2.Taille())
|
|
{ k2.Change_taille(taille);k3.Change_taille(taille);
|
|
k4.Change_taille(taille),k5.Change_taille(taille);
|
|
k6.Change_taille(taille);//f_inter.Change_taille(taille);
|
|
};
|
|
// -- premier incrément
|
|
val_finale = val_initiale + (deltat * b21) * der_initiale;
|
|
// appel de la dérivée pour le premier incrément
|
|
(instance.*Ptder_fonc) (t0+a2*deltat,val_finale,k2,erreur); k2 *= deltat;
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// -- deuxième incrément
|
|
val_finale = val_initiale + (deltat * b31) * der_initiale + b32* k2;
|
|
// appel de la dérivée pour le deuxième incrément
|
|
(instance.*Ptder_fonc) (t0+a3*deltat,val_finale,k3,erreur); k3 *= deltat;
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// -- troisième incrément
|
|
val_finale = val_initiale + (deltat*b41)*der_initiale+b42*k2+b43*k3;
|
|
// appel de la dérivée pour le troisième incrément
|
|
(instance.*Ptder_fonc) (t0+a4*deltat,val_finale,k4,erreur); k4 *= deltat;
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// -- quatrième incrément
|
|
val_finale = val_initiale + (deltat*b51)*der_initiale+b52*k2+b53*k3+b54*k4;
|
|
// appel de la dérivée pour le quatrième incrément
|
|
(instance.*Ptder_fonc) (t0+a5*deltat,val_finale,k5,erreur); k5 *= deltat;
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// -- cinquième incrément
|
|
val_finale = val_initiale + (deltat*b61)*der_initiale+b62*k2+b63*k3+b64*k4+b65*k5;
|
|
// appel de la dérivée pour le cinquième incrément
|
|
(instance.*Ptder_fonc) (t0+a6*deltat,val_finale,k6,erreur); k6 *= deltat;
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// somme pondéré des incréments
|
|
val_finale = val_initiale + (deltat*c1)*der_initiale+c3*k3+c4*k4+c6*k6;
|
|
// on vérifie que la solution finale est licite
|
|
(instance.*Ptverif_fonc) (t0+deltat,val_finale,erreur);
|
|
if (erreur) // cas où le calcul n'est pas licite
|
|
{ estime_erreur.Inita(ConstMath::tresgrand); // on indique une erreur monstrueuse
|
|
val_finale = val_initiale; // on retourne la valeur initiale
|
|
return;
|
|
};
|
|
// estimation de l'erreur comme différence entre les méthodes du quatrième
|
|
// et cinquième ordre
|
|
estime_erreur = (deltat*ce1)*der_initiale+ce3*k3+ce4*k4+ce5*k5+ce6*k6;
|
|
}
|
|
|
|
// un programme de pilotage de l'intégration d'un pas (on s'inspire du programme libre rkf45)
|
|
// on distingue une erreur globale et une erreur relative
|
|
// l'erreur réelle utilisée sur chaque step est: erreurRelative * |f| + erreurAbsolue
|
|
// ==== en entrée: =====
|
|
// cas_kutta : donne le type de routine kutta imbriqué a employer:
|
|
// =3 pour kutta23, =4 pour kutta34, =5 pour kutta45
|
|
// tdebut et tfin : temps de début et de fin du calcul, demandé
|
|
// val_initiale : valeur initiale de la fonction (donc à tdebut)
|
|
// der_initiale : dérivée initiale de la fonction (donc à tdebut)
|
|
// erreurAbsolue : erreur absolue demandée
|
|
// erreurRelative : erreur relative (à la fonction) demandée
|
|
// Ptder_fonc : pointeur de fonction
|
|
// en entrée: t et f : temps et la valeur de la fonction a t
|
|
// en sortie: df : dérivée de la fonction a t
|
|
// erreur : si diff de 0, indique qu'il y a eu une erreur
|
|
// Ptverif_fonc : pointeur de fonction, pour tester l'intégrité du résultat
|
|
// cela signifie que val_finale est testée avec cette fonction, systématiquement
|
|
// - à la fin de chaque prédiction des kuttas imbriqués
|
|
// - au retour du pilotage
|
|
// ---- en sortie: -----
|
|
// dernierTemps : temps final utilisé = tfin si calcul ok, sinon = le dernier temps calculé
|
|
// val_finale : valeur de la fonction à "dernierTemps"
|
|
// der_finale : dérivée finale
|
|
// dernierdeltat : le dernier deltat utilisé
|
|
// nombreAppelF : nombre d'appel de la fonction réellement utilisé dans le programme
|
|
// erreurRelative : si retour = 3 ==> erreur relative (à la fonction) minimum possible,
|
|
// nb_step : nombre de step utilisé pour le calcul (non compté les steps avec trop d'erreur)
|
|
// erreur_maxi_global : une approximation de l'erreur maxi global cumulant les erreurs sur tous les steps
|
|
// === en retour un entier qui donne le résultat du calcul : ====
|
|
// =2 : tout est ok : seul cas où toutes les valeurs de retour sont valides
|
|
// =3 : erreur: la précision relative demandée est trop petite, en retour la précision mini possible
|
|
// =4 : erreur: on a dépassé le nombre maxi d'appel fixé, d'où a peu près à (nombreAppelF/6) step de calcul.
|
|
// =6 : erreur: l'intégration pas possible, due aux précisions demandées, on doit augmenter ces précisions
|
|
// souvent du à une solution qui varie très rapidemment --> problème potentiel
|
|
// =8 : erreur: cas_kutta ne correspond pas à une routine de base existante
|
|
// =9 : tout c'est bien passé, mais la dernière valeur à t, n'a pu être calculée, donc c'est la valeur à t - quelque chose qui est renvoyée
|
|
// = 0 : erreur inconnue
|
|
|
|
template <class T>
|
|
int Algo_edp::Pilotage_kutta(int cas_kutta, T& instance
|
|
,Vecteur& (T::*Ptder_fonc) (const double & t,const Vecteur& f,Vecteur& df,int& erreur)
|
|
,void (T::*Ptverif_fonc) (const double & t,const Vecteur& f,int& erreur_final)
|
|
,const Vecteur& val_initiale,const Vecteur& der_initiale
|
|
,const double& tdebut,const double& tfin
|
|
,const double& erreurAbsolue, double& erreurRelative
|
|
,Vecteur& val_finale,Vecteur& der_finale
|
|
,double& dernierTemps, double& dernierdeltat,int& nombreAppelF,int& nb_step
|
|
,double& erreur_maxi_global)
|
|
{ // -- premières initialisation
|
|
nb_step=0; // compteur d'incrément
|
|
// double temps = tfin ; //******* modif 4 septembre:
|
|
double temps = tdebut;
|
|
nombreAppelF=0; // compteur global d'appel de Kutta
|
|
double deltat=tfin-tdebut;
|
|
erreur_maxi_global = 0.;
|
|
bool increment_valide=false; // pour savoir si l'exé d'un increment est valide
|
|
// -- on commence par faire quelques tests pour vérifier le contexte
|
|
// 1) vérification du niveau de la précision relative demandée
|
|
static double relerr_min = 2. * ConstMath::eps_machine * remin;
|
|
|
|
//// --- debug ---
|
|
//cout << "\n Algo_edp::Pilotage_kutta( "
|
|
// << "remin = " << remin << " eps_machine = " << ConstMath::eps_machine << " relerr_min= "
|
|
// << relerr_min << endl;
|
|
//
|
|
//// --- fin debug ---
|
|
|
|
|
|
if (erreurRelative < relerr_min)
|
|
{ if (permet_affichage > 4 )
|
|
cout << "\n Erreur: au niveau du pilotage de Runge Kutta, la precision relative demande= "
|
|
<< erreurRelative << " est inferieur a celle permise= " << relerr_min
|
|
<< "\n Algo_edp::Pilotage_kutta(..";
|
|
erreurRelative = relerr_min;
|
|
return 3; // indication au programme appelant
|
|
};
|
|
// -- récup de la dimension
|
|
int taille = val_initiale.Taille();
|
|
// vérif de la dimmension
|
|
if (taille != val_inter.Taille())
|
|
{ val_inter.Change_taille(taille);val_finale.Change_taille(taille);
|
|
der_inter.Change_taille(taille);der_finale.Change_taille(taille);
|
|
};
|
|
// -- initialisation
|
|
// -- initialisation avec sauvegarde des valeurs initiales
|
|
der_inter=der_initiale;
|
|
val_inter=val_initiale;
|
|
// def du minimum de step possible (cf. rkf45)
|
|
double mini_delta_t = 26. * ConstMath::eps_machine * MaX(Dabs(tdebut),Dabs(tfin));
|
|
// dans cette boucle l'idée est de piloter le pas de temps
|
|
while (Dabs(temps) < Dabs(tfin))
|
|
{ increment_valide = true; // par défaut on est optimiste
|
|
// on appel la routine de calcul de base,
|
|
// (val_inter, der_inter, temps et deltat : ne sont pas modifiés par le calcul)
|
|
switch (cas_kutta)
|
|
{ case 3: // cas kutta23
|
|
{this->Runge_Kutta_step23(instance,Ptder_fonc,Ptverif_fonc,val_inter,der_inter,temps,deltat
|
|
,val_finale,estime_erreur);
|
|
break;
|
|
};
|
|
case 4: // cas kutta34
|
|
{this->Runge_Kutta_step34(instance,Ptder_fonc,Ptverif_fonc,val_inter,der_inter,temps,deltat
|
|
,val_finale,estime_erreur);
|
|
break;
|
|
};
|
|
case 5: // cas kutta45
|
|
{this->Runge_Kutta_step45(instance,Ptder_fonc,Ptverif_fonc,val_inter,der_inter,temps,deltat
|
|
,val_finale,estime_erreur);
|
|
break;
|
|
};
|
|
default:
|
|
{ if (permet_affichage >4)
|
|
cout << "\n Erreur: au niveau du pilotage de Runge Kutta, le cas kutta demande= "
|
|
<< cas_kutta << " est errone ! par de routine de base de ce type (cf. doc) "
|
|
<< "\n Algo_edp::Pilotage_kutta(..";
|
|
return 8; // indication au programme appelant
|
|
};
|
|
};
|
|
nombreAppelF += cas_kutta; //5; // on incrémente le compteur d'appels
|
|
|
|
// calcul de la nouvelle dérivée initiale pour l'itération éventuelle suivante
|
|
// on le fait ici, pour vérifier à ce stade que la valeur finale et la dérivée est licite
|
|
// si ce n'est pas le cas cela veut dire que le calcul précédent n'est finalement pas bon
|
|
// cela peut-être le cas par exemple lorsque l'évolution est instable (le pas de temps deltat est trop grand)
|
|
// .. dans le cas où il y a eu une erreur dans le RKij précédent, val_finale est remis à la valeur initiale
|
|
// c-a-d à val_initiale (représenté par val_inter, dans l'appel de RKij)
|
|
int erreur_fonc=0; // initialisation
|
|
// --- debug contrôlé---
|
|
if (permet_affichage > 6)
|
|
{cout << "\n Algo_edp::Pilotage_kutta(.. juste après int erreur_fonc=0 "
|
|
<< "\n le vecteur= ";val_finale.Affiche();
|
|
cout << "\n l'erreur estime= ";estime_erreur.Affiche();
|
|
cout << endl;
|
|
};
|
|
// --- fin debug ---
|
|
// calcul de la dérivée finale, car on ne dispose pour l'instant que la la valeur finale
|
|
(instance.*Ptder_fonc) (temps,val_finale,der_finale,erreur_fonc);
|
|
// --- debug contrôlé ---
|
|
if (permet_affichage > 6)
|
|
{cout << "\n Algo_edp::Pilotage_kutta(.. appel de la fct "
|
|
<< "\n le vecteur= ";val_finale.Affiche();
|
|
cout << " sa dérivée= "; der_finale.Affiche();
|
|
cout << "\n temps= "<< temps <<" et temps finale visee= "<<(temps+deltat);
|
|
cout << endl;
|
|
};
|
|
// --- fin debug ---
|
|
nombreAppelF++; // incrémente le compteur d'appel de fonction
|
|
dernierTemps = temps; dernierdeltat=deltat; // init et en même temps sert de sauvegarde
|
|
|
|
// ... on vérifie que le nombre d'appel n'exède pas le maxi fixé
|
|
if (nombreAppelF > nbMaxiAppel)
|
|
{ if (permet_affichage >4)
|
|
cout << "\n Erreur au niveau du pilotage de Runge Kutta, le nombre maxi d'appel= "
|
|
<< nbMaxiAppel << " de la fonction est depasse "
|
|
<< "\n Algo_edp::Pilotage_kutta(..";
|
|
if (erreur_fonc) // si erreur dans le dernier calcul, cela signifie que la valeur calculée n'est pas licite
|
|
// dans ce cas on renvoie la valeur précédente
|
|
{ dernierTemps -= dernierdeltat;val_finale = val_inter;der_finale = der_inter;}
|
|
// sinon on renvoie la dernière valeur calculée, qui est licite
|
|
return 4; // indication au programme appelant
|
|
};
|
|
// ... si le dernier calcul a posé pb, la seule solution est de diminuer le pas de temps
|
|
// dans dernier calcul: on entend le calcul de la dérivée initiale et/ou la dernière résolution
|
|
// de manière arbitraire et de recommencer le calcul
|
|
if ((erreur_fonc)|| (estime_erreur.Max_val_abs() > ConstMath::grand))
|
|
{ deltat /= 2.; // on divise par deux de manière arbitraire
|
|
//// --- debug ---
|
|
//cout << "\n Algo_edp::Pilotage_kutta( debut: erreur_fonc= " << erreur_fonc << " estime_erreur.Max_val_abs()= "
|
|
// << estime_erreur.Max_val_abs() << " deltat= " << deltat;
|
|
//// --- fin debug ---
|
|
// on test néanmoins pour savoir si l'on n'est pas en dessous du delta limite inférieur
|
|
if (Dabs(deltat) < mini_delta_t )
|
|
{ if (permet_affichage > 4 )
|
|
cout << "\n Erreur1 au niveau du pilotage de Runge Kutta, le nouvelle increment qu'il faudrait utiliser = "
|
|
<< deltat << " est plus petit que le minimum autorise: " << mini_delta_t
|
|
<< "\n Algo_edp::Pilotage_kutta(..";
|
|
dernierTemps = temps - 2. * deltat;val_finale = val_inter;der_finale = der_inter;
|
|
return 6; // indication au programme appelant
|
|
};
|
|
}
|
|
else // sinon on peu continuer
|
|
{
|
|
// ... on passe en revue toutes les erreurs individuelles de chaque fonction
|
|
// pour avoir le maximum, comparé à l'erreur admise -> facteur
|
|
double facteur=0.; // facteur fonction des erreurs: pour contrôler le pas
|
|
double max_erreur_sur_step_estimee=0;
|
|
//// --- debug ---
|
|
//cout << "\n Algo_edp::Pilotage_kutta( ";
|
|
//// --- fin debug ---
|
|
|
|
for (int i=1; i<= taille; i++)
|
|
{ double erreur_admise = erreurAbsolue+Dabs(val_finale(i))*erreurRelative;
|
|
// comme on va diviser par erreur_admise, s'il est nulle on l'augmente
|
|
erreur_admise = MaX(erreur_admise,ConstMath::pasmalpetit);
|
|
facteur = MaX(facteur,Dabs(estime_erreur(i))/erreur_admise);
|
|
max_erreur_sur_step_estimee = MaX(max_erreur_sur_step_estimee,Dabs(estime_erreur(i)));
|
|
//// --- debug ---
|
|
//cout << "\n estime_erreur(i)= " << estime_erreur(i) << " erreur_admise= "<< erreur_admise
|
|
// << " max_erreur_sur_step_estimee= "<< max_erreur_sur_step_estimee;
|
|
//// --- fin debug ---
|
|
};
|
|
// on ajuste maintenant le pas en fonction du facteur qui dépend de l'erreur calculée
|
|
double AbsDeltat=Abs(deltat);
|
|
if (facteur > 1.)
|
|
{// signifie que l'erreur du calcul est supérieure à celle admise
|
|
// on doit donc diminuer le pas et recommencer le calcul
|
|
double deltat_essai=coef*AbsDeltat*pow(facteur,-0.25); // le delta essai
|
|
// on limite l'affaiblissement à 1/10 cf rkf45
|
|
deltat = DSigne(deltat)*MaX(0.1*AbsDeltat,deltat_essai);
|
|
increment_valide = false;
|
|
|
|
//// --- debug ---
|
|
//cout << "\n Algo_edp::Pilotage_kutta( "
|
|
// << "facteur = " << facteur << " max_erreur_sur_step_estimee = " << max_erreur_sur_step_estimee << " deltat_essai= "
|
|
// << deltat_essai << " deltat= "<< deltat << endl;
|
|
//
|
|
//// --- fin debug ---
|
|
|
|
// on test pour savoir si l'on n'est pas en dessous du delta limite inférieur
|
|
if (Dabs(deltat) < mini_delta_t )
|
|
{ if (permet_affichage > 4 )
|
|
cout << "\n Erreur2 au niveau du pilotage de Runge Kutta, le nouvelle increment qu'il faudrait utiliser = "
|
|
<< deltat << " est plus petit que le minimum autorise: " << mini_delta_t
|
|
<< "\n Algo_edp::Pilotage_kutta(..";
|
|
if (permet_affichage > 5 )
|
|
cout << "\n Algo_edp::Pilotage_kutta( "
|
|
<< "facteur = " << facteur << " max_erreur_sur_step_estimee = " << max_erreur_sur_step_estimee << " deltat_essai= "
|
|
<< deltat_essai << " deltat= "<< deltat << endl;
|
|
dernierTemps = temps;
|
|
dernierdeltat=deltat;
|
|
return 6; // indication au programme appelant
|
|
};
|
|
}
|
|
else // sinon tout est ok: pas de temps, erreur, nombres d'appels et valeurs finales
|
|
// donc cas ou le calcul à bien fonctionné
|
|
{// on met à jour le temps
|
|
temps += deltat; nb_step++;
|
|
erreur_maxi_global += max_erreur_sur_step_estimee;
|
|
//// --- debug ---
|
|
//cout << "\n Algo_edp::Pilotage_kutta( calcul validé: "
|
|
// << " erreur_maxi_global= "<<erreur_maxi_global << endl;
|
|
//
|
|
//// --- fin debug ---
|
|
if (temps == tfin)
|
|
{ // dernier calcul pour une fin qui est normale
|
|
//----------------------------- premier bloc de fin normal ---------------
|
|
// sinon ------ fin normal du calcul -------
|
|
// on renvoie la dernière valeur calculée, qui est licite
|
|
dernierTemps = temps;
|
|
nombreAppelF++;
|
|
return 2;
|
|
//----------------------------- fin du premier bloc de fin normal ---------------
|
|
};
|
|
|
|
// -- si on est trop près de la fin, on extrapole et retour
|
|
// on utilise une borne défini dans rkf45
|
|
if (Dabs(tfin-temps) <= 26.* ConstMath::eps_machine * Dabs(deltat))
|
|
{dernierTemps = temps; // dernier temps licite
|
|
temps=tfin;
|
|
der_finale = der_inter; // sauvegarde de la dernière dérivée licite, on se sert de der_finale
|
|
int erreur1=0;
|
|
(instance.*Ptder_fonc) (tfin,val_finale,der_inter,erreur1);
|
|
val_finale += (tfin-temps) * der_inter; // euler
|
|
if (erreur1)// il y a eu erreur, donc on récupère la dernière dérivée licite
|
|
der_inter = der_finale;
|
|
int erreur2=0;
|
|
(instance.*Ptder_fonc) (tfin,val_finale,der_finale,erreur2);
|
|
// on regarde s'il y a eu une erreur
|
|
if (erreur1 || erreur2) // si erreur, cela signifie que la dernière valeur calculée n'est pas licite
|
|
// dans ce cas on renvoie la valeur précédente
|
|
{ val_finale = val_inter;der_finale = der_inter;
|
|
return 9;
|
|
}
|
|
//----------------------------- deuxième bloc de fin normal ---------------
|
|
else
|
|
{ nombreAppelF +=2; dernierTemps = temps; dernierdeltat=deltat;
|
|
return 2;
|
|
};
|
|
//----------------------------- fin du deuxième bloc de fin normal ---------------
|
|
};
|
|
// sinon on peut au contraire augmenter le pas
|
|
double deltat_essai=coef*AbsDeltat*pow(facteur,-0.2);
|
|
// on limite l'augmentation à 5 fois le pas initial
|
|
deltat = DSigne(deltat)*MiN(MiN(5.*AbsDeltat,deltat_essai),Dabs(tfin-temps));
|
|
//// --- debug ---
|
|
//cout << "\n Algo_edp::Pilotage_kutta( *** augmentation du pas de temps éventuelle \n "
|
|
// << " deltat_essai= "
|
|
// << deltat_essai << " deltat= "<< deltat << " erreur_maxi_global= "<<erreur_maxi_global << endl;
|
|
//
|
|
//// --- fin debug ---
|
|
};
|
|
};
|
|
// on initialise l'appel du runge qui va suivre
|
|
if (increment_valide) // uniquement si l'incrément a été validé
|
|
{ val_inter = val_finale;
|
|
der_inter = der_finale;
|
|
};
|
|
}; // -- fin du while
|
|
// pour faire taire le compilo car on ne doit jamais passer ici
|
|
return 0; // erreur inconnue
|
|
}
|
|
|
|
|
|
#endif
|