2021-09-23 11:21:15 +02:00
|
|
|
// FICHIER : Hysteresis1D_2.cc
|
|
|
|
// CLASSE : Hysteresis1D
|
|
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
//
|
2023-05-03 17:23:49 +02:00
|
|
|
// Copyright (C) 1997-2022 Université Bretagne Sud (France)
|
2021-09-23 11:21:15 +02:00
|
|
|
// 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 <math.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "Sortie.h"
|
|
|
|
#include "ConstMath.h"
|
|
|
|
#include "ExceptionsLoiComp.h"
|
|
|
|
|
|
|
|
#include "Hysteresis1D.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// calcul de l'expression permettant d'obtenir la dérivée temporelle de la contrainte
|
|
|
|
// en fait il s'agit de l'équation constitutive
|
|
|
|
// utilisée dans la résolution explicite (runge par exemple) de l'équation constitutive
|
|
|
|
// erreur : =0: le calcul est licite, si diff de 0, indique qu'il y a eu une erreur
|
|
|
|
// =1: la norme de sigma est supérieure à la valeur limite de saturation
|
|
|
|
Vecteur& Hysteresis1D::Sigma_point(const double & tau, const Vecteur & sigma_tau
|
|
|
|
, Vecteur& sig_point,int & erreur)
|
|
|
|
{ // récup de la contrainte à tau
|
|
|
|
double titi= tau; // sert à rien, c'est pour taire le compilo car tau ne sert pas directement
|
|
|
|
sigma_tauBH.Coor(1,1) = sigma_tau(1);
|
|
|
|
//modif : 1 juin 2014
|
|
|
|
// non on fait la vérif sur le delta cf. plus bas
|
|
|
|
// // on vérifie que l'amplitude de la contrainte transmise, n'est pas supérieure à la saturation
|
|
|
|
// double sigma_tau_II = sigma_tauBH.II();
|
|
|
|
// if ( sigma_tau_II > Qzero*Qzero) {erreur = 1; }
|
|
|
|
// else { erreur = 0;};
|
|
|
|
// variation de sigma de R à tau
|
|
|
|
delta_sigma_barre_R_a_tauBH = sigma_tauBH - sigma_barre_BH_R;
|
|
|
|
double deux_xmu = 2. * xmu; // pour simplifier
|
|
|
|
// calcul de QdeltaSigma
|
|
|
|
double QdeltaSigma = sqrt((delta_sigma_barre_R_a_tauBH * delta_sigma_barre_R_a_tauBH).Trace());
|
|
|
|
|
|
|
|
if ( QdeltaSigma > (Qzero+ConstMath::unpeupetit)) {erreur = 1; }
|
|
|
|
else { erreur = 0;};
|
|
|
|
|
|
|
|
// calcul de Beta
|
|
|
|
double unsurwprimeQ0_puiss_np = 1./pow(wprime*Qzero,xnp);
|
|
|
|
double beta;
|
|
|
|
if (xnp == 2.)
|
|
|
|
{beta = - deux_xmu * unsurwprimeQ0_puiss_np ;}
|
|
|
|
else
|
|
|
|
{double puiss = pow(QdeltaSigma,2.-xnp);
|
|
|
|
if (Dabs(puiss) <= ConstMath::pasmalpetit) puiss = Signe(puiss,ConstMath::pasmalpetit);
|
|
|
|
beta = - deux_xmu * unsurwprimeQ0_puiss_np / puiss;
|
|
|
|
};
|
|
|
|
// calcul de phi
|
|
|
|
// il n'y a pas - QdeltaSigma * QdeltaSigma * wprime_point / (2.*xmu * wprime)
|
|
|
|
// car on est en chargement radial, donc wprime_point = 0
|
|
|
|
double phitau = delta_sigma_barre_R_a_tauBH && delta_barre_epsBH ;
|
|
|
|
// -- calcul de la dérivée temporelle de la contrainte
|
|
|
|
// on considère que D est constant = delta_barre_epsBH/deltat et comme deltat = 1
|
|
|
|
// ==> = delta_barre_epsBH
|
|
|
|
// variables intermédiaires pour les tests
|
|
|
|
betaphideltasigHB = beta * phitau * delta_sigma_barre_R_a_tauBH;
|
|
|
|
deuxmudeltaepsHB = deux_xmu * delta_barre_epsBH;
|
|
|
|
// formule normale : sigma_pointBH = deux_xmu * delta_barre_epsBH + beta * phitau * delta_sigma_barre_R_a_tauBH;
|
|
|
|
sigma_pointBH = deuxmudeltaepsHB + betaphideltasigHB;
|
|
|
|
// on limite la variation de la dérivée de la contrainte
|
|
|
|
if (deuxmudeltaepsHB(1,1) > 0.)
|
|
|
|
{// cas d'une contrainte positive et normalement la dérivée doit évoluer entre 2mu et 0 (saturation)
|
|
|
|
if (sigma_pointBH(1,1) < 0. ) sigma_pointBH.Coor(1,1) = 0.;
|
|
|
|
if (sigma_pointBH(1,1) > deuxmudeltaepsHB(1,1)) sigma_pointBH.Coor(1,1) = deuxmudeltaepsHB(1,1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{// cas d'une contrainte négative et normalement la dérivée doit évoluer entre -2mu et 0 (saturation)
|
|
|
|
if (sigma_pointBH(1,1) < deuxmudeltaepsHB(1,1) ) sigma_pointBH.Coor(1,1) = deuxmudeltaepsHB(1,1);
|
|
|
|
if (sigma_pointBH(1,1) > 0.) sigma_pointBH.Coor(1,1) = 0.;
|
|
|
|
};
|
|
|
|
// retour de la dérivée temporelle de la contrainte
|
|
|
|
sig_point(1) = sigma_pointBH(1,1);
|
|
|
|
return sig_point;
|
|
|
|
};
|
|
|
|
|
|
|
|
// vérification de l'intégrité du sigma calculé
|
|
|
|
// erreur : =0: le calcul est licite, si diff de 0, indique qu'il y a eu une erreur
|
|
|
|
// =1: la norme de sigma est supérieure à la valeur limite de saturation
|
|
|
|
void Hysteresis1D::Verif_integrite_Sigma(const double & , const Vecteur & sigma_tau,int & erreur)
|
|
|
|
{ // récup de la contrainte à tau
|
|
|
|
sigma_tauBH.Coor(1,1) = sigma_tau(1);
|
|
|
|
//modif : 1 juin 2014
|
|
|
|
// non on fait la vérif sur le delta cf. plus bas
|
|
|
|
// // on vérifie que l'amplitude de la contrainte transmise, n'est pas supérieure à la saturation
|
|
|
|
// double sigma_tau_II = sigma_tauBH.II();
|
|
|
|
// if ( sigma_tau_II > Qzero*Qzero) {erreur = 1; }
|
|
|
|
// else { erreur = 0;};
|
|
|
|
// variation de sigma de R à tau
|
|
|
|
delta_sigma_barre_R_a_tauBH = sigma_tauBH - sigma_barre_BH_R;
|
|
|
|
// calcul de QdeltaSigma
|
|
|
|
double QdeltaSigma = sqrt((delta_sigma_barre_R_a_tauBH * delta_sigma_barre_R_a_tauBH).Trace());
|
|
|
|
|
|
|
|
if ( QdeltaSigma > (Qzero+ConstMath::unpeupetit)) {erreur = 1; }
|
|
|
|
else { erreur = 0;};
|
|
|
|
};
|
|
|
|
|
|
|
|
// méthode permettant le calcul de sigma à tdt par différente méthodes: linéarisation
|
|
|
|
// ou kutta
|
|
|
|
// en sortie calcul de :
|
|
|
|
// sigma_t_barre_tdt, delta_sigma_barre_tdt_BH
|
|
|
|
void Hysteresis1D::CalculContrainte_tdt(Tableau<double>& indicateurs_resolution)
|
|
|
|
{// le calcul de la contrainte s'effectue par la résolution de l'équation différentielle
|
|
|
|
// du schéma constitutif
|
|
|
|
// sigma_point = 2*mu*D_barre + beta*phi*(delta_barre de t à R de Sigma)
|
|
|
|
|
|
|
|
if ((sortie_post)&&(indicateurs_resolution.Taille()!= 5)) // dimensionnement éventuelle de la sortie d'indicateurs
|
|
|
|
indicateurs_resolution.Change_taille(5);
|
|
|
|
|
|
|
|
//-- choix de la méthode
|
|
|
|
switch (type_resolution_equa_constitutive)
|
|
|
|
{case 1: // cas de la linéarisation de l'équation
|
|
|
|
{// ----- pour ce faire on appelle une methode de recherche de zero
|
|
|
|
Vecteur val_initiale(1); // on démarre la recherche à la valeur à t
|
|
|
|
Vecteur racine(1); // dimensionnement init du résultat à 0.
|
|
|
|
Mat_pleine der_at_racine(1,1); // dimensionnement et init de la matrice dérivée à 0.
|
|
|
|
// comme la matrice n'est pas forcément définit positive on utilise CRAMER
|
|
|
|
der_at_racine.Change_Choix_resolution(CRAMER,RIEN_PRECONDITIONNEMENT);
|
|
|
|
// 1== résolution de l'équation constitutive d'avancement discrétisée en euler implicite
|
|
|
|
int nb_incr_total,nb_iter_total; // variables intermédiaires d'indicateurs, pas utilisées pour l'instant
|
|
|
|
bool conver=alg_zero.Newton_raphson
|
|
|
|
(*this,&Hysteresis1D::Residu_constitutif,&Hysteresis1D::Mat_tangente_constitutif
|
|
|
|
,val_initiale,racine,der_at_racine,nb_incr_total,nb_iter_total
|
|
|
|
,maxi_delta_var_sig_sur_iter_pour_Newton);
|
|
|
|
if(sortie_post) // sauvegarde éventuelle des indicateurs
|
|
|
|
{indicateurs_resolution(1)+=nb_incr_total;indicateurs_resolution(2)+=nb_iter_total;
|
|
|
|
};
|
|
|
|
if (!conver)
|
|
|
|
{ cout << "\n non convergence sur l'algo de la resolution du schema constitutif"
|
|
|
|
<< "\n Hysteresis1D::CalculContrainte_tdt (...";
|
|
|
|
Sortie(1);
|
|
|
|
};
|
|
|
|
delta_sigma_barre_tdt_BH.Coor(1,1)=racine(1); // récup de la solution
|
|
|
|
sigma_t_barre_tdt = sigma_i_barre_BH + delta_sigma_barre_tdt_BH;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2: // cas d'une résolution par intégration explicite par du kutta
|
|
|
|
{// def des variables de calcul (peut-être ensuite à mettre dans le dimensionnement global)
|
|
|
|
Vecteur sig_initiale(1),dersig_initiale(1);
|
|
|
|
sig_initiale(1)=sigma_i_barre_BH(1,1);
|
|
|
|
double tdeb=0.,tfi=1.;
|
|
|
|
// calcul de la dérivée initiale
|
|
|
|
int erreur=0; //init d'une erreur de calcul de Sigma_point
|
|
|
|
Sigma_point(tdeb,sig_initiale,dersig_initiale,erreur);
|
|
|
|
if (erreur) // cas où le calcul de la dérivée initiale n'est pas possible, on ne peut pas aller plus loin
|
2023-05-03 17:23:49 +02:00
|
|
|
{ if (Permet_affichage() > 0)
|
2021-09-23 11:21:15 +02:00
|
|
|
cout << "\n erreur dans l'algo de la resolution du schema constitutif"
|
|
|
|
<< " au niveau du calcul de la derivee initiale "
|
|
|
|
<< "\n Hysteresis1D::CalculContrainte_tdt() (..." << endl;
|
|
|
|
// on génère une exception
|
|
|
|
throw ErrNonConvergence_loiDeComportement();break;
|
|
|
|
};
|
|
|
|
|
|
|
|
Vecteur sig_finale(1),dersig_finale(1);
|
|
|
|
double dernierTemps=0.,dernierdeltat=0.; // valeurs de retour
|
|
|
|
int nombreAppelF=0,nb_step=0; // " "
|
|
|
|
double erreur_maxi_global=0.; // "
|
|
|
|
|
|
|
|
// appel de la fonction kutta
|
|
|
|
int conver=alg_edp.Pilotage_kutta
|
|
|
|
(cas_kutta,*this,& Hysteresis1D::Sigma_point,& Hysteresis1D::Verif_integrite_Sigma
|
|
|
|
,sig_initiale,dersig_initiale
|
|
|
|
,tdeb,tfi,erreurAbsolue,erreurRelative
|
|
|
|
,sig_finale,dersig_finale,dernierTemps,dernierdeltat
|
|
|
|
,nombreAppelF,nb_step,erreur_maxi_global);
|
|
|
|
if(sortie_post) // sauvegarde éventuelle des indicateurs
|
|
|
|
{indicateurs_resolution(3)+=nombreAppelF;indicateurs_resolution(4)+=nb_step;
|
|
|
|
indicateurs_resolution(5)+=erreur_maxi_global;
|
|
|
|
};
|
|
|
|
// gestion de l'erreur de retour
|
|
|
|
if (conver !=2)
|
|
|
|
{ // on appel kutta45 sans gestion d'erreur !!
|
|
|
|
double deltat=tfi-tdeb;
|
|
|
|
Vecteur estime_erreur(1);
|
|
|
|
alg_edp.Runge_Kutta_step45(*this,& Hysteresis1D::Sigma_point,& Hysteresis1D::Verif_integrite_Sigma
|
|
|
|
,sig_initiale,dersig_initiale
|
|
|
|
,tdeb,deltat,sig_finale,estime_erreur);
|
|
|
|
if (estime_erreur(1) >= ConstMath::tresgrand)
|
|
|
|
{ // là on ne peur rien faire
|
2023-05-03 17:23:49 +02:00
|
|
|
if (Permet_affichage() > 0)
|
2021-09-23 11:21:15 +02:00
|
|
|
cout << "\n erreur fatale dans l'algo de la resolution du schema constitutif"
|
|
|
|
<< " au niveau de l'appel directe de calcul de alg_edp.Runge_Kutta_step45 "
|
|
|
|
<< "\n Hysteresis1D::CalculContrainte_tdt() (..." << endl;
|
|
|
|
// on génère une exception
|
|
|
|
throw ErrNonConvergence_loiDeComportement();break;
|
|
|
|
}
|
2023-05-03 17:23:49 +02:00
|
|
|
else if (Permet_affichage() > 4)
|
2021-09-23 11:21:15 +02:00
|
|
|
{cout << "\n erreur dans la resolution de l'equation constitutive avec Runge Kutta"
|
|
|
|
<< " indication de retour = " << conver << "appel direct de kutta45-> erreur estimee= "
|
|
|
|
<< estime_erreur(1)
|
|
|
|
<< "\n Hysteresis1D::CalculContrainte_tdt (...";
|
|
|
|
// Sortie(1);
|
|
|
|
// pour le debug
|
|
|
|
// if ((Dabs(estime_erreur(1)) > 10) || (Dabs(sig_finale(1)) > 10))
|
|
|
|
// alg_edp.Runge_Kutta_step45(*this,& Hysteresis1D::Sigma_point,sig_initiale,dersig_initiale
|
|
|
|
// ,tdeb,deltat,sig_finale,estime_erreur);
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
// récup des résultats
|
|
|
|
sigma_t_barre_tdt.Coor(1,1)=sig_finale(1);
|
|
|
|
delta_sigma_barre_tdt_BH = sigma_t_barre_tdt - sigma_i_barre_BH;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
// =============== fonction protegee ============
|
|
|
|
|
|
|
|
|
|
|
|
// calcul de la fonction résidu de la résolution de l'équation constitutive
|
|
|
|
// l'argument test ramène
|
|
|
|
// . 1 si le calcul a été ok, -1 s'il y a eu un pb, mais on peut continuer, 0 s'il y a eu un pb
|
|
|
|
// fatal, qui invalide le calcul du résidu
|
|
|
|
Vecteur& Hysteresis1D::Residu_constitutif (const double & alpha,const Vecteur & x, int& test)
|
|
|
|
{ // tout d'abord on calcul le vecteur sigma
|
|
|
|
rdelta_sigma_barre_tdt_BH.Coor(1,1) = x(1); // accroissement de t à tdt
|
|
|
|
// variation de sigma de R à tdt
|
|
|
|
rdelta_sigma_barre_BH_Ratdt = sigma_i_barre_BH + rdelta_sigma_barre_tdt_BH - sigma_barre_BH_R;
|
|
|
|
double deux_xmu = 2. * xmu; // pour simplifier
|
|
|
|
delta_barre_alpha_epsBH = alpha * delta_barre_epsBH; // limitation de la charge
|
|
|
|
// calcul de QdeltaSigma
|
|
|
|
double QdeltaSigma = sqrt((rdelta_sigma_barre_BH_Ratdt * rdelta_sigma_barre_BH_Ratdt).Trace());
|
|
|
|
// calcul de Beta
|
|
|
|
double unsurwprimeQ0_puiss_np = 1./pow(wprime*Qzero,xnp);
|
|
|
|
double beta;
|
|
|
|
if (xnp == 2.)
|
|
|
|
{beta = - deux_xmu * unsurwprimeQ0_puiss_np ;}
|
|
|
|
else
|
|
|
|
{double puiss = pow(QdeltaSigma,2.-xnp);
|
|
|
|
if (Dabs(puiss) <= ConstMath::pasmalpetit) puiss = Signe(puiss,ConstMath::pasmalpetit);
|
|
|
|
beta = - deux_xmu * unsurwprimeQ0_puiss_np / puiss;
|
|
|
|
};
|
|
|
|
// switch (cas_prager)
|
|
|
|
// { case 1: beta = - deux_xmu * unsurwprimeQ0_puiss_np ; break;
|
|
|
|
// case 2: { double puiss = pow(QdeltaSigma,2.-xnp);
|
|
|
|
// if (Dabs(puiss) <= ConstMath::pasmalpetit) puiss = Signe(puiss,ConstMath::pasmalpetit);
|
|
|
|
// beta = - deux_xmu * unsurwprimeQ0_puiss_np / puiss; break;}
|
|
|
|
// case 3: beta = - deux_xmu * unsurwprimeQ0_puiss_np / pow(QdeltaSigma,2.-xnp); break;
|
|
|
|
// }
|
|
|
|
// calcul de phidt
|
|
|
|
double phidt = rdelta_sigma_barre_BH_Ratdt && delta_barre_alpha_epsBH;
|
|
|
|
// calcul du résidu
|
|
|
|
residuBH = rdelta_sigma_barre_tdt_BH
|
|
|
|
- deux_xmu * delta_barre_alpha_epsBH - beta * phidt * rdelta_sigma_barre_BH_Ratdt;
|
|
|
|
// a priori ici on n'envisage pas de pb de calcul (pour l'instant)
|
|
|
|
test = 1;
|
|
|
|
// retour du résidu
|
|
|
|
residu(1) = residuBH(1,1);
|
|
|
|
return residu;
|
|
|
|
};
|
|
|
|
|
|
|
|
// calcul de la matrice tangente de la résolution de l'équation constitutive
|
|
|
|
// . 1 si le calcul a été ok, -1 s'il y a eu un pb, mais on peut continuer, 0 s'il y a eu un pb
|
|
|
|
// fatal, qui invalide le calcul du résidu et de la dérivée
|
|
|
|
Mat_abstraite& Hysteresis1D::Mat_tangente_constitutif
|
|
|
|
(const double & alpha,const Vecteur & x, Vecteur& residu, int& test)
|
|
|
|
{ // récupération du vecteur delta_sigma
|
|
|
|
rdelta_sigma_barre_tdt_BH.Coor(1,1) = x(1);
|
|
|
|
rdelta_sigma_barre_BH_Ratdt = sigma_i_barre_BH + rdelta_sigma_barre_tdt_BH - sigma_barre_BH_R;
|
|
|
|
double deux_xmu = 2. * xmu; // pour simplifier
|
|
|
|
delta_barre_alpha_epsBH = alpha * delta_barre_epsBH; // limitation de la charge
|
|
|
|
// calcul de QdeltaSigma
|
|
|
|
double QdeltaSigma = sqrt((rdelta_sigma_barre_BH_Ratdt * rdelta_sigma_barre_BH_Ratdt).Trace());
|
|
|
|
double unsurQdeltaSigma; // on limite la valeur par exemple pour le démarrage
|
|
|
|
if ( Dabs(QdeltaSigma) > ConstMath::pasmalpetit) {unsurQdeltaSigma=1./QdeltaSigma;}
|
|
|
|
else unsurQdeltaSigma = ConstMath::grand;
|
|
|
|
// calcul de Beta
|
|
|
|
double unsurwprimeQ0_puiss_np = 1./pow(wprime*Qzero,xnp);
|
|
|
|
double beta;
|
|
|
|
if (xnp == 2.)
|
|
|
|
{beta = - deux_xmu * unsurwprimeQ0_puiss_np ;}
|
|
|
|
else
|
|
|
|
{double puiss = pow(QdeltaSigma,2.-xnp);
|
|
|
|
if (Dabs(puiss) <= ConstMath::pasmalpetit) puiss = Signe(puiss,ConstMath::pasmalpetit);
|
|
|
|
beta = - deux_xmu * unsurwprimeQ0_puiss_np / puiss;
|
|
|
|
};
|
|
|
|
// switch (cas_prager)
|
|
|
|
// { case 1: beta = - deux_xmu * unsurwprimeQ0_puiss_np ; break;
|
|
|
|
// case 2: { double puiss = pow(QdeltaSigma,2.-xnp);
|
|
|
|
// if (Dabs(puiss) <= ConstMath::pasmalpetit) puiss = Signe(puiss,ConstMath::pasmalpetit);
|
|
|
|
// beta = - deux_xmu * unsurwprimeQ0_puiss_np / puiss; break;}
|
|
|
|
// case 3: beta = - deux_xmu * unsurwprimeQ0_puiss_np / pow(QdeltaSigma,2.-xnp); break;
|
|
|
|
// }
|
|
|
|
// calcul de phidt
|
|
|
|
double phidt = rdelta_sigma_barre_BH_Ratdt && delta_barre_alpha_epsBH;
|
|
|
|
// calcul du résidu
|
|
|
|
residuBH = rdelta_sigma_barre_tdt_BH
|
|
|
|
- deux_xmu * delta_barre_alpha_epsBH - beta * phidt * rdelta_sigma_barre_BH_Ratdt;
|
|
|
|
// retour du résidu
|
|
|
|
residu(1) = residuBH(1,1);
|
|
|
|
|
|
|
|
// -- calcul de la variation du résidu
|
|
|
|
// on n'utilise pas de tenseur du quatrième ordre en 1D par simplicité: toutes les grandeurs
|
|
|
|
// = un double
|
|
|
|
double d_SigRtdt = 1.;
|
|
|
|
// variation de QdeltaSigma : rappel de la formule puis simplification
|
|
|
|
// d_QdeltaSigma = 0.5 * unsurQdeltaSigma *
|
|
|
|
// (( rdelta_sigma_barre_BH_Ratdt * d_SigRtdt + d_SigRtdt * rdelta_sigma_barre_BH_Ratdt) && IdHB1);
|
|
|
|
double d_QdeltaSigma = unsurQdeltaSigma * rdelta_sigma_barre_BH_Ratdt(1,1);
|
|
|
|
// variation de beta
|
|
|
|
double d_beta;
|
|
|
|
if (xnp == 2.)
|
|
|
|
{d_beta = 0.;}
|
|
|
|
else
|
|
|
|
{double puiss = pow(QdeltaSigma,3.-xnp);
|
|
|
|
if (Dabs(puiss) <= ConstMath::pasmalpetit) puiss = Signe(puiss,ConstMath::pasmalpetit);
|
|
|
|
d_beta = -deux_xmu * unsurwprimeQ0_puiss_np * (xnp-2.)
|
|
|
|
* d_QdeltaSigma / puiss;
|
|
|
|
};
|
|
|
|
// switch (cas_prager)
|
|
|
|
// { case 1: d_beta = 0. ; break;
|
|
|
|
// case 2: { double puiss = pow(QdeltaSigma,3.-xnp);
|
|
|
|
// if (Dabs(puiss) <= ConstMath::pasmalpetit) puiss = Signe(puiss,ConstMath::pasmalpetit);
|
|
|
|
// d_beta = -deux_xmu * unsurwprimeQ0_puiss_np * (xnp-2.)
|
|
|
|
// * d_QdeltaSigma / puiss; break;}
|
|
|
|
// case 3: { d_beta = -deux_xmu * unsurwprimeQ0_puiss_np * (xnp-2.)
|
|
|
|
// * d_QdeltaSigma * (pow(QdeltaSigma,xnp-3.)); break; }
|
|
|
|
// }
|
|
|
|
// variation de phidt
|
|
|
|
double d_phidt= d_SigRtdt * delta_barre_alpha_epsBH(1,1);
|
|
|
|
// variation du résidu
|
|
|
|
double d_res = d_SigRtdt - d_beta * phidt * rdelta_sigma_barre_BH_Ratdt(1,1)
|
|
|
|
- beta * d_phidt * rdelta_sigma_barre_BH_Ratdt(1,1)
|
|
|
|
- beta * phidt * d_SigRtdt;
|
|
|
|
// a priori ici on n'envisage pas de pb de calcul (pour l'instant)
|
|
|
|
test = 1;
|
|
|
|
// retour de la matrice tangente
|
|
|
|
derResidu(1,1)=d_res;
|
|
|
|
return derResidu;
|
|
|
|
};
|
|
|
|
|
|
|
|
// calcul de l'opérateur tangent : dsigma/depsilon
|
|
|
|
TenseurQ1geneBHBH& Hysteresis1D::Dsig_depsilon(TenseurQ1geneBHBH& dsig_deps)
|
|
|
|
{ // calcul des termes élémentaires de l'équation constitutive
|
|
|
|
double deux_xmu = 2. * xmu; // pour simplifier
|
|
|
|
// calcul de QdeltaSigma
|
|
|
|
double QdeltaSigma = sqrt((delta_sigma_barre_BH_Ratdt * delta_sigma_barre_BH_Ratdt).Trace());
|
|
|
|
double unsurQdeltaSigma; // on limite la valeur par exemple pour le démarrage
|
|
|
|
if ( Dabs(QdeltaSigma) > ConstMath::pasmalpetit) {unsurQdeltaSigma=1./QdeltaSigma;}
|
|
|
|
else unsurQdeltaSigma = ConstMath::grand;
|
|
|
|
// calcul de Beta
|
|
|
|
double unsurwprimeQ0_puiss_np = 1./pow(wprime*Qzero,xnp);
|
|
|
|
double beta;
|
|
|
|
if (xnp == 2.)
|
|
|
|
{beta = - deux_xmu * unsurwprimeQ0_puiss_np ;}
|
|
|
|
else
|
|
|
|
{double puiss = pow(QdeltaSigma,2.-xnp);
|
|
|
|
if (Dabs(puiss) <= ConstMath::pasmalpetit) puiss = Signe(puiss,ConstMath::pasmalpetit);
|
|
|
|
beta = - deux_xmu * unsurwprimeQ0_puiss_np / puiss;
|
|
|
|
};
|
|
|
|
// calcul de phidt
|
|
|
|
double phidt = delta_sigma_barre_BH_Ratdt && delta_barre_epsBH;
|
|
|
|
// ---- calcul des dérivées des termes élémentaires par rapport à sigmaij
|
|
|
|
// on n'utilise pas de tenseur du quatrième ordre en 1D par simplicité: toutes les grandeurs
|
|
|
|
// = un double
|
|
|
|
double d_SigRtdt_dsig = 1.;
|
|
|
|
// variation de QdeltaSigma : rappel de la formule puis simplification
|
|
|
|
// d_QdeltaSigma = 0.5 * unsurQdeltaSigma *
|
|
|
|
// (( rdelta_sigma_barre_BH_Ratdt * d_SigRtdt + d_SigRtdt * rdelta_sigma_barre_BH_Ratdt) && IdHB1);
|
|
|
|
double d_QdeltaSigma_dsig = unsurQdeltaSigma * delta_sigma_barre_BH_Ratdt(1,1);
|
|
|
|
// variation de beta
|
|
|
|
double d_beta_dsig;
|
|
|
|
if (xnp == 2.)
|
|
|
|
{d_beta_dsig = 0.;}
|
|
|
|
else
|
|
|
|
{double puiss = pow(QdeltaSigma,3.-xnp);
|
|
|
|
if (Dabs(puiss) <= ConstMath::pasmalpetit) puiss = Signe(puiss,ConstMath::pasmalpetit);
|
|
|
|
d_beta_dsig = -deux_xmu * unsurwprimeQ0_puiss_np * (xnp-2.)
|
|
|
|
* d_QdeltaSigma_dsig / puiss;
|
|
|
|
};
|
|
|
|
// variation de phidt
|
|
|
|
double d_phidt_dsig= d_SigRtdt_dsig * delta_barre_epsBH(1,1);
|
|
|
|
// ---- calcul du terme H
|
|
|
|
double HT = (1.- beta * phidt) * d_SigRtdt_dsig
|
|
|
|
- d_beta_dsig * phidt * delta_sigma_barre_BH_Ratdt(1,1)
|
|
|
|
- beta * d_phidt_dsig * delta_sigma_barre_BH_Ratdt(1,1);
|
|
|
|
// ---- calcul des dérivées des termes élémentaires par rapport à epsilon_kl
|
|
|
|
// ici la notion de déviateur n'exite pas: le déviateur = le tenseur
|
|
|
|
double d_delta_eps_deps = 1.;
|
|
|
|
double d_phidt_deps = delta_sigma_barre_BH_Ratdt(1,1) * d_delta_eps_deps;
|
|
|
|
// ---- calcul du terme M
|
|
|
|
double M = deux_xmu * d_delta_eps_deps - beta * delta_sigma_barre_BH_Ratdt(1,1) * d_phidt_deps;
|
|
|
|
// ----- calcul de l'opérateur tangent
|
|
|
|
double optang=M/HT;
|
|
|
|
#ifdef MISE_AU_POINT
|
|
|
|
if (Dabs(optang) <= ConstMath::trespetit)
|
|
|
|
{ cout << "\n erreur l'operateur tangent: " << optang << " , est nul !!"
|
|
|
|
<< "\n Hysteresis1D::Dsig_depsilon (... ";
|
|
|
|
Sortie(1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
dsig_deps.Change(1,1,1,1,optang);
|
|
|
|
|
|
|
|
//----- vérife avec dérivée numérique
|
|
|
|
// double divise = delta_barre_epsBH(1,1);
|
|
|
|
// if (Dabs(divise) <= ConstMath::pasmalpetit) divise = Signe(divise,ConstMath::pasmalpetit);
|
|
|
|
// double optang_num =(sigma_t_barre_tdt(1,1)-sigma_i_barre_BH(1,1))/divise;
|
|
|
|
// if (Dabs(optang_num) <= ConstMath::pasmalpetit) optang_num = Signe(optang_num,ConstMath::pasmalpetit);
|
|
|
|
//
|
|
|
|
// dsig_deps.Change(1,1,1,1,optang_num);
|
|
|
|
//
|
|
|
|
// cout << "\n optang= " << optang << " approchee= "
|
|
|
|
// << optang_num;
|
|
|
|
|
|
|
|
return dsig_deps;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// --------------- méthodes internes ---------------:
|
|
|
|
// affinage d'un point de coincidence
|
|
|
|
// ramène true si le traitement est exactement terminé, sinon false, ce qui signifie qu'il
|
|
|
|
// faut encore continuer à utiliser l'équation d'évolution
|
|
|
|
// premiere_charge : indique si c'est oui ou non une coincidence avec la première charge
|
|
|
|
// pt_sur_principal : indique si oui ou non les pointeurs iafct et iatens pointent sur les listes
|
|
|
|
// principales
|
|
|
|
// iatens_princ et iafct_princ: pointeurs sur les listes principales
|
|
|
|
bool Hysteresis1D::Coincidence(double& unSur_wprimeCarre,bool premiere_charge
|
|
|
|
,SaveResulHysteresis1D & save_resul,double& W_a
|
|
|
|
,List_io <Tenseur1BH>::iterator& iatens,List_io <double>::iterator& iafct,bool& pt_sur_principal
|
|
|
|
,List_io <Tenseur1BH>::iterator& iatens_princ,List_io <double>::iterator& iafct_princ
|
|
|
|
,double& delta_W_a)
|
|
|
|
{ // tout d'abord on vérifie que l'on n'est pas exactement à un point de
|
|
|
|
// coincidence à la précision près
|
|
|
|
double c = (W_a - delta_W_a) - *iafct;
|
|
|
|
bool coin_exacte = false; // drapeau indiquant si l'on a une coincidence exacte ou pas
|
|
|
|
// if (Abs(save_resul.fonction_aide_t - save_resul.fct_aide.front()) <= tolerance_coincidence)
|
|
|
|
if ((Abs(W_a - *iafct) <= tolerance_coincidence) || (c >= 0.)) // !!! rajoue du cas c>0 pour voir !!!!
|
|
|
|
{ if (save_resul.modif == 0)
|
|
|
|
{save_resul.modif = 1;} // on signale que c'est une coincidence
|
|
|
|
// else if (save_resul.modif == 1) // cas ou l'on suit une inversion: a priori une erreur
|
|
|
|
// modif: 6 mars 2015
|
|
|
|
else if (save_resul.modif == 2) // cas ou l'on suit une inversion
|
|
|
|
{save_resul.modif = 3;} // on signale que l'on est en mixte
|
|
|
|
// on signale qu'il y a une coincidence exacte (pour le retour de la méthode Coincidence)
|
|
|
|
save_resul.sigma_barre_BH_tdt = sigma_t_barre_tdt;
|
|
|
|
coin_exacte = true;
|
|
|
|
|
|
|
|
// a finir de modifier !!!!!!!!!!!!!!!
|
|
|
|
//cout << "\n on passe par le truc non modifié";
|
|
|
|
|
|
|
|
// save_resul.fonction_aide_tdt = W_a;
|
|
|
|
// modif: 6 mars 2015
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (premiere_charge)
|
|
|
|
{save_resul.wprime_tdt = 1.;
|
|
|
|
save_resul.fonction_aide_tdt = 0.; // La fonction initiale = 0
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{save_resul.wprime_tdt = wprime;
|
|
|
|
};
|
|
|
|
save_resul.nb_coincidence++;
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
// --- sinon on continue mais le traitement ne sera pas fini !!
|
|
|
|
|
|
|
|
// dans une première étape on tente de calculer plus précisemment le point d'inversion,
|
|
|
|
// en supposant une approximation linéaire de l'évolution de sigma sur le pas de temps:
|
|
|
|
// cf thèse nicolas: (9.56)
|
|
|
|
// avec cependant un facteur 4 qui manque sur a: la formule 9.56 est fausse
|
|
|
|
// la formule 9.55 montre qu'il faut un facteur 4
|
|
|
|
bool bon_calcul = false; // permet de savoir si le calcul précis est correcte
|
|
|
|
double a = 4. * unSur_wprimeCarre * (delta_barre_epsBH && delta_sigma_barre_tdt_BH);
|
|
|
|
double b = 2. * unSur_wprimeCarre * (delta_barre_epsBH && delta_sigma_barre_BH_Rat);
|
|
|
|
#ifdef MISE_AU_POINT
|
|
|
|
if(!premiere_charge)
|
|
|
|
if (c >= 0.) // signifie qu'au pas précédent on n'a pas bien détecté une coincidence
|
|
|
|
{ cout << "\n erreur algo coincidence : 1 "
|
|
|
|
<< "\n Hysteresis1D::Coincidence(... ";
|
|
|
|
Sortie(1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// recherche de racine
|
|
|
|
double racine1,racine2; int cas;
|
|
|
|
alg_zero.SecondDegre(a,b,c,racine1,racine2,cas);
|
|
|
|
// traitement suivant les différents cas
|
|
|
|
switch (cas)
|
|
|
|
{ case 1: // deux racines distinctes
|
|
|
|
{ // normalement les deux racines sont de signe opposé, seule la positive <=1 est recevable
|
|
|
|
if (racine1*racine2 <=0.)
|
|
|
|
{// on regarde si racine2 <= 1 ce qui est le cas normal
|
|
|
|
if (racine2 <= 1.) // racine2 est la plus grande donc positive
|
|
|
|
// cas normal, on valide la procédure
|
|
|
|
{ bon_calcul = true;}
|
|
|
|
#ifdef MISE_AU_POINT
|
|
|
|
else // sinon cela signifie que la méthode n'est pas bonne
|
|
|
|
{ cout << "\n warning algo coincidence, dans la recherche des racines de l'equa 2 degre "
|
|
|
|
<< " la seconde racine est superieur a 1"
|
|
|
|
<< "\n Hysteresis1D::Coincidence(... ";
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifdef MISE_AU_POINT
|
|
|
|
// normalement a >=0 donc avec c <0 on ne doit pas avoir 2 racines du même signe
|
|
|
|
if(!premiere_charge) // sauf dans le cas de la première charge
|
|
|
|
{ cout << "\n erreur algo coincidence, dans la recherche des racines de l'equa 2 degre "
|
|
|
|
<< " les deux racines sont du meme signe ??, a= " <<a<<" c= " << c << " racine1= "
|
|
|
|
<< racine1 << " racine2= " << racine1
|
|
|
|
<< "\n Hysteresis1D::Coincidence(... ";
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// on regarde si une des racines convient
|
|
|
|
// a priori on garde la plus grande, si les deux conviennent
|
|
|
|
double la_plus_grande = MaX(racine1,racine2);
|
|
|
|
if ((la_plus_grande>0.) && (la_plus_grande<1.))
|
|
|
|
{ racine2=la_plus_grande; bon_calcul = true;}
|
|
|
|
else // sinon on test l'autre
|
|
|
|
{ double la_plus_petite = MiN(racine1,racine2);
|
|
|
|
if ((la_plus_petite>0.) && (la_plus_petite<1.))
|
|
|
|
{ racine2=la_plus_petite; bon_calcul = true;}
|
|
|
|
// sinon rien tous les autres cas sont mauvais
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2: // deux racines identiques
|
|
|
|
{ // normalement la racine doit être comprise entre 0 et 1, sinon non recevable
|
|
|
|
if ((racine2>=0.) && (racine2 <= 1.))
|
|
|
|
// cas normal, on valide la procédure
|
|
|
|
{ bon_calcul = true;}
|
|
|
|
// tous les autres cas sont mauvais
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case -3: // une racine simple
|
|
|
|
{ // normalement la racine doit être comprise entre 0 et 1, sinon non recevable
|
|
|
|
if ((racine1>=0.) && (racine1 <= 1.))
|
|
|
|
// cas normal, on valide la procédure
|
|
|
|
{ bon_calcul = true;
|
|
|
|
racine2=racine1; // pour la suite
|
|
|
|
}
|
|
|
|
// tous les autres cas sont mauvais
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!bon_calcul)
|
|
|
|
// cas où la recherche fine avec la résolution de l'équat du second degré n'a pas marché
|
|
|
|
{ // on utilise une interpolation plus grossière à l'aide de la fonction de charge
|
|
|
|
cout << "\n on passe par l'interpolation de la fonction d'aide";
|
|
|
|
double W_t=W_a - delta_W_a;
|
|
|
|
racine2 = (*iafct - W_t) / delta_W_a;
|
|
|
|
#ifdef MISE_AU_POINT
|
|
|
|
// normalement racine2 est compris entre 0 et 1
|
|
|
|
if ( (racine2 < 0.) || (racine2 > 1.))
|
|
|
|
{ cout << "\n erreur algo coincidence, dans la recherche de racine2 a l'aide de la fonction de charge "
|
|
|
|
<< " W_ref= " <<*iafct<<" W_t= " << W_t << " delta_W_a= " << delta_W_a
|
|
|
|
<< "\n Hysteresis1D::Coincidence(... ";
|
|
|
|
Sortie(1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
// maintenant on traite les infos
|
|
|
|
// calcul de la valeur de sigma au point de coincidence, qui servira pour un nouveau calcul
|
|
|
|
sigma_i_barre_BH += racine2 * delta_sigma_barre_tdt_BH;
|
|
|
|
// calcul du reste d'incrément de déformation qu'il faut utiliser pour la suite
|
|
|
|
delta_barre_epsBH *= (1.-racine2);
|
|
|
|
// mise à jour des différents pointeurs
|
|
|
|
if (premiere_charge) // cas où l'on sait déjà que l'on a rejoint la courbe de première charge
|
|
|
|
{ wprime = 1.; unSur_wprimeCarre = 1./(wprime*wprime);
|
|
|
|
iatens = save_resul.sigma_barre_BH_R.end(); iatens--;
|
|
|
|
iafct = save_resul.fct_aide.end();iafct--;
|
|
|
|
}
|
|
|
|
if (!premiere_charge) // cas où on ne sait pas que l'on est sur une courbe de première charge
|
|
|
|
// donc a priori courbes secondaires, donc il existe au moins 2 pt de références enregistrées
|
|
|
|
// à moins qu'en dépilant on s'apperçoit que l'on a également rejoint la courbe de première charge
|
|
|
|
// on commence par essayer de remonter de 2 dans les listes de pointeurs
|
|
|
|
{// on définit le pointeur de première fonction de charge
|
|
|
|
List_io <double>::iterator ip1 = save_resul.fct_aide.end();ip1--;
|
|
|
|
if(pt_sur_principal)
|
|
|
|
// on est sur la liste principale, et pas sur la première charge, on dépile
|
|
|
|
{iatens++;iafct++;iatens_princ++; iafct_princ++;
|
|
|
|
// on vérifie que l'on n'a pas rejoint la première charge, ce qui peut arriver lorsque l'on passe
|
|
|
|
// de tout traction en directement une coincidence en compression
|
|
|
|
if (iafct == ip1)
|
|
|
|
{ premiere_charge = true;}
|
|
|
|
else // sinon on peut encore dépiler ce qui est le cas d'une boucle
|
|
|
|
{ iatens++;iafct++;iatens_princ++; iafct_princ++;
|
|
|
|
// puis on recommence la vérif
|
|
|
|
if (iafct == ip1)
|
|
|
|
{ premiere_charge = true;} // dans ce cas la coincidence est avec la première charge
|
|
|
|
// sinon on laisse comme c'est, et premiere_charge est false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // sinon il faut tester si l'on épuise la liste secondaire
|
|
|
|
{ iatens++;iafct++; // on incrémente
|
|
|
|
// puis on vérifie que l'incrémentation est valide
|
|
|
|
if (iatens == save_resul.sigma_barre_BH_R_t_a_tdt.end())
|
|
|
|
{ // on est revenu sur la liste principale
|
|
|
|
iatens = iatens_princ; iafct = iafct_princ;
|
|
|
|
// comme l'incrémentation que l'on venait de faire n'était pas valide
|
|
|
|
// on la refait, mais maintenant sur la liste principale
|
|
|
|
// pas sur à vérifier ---------- !!!!!!!!!!!
|
|
|
|
iatens++;iatens_princ++; iafct++; iafct_princ++;
|
|
|
|
pt_sur_principal = true;
|
|
|
|
// on vérifie que l'on n'a pas rejoint la première charge,
|
|
|
|
// ce qui peut arriver lorsque l'on passe
|
|
|
|
// de tout traction en directement une coincidence en compression
|
|
|
|
if (iafct == ip1)
|
|
|
|
{ premiere_charge = true;} // et le traitement sera fait après
|
|
|
|
else // sinon on peut encore dépiler ce qui est le cas d'une boucle
|
|
|
|
{ iatens++;iafct++;iatens_princ++; iafct_princ++;
|
|
|
|
// puis on recommence la vérif
|
|
|
|
if (iafct == ip1)
|
|
|
|
{ premiere_charge = true;} // dans ce cas la coincidence est avec la première charge
|
|
|
|
// sinon on laisse comme c'est, et premiere_charge est false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // fin du else quand on parcours la liste secondaire
|
|
|
|
} // fin du else : cas on on est pas sur la première charge
|
|
|
|
|
|
|
|
// traitement particulier dans le cas où on a rejoint la première charge
|
|
|
|
if (premiere_charge) // cas où l'on est sur la courbe de première charge
|
|
|
|
{ wprime = 1.; unSur_wprimeCarre = 1./(wprime*wprime);
|
|
|
|
iatens = save_resul.sigma_barre_BH_R.end(); iatens--;
|
|
|
|
iafct = save_resul.fct_aide.end();iafct--;
|
|
|
|
W_a = *save_resul.ip2; // on récupère la valeur de la fct d'aide au deuxième point
|
|
|
|
}
|
|
|
|
else
|
|
|
|
// on met à jour la fonction d'aide, en indiquant que l'on a rattrapé une courbe
|
|
|
|
{W_a = *iafct; wprime = 2.; unSur_wprimeCarre = 1./(wprime*wprime);}
|
|
|
|
|
|
|
|
// gestion pour la sauvegarde ultérieure
|
|
|
|
if (save_resul.modif == 0)
|
|
|
|
{save_resul.modif = 1;} // on signale qu'il y a une coincidence
|
|
|
|
else if (save_resul.modif == 2) // cas ou l'on suit une inversion
|
|
|
|
{save_resul.modif = 3;} // on signale que l'on est en mixte
|
|
|
|
// sinon cela signifie que l'on est déjà en mixte
|
|
|
|
save_resul.indic_coin.push_back(true); // signale la coincidence
|
|
|
|
save_resul.nb_coincidence++; // on incrémente le nombre de coincidence sur le pas de temps
|
|
|
|
// fin et retour, comme le calcul n'est pas fini on retourne false
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
// initialisation éventuelle des variables thermo-dépendantes
|
|
|
|
void Hysteresis1D::Init_thermo_dependance()
|
|
|
|
{ // cas d'une thermo dépendance, on calcul les grandeurs en fonction de la température
|
|
|
|
if (xnp_temperature != NULL) xnp = xnp_temperature->Valeur(*temperature);
|
|
|
|
if (Qzero_temperature != NULL) Qzero = Qzero_temperature->Valeur(*temperature);
|
|
|
|
if (xmu_temperature != NULL) xmu = xmu_temperature->Valeur(*temperature);
|
|
|
|
};
|
|
|
|
|
|
|
|
// calcul de l'avancement temporel sur 1 pas,
|
|
|
|
// utilisé par les 3 programmes principaux:
|
|
|
|
// Calcul_SigmaHH, Calcul_DsigmaHH_tdt, Calcul_dsigma_deps,
|
|
|
|
void Hysteresis1D::Avancement_temporel(const Tenseur1BB & delta_epsBB,const Tenseur1HH & gijHH
|
|
|
|
,SaveResulHysteresis1D & save_resul
|
|
|
|
,Tenseur1HH & sigHH)
|
|
|
|
{
|
|
|
|
// le tenseur des contraintes initiale en mixte, qui sera considéré comme la contrainte de début
|
|
|
|
// de calcul, pour toute la suite, s'il y a plusieurs passage dans la boucle while qui suit,
|
|
|
|
// sigma_i_barre_BH variera, à chaque passage
|
|
|
|
// on utilise le sigmaBH sauvegardé plutôt que le sigmaHH ce qui signifie que l'on effectue un transport
|
|
|
|
// en mixte plutôt qu'en 2 fois contravariants !!
|
|
|
|
sigma_i_barre_BH = save_resul.sigma_barre_BH_t;
|
|
|
|
|
|
|
|
//récup et initialisation des variables de travail
|
|
|
|
// iatens_princ: pointeur sur la liste déjà enregistrée, iatens: pointeur courant
|
|
|
|
List_io <Tenseur1BH>::iterator iatens_princ = save_resul.sigma_barre_BH_R.begin();
|
|
|
|
List_io <Tenseur1BH>::iterator iatens = iatens_princ;
|
|
|
|
// idem pour la fonction d'aide
|
|
|
|
List_io <double>::iterator iafct_princ = save_resul.fct_aide.begin();
|
|
|
|
List_io <double>::iterator iafct = iafct_princ;
|
|
|
|
// un pointeur qui indique si les pointeurs courants sont sur les listes principales ou
|
|
|
|
// sur les nouvelles liste
|
|
|
|
bool pt_sur_principal = true;
|
|
|
|
save_resul.nb_coincidence=0; // init au début du pas, pour l'instant on n'a pas de coïncidence
|
|
|
|
|
|
|
|
double W_a = save_resul.fonction_aide_t; // valeur courante de la fonction d'aide
|
|
|
|
double delta_W_a = 0.; // valeur courante du delta W
|
|
|
|
wprime = save_resul.wprime_t; // initialisation du paramètre de masing
|
|
|
|
double unSur_wprimeCarre= 1./(wprime*wprime);
|
|
|
|
delta_barre_epsBH = delta_epsBB * gijHH; // incrément de def en mixte
|
|
|
|
|
|
|
|
// indicateur pour gérer la fin du traitement
|
|
|
|
bool fin_traitement= false;
|
|
|
|
// ==== 1 === calcul de la contrainte à tdt =============
|
|
|
|
// faire tant que fin_traitement n'est pas bon
|
|
|
|
int nb_coin_inver=0; // compteur pour éviter une boucle infinie
|
|
|
|
while (!fin_traitement)
|
|
|
|
{// initialisation des variables de travail qui peuvent varier à chaque passage
|
|
|
|
sigma_barre_BH_R = *iatens; // la contrainte de référence
|
|
|
|
delta_sigma_barre_BH_Rat = sigma_i_barre_BH - sigma_barre_BH_R;
|
|
|
|
|
|
|
|
// le calcul de la contrainte s'effectue par la résolution de l'équation différentielle
|
|
|
|
// du schéma constitutif
|
|
|
|
// sigma_point = 2*mu*D_barre + beta*phi*(delta_barre de t à R de Sigma)
|
|
|
|
|
|
|
|
// méthode permettant le calcul de sigma à tdt par différente méthodes: linéarisation
|
|
|
|
// ou kutta
|
|
|
|
// en sortie calcul de :
|
|
|
|
// sigma_t_barre_tdt, delta_sigma_barre_tdt_BH
|
|
|
|
CalculContrainte_tdt(save_resul.indicateurs_resolution);
|
|
|
|
|
|
|
|
delta_sigma_barre_BH_Ratdt = sigma_t_barre_tdt - sigma_barre_BH_R;
|
|
|
|
|
|
|
|
// 2==== gestion de la mémoire discrète
|
|
|
|
// double phi_1_dt = delta_sigma_barre_BH_Rat && delta_barre_epsBH;
|
|
|
|
// modif le 10 mai 2016: a priori il vaut mieux calculer le phi à tdt plutôt que t
|
|
|
|
double phi_1_dt = delta_sigma_barre_BH_Ratdt && delta_barre_epsBH;
|
|
|
|
|
|
|
|
if (phi_1_dt<0.) // au lieu de (delta_W_a < 0.) : cf. thèse de Nicolas
|
|
|
|
// --- cas de la détection d'un point d'inversion ----
|
|
|
|
{ // on vérifie la cohérence de la valeur de la fonction d'aide (qui n'a rien à voir ici
|
|
|
|
// et donc doit être inférieur au maxi)
|
|
|
|
if (W_a > *iafct)
|
|
|
|
{cout << "\n erreur dans l'algorithme de gestion de la memoire discrete, "
|
|
|
|
<< " bien que l'on ait detecte un point d'inversion, il se trouve que la fonction d'aide "
|
|
|
|
<< " est superieur au dernier maxi, ce qui signifie que le calcul de l'integrale de la puissance "
|
|
|
|
<< " non reversible (phi(t)) n'a pas ete correcte (c'est une erreur cumulee), du sans doute a des pas de temps trop grand, "
|
|
|
|
<< " il n'est pas possible de continuer le calcul, essayer de le reprendre avec un pas de temps plus petit ! "
|
|
|
|
<< " \n W_a= " << W_a << " iafct= " << *iafct
|
|
|
|
<< "\n Hysteresis1D::Avancement_temporel(..."
|
|
|
|
<< endl;
|
|
|
|
Sortie(1);
|
|
|
|
};
|
|
|
|
// ----- débug (a virer ) ------
|
|
|
|
// // on vérifie que le niveau de la fonction d'aide est acceptable
|
|
|
|
// // d'abord les points déjà existants
|
|
|
|
// List_io <double>::iterator iaf,iafend = save_resul.fct_aide.end();
|
|
|
|
// for (iaf=save_resul.fct_aide.begin();iaf!=iafend;iaf++)
|
|
|
|
// if (W_a > *iaf)
|
|
|
|
// {cout << "\n erreur 1 W_a= " << W_a << " *iaf= " << *iaf
|
|
|
|
// << " iafct= " << *iafct << endl;Sortie(1);}
|
|
|
|
// // ensuite la liste intermédiaire
|
|
|
|
// List_io <double>::iterator iafe,iafende = save_resul.fct_aide_t_a_tdt.end();
|
|
|
|
// for (iafe=save_resul.fct_aide_t_a_tdt.begin();iafe!=iafende;iafe++)
|
|
|
|
// if (W_a > *iafe)
|
|
|
|
// {cout << "\n erreur 2 " << endl;Sortie(1);}
|
|
|
|
// ----- fin débug (a virer ) ------
|
|
|
|
|
|
|
|
if (save_resul.modif == 0)
|
|
|
|
{save_resul.modif = 2;} // on signale qu'il y a une inversion
|
|
|
|
else if (save_resul.modif == 1) // cas ou l'inversion suit une coincidence
|
|
|
|
{save_resul.modif = 3;} // on signale que l'on est en mixte
|
|
|
|
// sinon c'est = 3, on le laisse en 3 : c'est-a-dire en mixte
|
|
|
|
save_resul.indic_coin.push_back(false); // signale l'inversion
|
|
|
|
// ajout de la contrainte de référence = sigma at, et du niveau de la fonction d'aide
|
|
|
|
save_resul.sigma_barre_BH_R_t_a_tdt.push_front(sigma_i_barre_BH);
|
|
|
|
save_resul.fct_aide_t_a_tdt.push_front(W_a);
|
|
|
|
iatens = save_resul.sigma_barre_BH_R_t_a_tdt.begin(); // pour l'init du début du while générale
|
|
|
|
iafct = save_resul.fct_aide_t_a_tdt.begin();
|
|
|
|
pt_sur_principal = false; // on indique que maintenant on pointe sur la nouvelle liste
|
|
|
|
W_a=0.; delta_W_a = 0.; // on ré-initialise pour les futures coincidences éventuelles
|
|
|
|
// on indique le point d'inversion pour les futurs traitements
|
|
|
|
sigma_barre_BH_R = save_resul.sigma_barre_BH_R_t_a_tdt.front();
|
|
|
|
// point d'inversion
|
|
|
|
save_resul.wprime_tdt = 2.;
|
|
|
|
wprime = 2.; unSur_wprimeCarre = 1./(wprime*wprime);
|
|
|
|
fin_traitement = false; // il faut recalculer avec le nouveau point d'inversion
|
|
|
|
}
|
|
|
|
else // sinon (phi_1_dt >= 0.) donc pas d'inversion
|
|
|
|
{double phidt = delta_sigma_barre_BH_Ratdt && delta_barre_epsBH;
|
|
|
|
// dans l'expression suivante, on utilise la règle des trapèzes pour le calcul de l'incrément
|
|
|
|
delta_W_a = 0.5*(phi_1_dt + phidt) * unSur_wprimeCarre;
|
|
|
|
W_a += delta_W_a;
|
|
|
|
if ((W_a <= *iafct) // cas normal après plusieurs inversion
|
|
|
|
|| (save_resul.fct_aide.size() == 1) || (wprime == 1.)) // cas de la courbe de première charge
|
|
|
|
{ // --- cas d'une évolution normale sans inversion ni coincidence ---
|
|
|
|
// mais cette évolution peut-être la fin après une coincidence par exemple, donc
|
|
|
|
// on ne modifie pas save_resul.modif
|
|
|
|
save_resul.sigma_barre_BH_tdt = sigma_t_barre_tdt;
|
|
|
|
save_resul.fonction_aide_tdt = W_a;
|
|
|
|
save_resul.wprime_tdt = wprime;
|
|
|
|
fin_traitement = true;
|
|
|
|
// calcul de la contrainte de retour
|
|
|
|
sigHH = gijHH * sigma_t_barre_tdt;
|
|
|
|
}
|
|
|
|
else // sinon (W_a > save_resul.fct_aide.front()) -> coincidence
|
|
|
|
{ // et ici il y a forcément plus de 1 point d'inversion
|
|
|
|
if (W_a <= *(save_resul.ip2))
|
|
|
|
// if (W_a > *(save_resul.ip2))
|
|
|
|
{ // --- cas d'une coincidence quelconque ou sur première charge ---
|
|
|
|
fin_traitement = Coincidence(unSur_wprimeCarre,false,save_resul,W_a,iatens
|
|
|
|
,iafct,pt_sur_principal,iatens_princ,iafct_princ,delta_W_a);
|
|
|
|
if (fin_traitement) // calcul de la contrainte de retour si le traitement est fini
|
|
|
|
sigHH = gijHH * sigma_t_barre_tdt;
|
|
|
|
}
|
|
|
|
else // coincidence avec la courbe de première charge
|
|
|
|
{ fin_traitement = Coincidence(unSur_wprimeCarre,true,save_resul,W_a,iatens
|
|
|
|
,iafct,pt_sur_principal,iatens_princ,iafct_princ,delta_W_a);
|
|
|
|
if (fin_traitement) // calcul de la contrainte de retour si le traitement est fini
|
|
|
|
sigHH = gijHH * sigma_t_barre_tdt;
|
|
|
|
}
|
|
|
|
}; // -- fin du else du if (phi_1_dt<0.) : évolution normale
|
|
|
|
}; // -- fin du else du if (phi_1_dt<0.) : point d'inversion
|
|
|
|
|
|
|
|
// on regarde si l'on n'a pas dépassé le nombre de boucle maxi permis: ce qui permet
|
|
|
|
// d'éviter les boucles infinies
|
|
|
|
nb_coin_inver++;
|
|
|
|
if (nb_coin_inver > nb_maxInvCoinSurUnPas)
|
|
|
|
{ // dans ce cas cela signifie qu'il y a pb et on arrête en générant une interruption d'erreur
|
|
|
|
// de convergence dans une loi de comportement
|
|
|
|
throw ErrNonConvergence_loiDeComportement();break;
|
|
|
|
};
|
|
|
|
}; // -- fin du While (!fin_traitement)
|
|
|
|
};
|
|
|
|
|
|
|
|
/* // **********calcul d'une dérivée numérique-------------
|
|
|
|
double peti= 1.E-10;
|
|
|
|
double lambda_ver = lambda+peti;
|
|
|
|
double un_sur_1_plus_2_G_lambda_ver = 1. / (1. + deux_G*lambda_ver);
|
|
|
|
double un_sur_1_plus_2_G_lambda2_ver =
|
|
|
|
un_sur_1_plus_2_G_lambda_ver * un_sur_1_plus_2_G_lambda_ver;
|
|
|
|
double alphaa_ver = un_sur_1_plus_2_G_lambda_ver*deux_G;
|
|
|
|
double alphaa2_ver = un_sur_1_plus_2_G_lambda2_ver * deux_G_carre;
|
|
|
|
double omega_ver = alphaa_ver*K/(alphaa_ver+2.*K); double omega2_ver = omega_ver*omega_ver;
|
|
|
|
// a moins que lambda soit très grand on considère qu'omega est positif
|
|
|
|
// par contre lambda peut-être négatif
|
|
|
|
// if (lambda >= 0)
|
|
|
|
double delta_eps_equi_ver = 2.*lambda_ver*omega_ver * eps_elasBH(1,1);
|
|
|
|
// delta_eps_equi = 2.*lambda*omega * sqrt(c_c);
|
|
|
|
double epsilon_barre_ver = epsilon_barre_t + // def plastique cumulée
|
|
|
|
delta_eps_equi_ver;
|
|
|
|
// nouvelle valeur de la variation de sigma_barre
|
|
|
|
// et valeur de la tangente de la courbe d'écrouissage
|
|
|
|
Courbe1D::ValDer valder_ver = f_ecrouissage->Valeur_Et_derivee(epsilon_barre_ver);
|
|
|
|
double sig_equi_ver = valder_ver.valeur;
|
|
|
|
|
|
|
|
// calcul du résidu
|
|
|
|
double res_plas_ver = 3.*c_c*omega2_ver - sig_equi_ver * sig_equi_ver * un_tiers;
|
|
|
|
double der1 = 3.*c_c*(omega2_ver-omega2)/peti;
|
|
|
|
double dereps_barre = (epsilon_barre_ver -epsilon_barre)/peti;
|
|
|
|
double der2 = un_tiers*(- sig_equi_ver * sig_equi_ver - - sig_equi * sig_equi)/peti;
|
|
|
|
double der = (res_plas_ver - res_plas)/peti;
|
|
|
|
double delta_lambda_ver = - res_plas/der ;
|
|
|
|
// **********fin du calcul de la dérivée numérique------- */
|