// 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) . // // 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 . // // For more information, please consult: . //#include "Debug.h" # include using namespace std; //introduces namespace std #include #include #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& 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 { if (Permet_affichage() > 0) 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 if (Permet_affichage() > 0) 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; } else if (Permet_affichage() > 4) {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 ::iterator& iatens,List_io ::iterator& iafct,bool& pt_sur_principal ,List_io ::iterator& iatens_princ,List_io ::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= " <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 ::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 ::iterator iatens_princ = save_resul.sigma_barre_BH_R.begin(); List_io ::iterator iatens = iatens_princ; // idem pour la fonction d'aide List_io ::iterator iafct_princ = save_resul.fct_aide.begin(); List_io ::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 ::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 ::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------- */