// FICHIER : Hysteresis_bulk_2.cc // CLASSE : Hysteresis_bulk // 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 "Hysteresis_bulk.h" // calcul de l'expression permettant d'obtenir la dérivée temporelle de la pression // 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& Hysteresis_bulk::Pression_point(const double & tau, const Vecteur & press_tau , Vecteur& pres_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 MPr_tau = press_tau(1); // variation de sigma de R à tau delta_MPr_R_a_tau = MPr_tau - MPr_R; double deux_xmu = 2. * xmu; // pour simplifier // calcul de QdeltaPression double QdeltaPression = Abs(delta_MPr_R_a_tau); // on vérifie que l'amplitude de la contrainte transmise, n'est pas supérieure à la saturation // on fait la vérification sur le delta, car si on est sur une branche non initiale, on peut très bien // dépasser le Qzero, localement, mais ce sera invalidé ensuite avec l'algo de coincidence if ( QdeltaPression > (Qzero*depassement_Q0*wprime)) {erreur = 1; //--- pour le debug if (Permet_affichage() > 5) cout << "\n QdeltaPression =" << QdeltaPression << " au lieu de "<< (Qzero*depassement_Q0*wprime) << " Hysteresis_bulk::Pression_point( " << endl ; //--- fin debug } else // sinon on continue { erreur = 0;}; // calcul de Beta: ici ce n'est pas le même qu'en 3D, il est simplifié: voir doc // on l'appel donc beta_P double unsurwprimeQ0_puiss_np = 1./pow(wprime*Qzero,xnp); double beta_P=0.; // init dans le cas où ne le calcul ne fonctionne pas après if (QdeltaPression >= ConstMath::pasmalpetit) // on ne calcul beta que si la norme de QdeltaSigma est significative // sinon l'opération pow(QdeltaPression,xnp); peut poser pb via le log(QdeltaPression) { beta_P = pow(QdeltaPression,xnp) * unsurwprimeQ0_puiss_np; }; // on considère que D est constant = delta_V/deltat et comme deltat = 1 // ==> = delta_V double deuxmudeltaV = deux_xmu * delta_V ; // formule : MPr_point = deux_xmu * delta_V(1- (1/(w'*Q0)^np) * QdeltaPression^np ); MPr_point = deuxmudeltaV*(1. - beta_P); // on limite la variation de la dérivée de la contrainte if (deuxmudeltaV > 0.) {// cas d'une contrainte positive et normalement la dérivée doit évoluer entre 0. et 2mu * delta_V (c-a-d saturation) if (MPr_point < 0. ) MPr_point = 0.; if (MPr_point > deuxmudeltaV) MPr_point = deuxmudeltaV; } else {// cas d'une contrainte négative et normalement la dérivée doit évoluer entre -2mu * delta_V (saturation) et 0 if (MPr_point < deuxmudeltaV ) MPr_point = deuxmudeltaV; if (MPr_point > 0.) MPr_point = 0.; }; // retour de la dérivée temporelle de la contrainte pres_point(1) = MPr_point; return pres_point; }; // vérification de l'intégrité de la MPr calculée // 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 Hysteresis_bulk::Verif_integrite_Pression(const double & , const Vecteur & MPr_tau_vect,int & erreur) { // récup de la contrainte à tau MPr_tau = MPr_tau_vect(1); // variation de sigma de R à tau delta_MPr_R_a_tau = MPr_tau - MPr_R; // calcul de QdeltaPression double QdeltaPression = Abs(delta_MPr_R_a_tau); // on vérifie que l'amplitude de la contrainte transmise, n'est pas supérieure à la saturation // on fait la vérification sur le delta, car si on est sur une branche non initiale, on peut très bien // dépasser le Qzero, localement, mais ce sera invalidé ensuite avec l'algo de coincidence if ( QdeltaPression > (Qzero*depassement_Q0*wprime)) {erreur = 1; //--- pour le debug if (Permet_affichage() > 5) cout << "\n QdeltaPression =" << QdeltaPression << " au lieu de "<< (Qzero*depassement_Q0*wprime) << " Hysteresis_bulk::Verif_integrite_Pression( " << endl ; //--- fin debug } else // sinon on continue { erreur = 0; }; }; // méthode permettant le calcul de la MPr à tdt par différente méthodes: linéarisation // ou kutta // en sortie calcul de : // MPr_t___tdt, delta_MPr_tatdt void Hysteresis_bulk::CalculPression_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); int nb_incr_total,nb_iter_total; // variables intermédiaires d'indicateurs, pas utilisées pour l'instant // 1== résolution de l'équation constitutive d'avancement discrétisée en euler implicite bool conver = false; // init par défaut try // on met le bloc sous surveillance { conver=alg_zero.Newton_raphson (*this,&Hysteresis_bulk::Residu_constitutif,&Hysteresis_bulk::Mat_tangente_constitutif ,val_initiale,racine,der_at_racine,nb_incr_total,nb_iter_total ,maxi_delta_var_sig_sur_iter_pour_Newton); } catch (ErrSortieFinale) // cas d'une direction voulue vers la sortie // on relance l'interuption pour le niveau supérieur { ErrSortieFinale toto; throw (toto); } catch ( ... ) //(ErrNonConvergence_Newton erreur) { double absracinemax=racine.Max_val_abs(); if (Permet_affichage() > 0) {cout << "\n non convergence 2 sur l'algo de la resolution du schema constitutif" << " abs_racine_max " << absracinemax << "\n Hysteresis_bulk::CalculPression_tdt (..." << endl; }; LibereTenseur(); LibereTenseurQ(); // on génère une exception throw ErrNonConvergence_loiDeComportement();break; }; if(sortie_post) // sauvegarde éventuelle des indicateurs {indicateurs_resolution(1)+=nb_incr_total;indicateurs_resolution(2)+=nb_iter_total;}; // --- debug piloté --- if (Permet_affichage() > 5) cout << "\n Hysteresis_bulk::CalculContrainte_tdt " << "nombreIncre= " << nb_incr_total << " nb_iter_total= " << nb_iter_total << endl; // --- fin debug piloté--- if (!conver) { cout << "\n non convergence sur l'algo de la resolution du schema constitutif" << "\n Hysteresis_bulk::CalculPression_tdt (..."; // on génère une exception throw ErrNonConvergence_loiDeComportement();break; }; delta_MPr_tatdt=racine(1); // récup de la solution MPr_t___tdt = MPr_i___ + delta_MPr_tatdt; // variation de -pression=MPr de R à tdt rdelta_MPr_Ratdt = MPr_t___tdt - MPr_R; // on vérifie l'amplitude de la pression calculée {double QdeltaPression = Abs(rdelta_MPr_Ratdt); // calcul de QdeltaPression if ( QdeltaPression > (Qzero*depassement_Q0*wprime)) {if (Permet_affichage() > 0) cout << "\n erreur10 fatale dans l'algo de la resolution du schema constitutif (en sortie de Newton) " << " concernant le niveau de saturation de la pression " << "\n QdeltaPression =" << QdeltaPression << " au lieu de "<< (Qzero*depassement_Q0*wprime) << "\n Hysteresis_bulk::CalculPression_tdt (..." << endl; // on génère une exception throw ErrNonConvergence_loiDeComportement();break; } // else if ( rdelta_MPr_Ratdt <= 0.) // {if ((ParaGlob::NiveauImpression() > 3)|| (abs(Permet_affichage()) > 0)) // cout << "\n erreur20 fatale dans l'algo de la resolution du schema constitutif (en sortie de Newton) " // << " concernant la nouvelle valeur de pression determinee " // << "\n rdelta_MPr_Ratdt =" << rdelta_MPr_Ratdt << " qui devrait etre >= 0 " << "\n Hysteresis_bulk::CalculPression_tdt (..." << endl; // // on génère une exception // throw ErrNonConvergence_loiDeComportement();break; // }; }; // on vérifie également le niveau de la pression totale calculée {double AbsPression = Abs(MPr_t___tdt); if ( AbsPression > (Qzero*depassement_Q0)) { if (Permet_affichage() > 0) cout << "\n erreur101 fatale dans l'algo de la resolution du schema constitutif (en sortie de Newton) " << " concernant le niveau de saturation de la pression " << "\n AbsPression =" << AbsPression << " au lieu de "<< (Qzero*depassement_Q0) << "\n Hysteresis_bulk::CalculPression_tdt (..." << endl; // on génère une exception throw ErrNonConvergence_loiDeComportement();break; }; }; 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 pres_initiale(1),derpres_initiale(1); ////--- debug --- //#ifdef MISE_AU_POINT //if (Abs(MPr_i___) > Qzero) // { cout << "\n debug Hysteresis_bulk::CalculPression_tdt( " // << " MPr_i___= "<< MPr_i___ << endl; // }; //#endif ////----- fin debug ---- pres_initiale(1)=MPr_i___; double tdeb=0.,tfi=1.; // calcul de la dérivée initiale int erreur=0; //init d'une erreur de calcul de MPr_point Pression_point(tdeb,pres_initiale,derpres_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 (ParaGlob::NiveauImpression() > 0) cout << "\n erreur dans l'algo de la resolution du schema constitutif" << " au niveau du calcul de la derivee initiale " << "\n Hysteresis_bulk::CalculPression_tdt() (..." << endl; // on génère une exception throw ErrNonConvergence_loiDeComportement();break; }; Vecteur pres_finale(1),derpres_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,& Hysteresis_bulk::Pression_point,& Hysteresis_bulk::Verif_integrite_Pression ,pres_initiale,derpres_initiale ,tdeb,tfi,erreurAbsolue,erreurRelative ,pres_finale,derpres_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; }; // --- debug piloté --- if (Permet_affichage() > 5) cout << "\n Hysteresis_bulk::CalculContrainte_tdt " << " pres_finale= "<< pres_finale(1) << " nombreAppelF= " << nombreAppelF << " nb_step= " << nb_step << " erreur_maxi_global= " << erreur_maxi_global << endl; // --- fin debug piloté--- // gestion de l'erreur de retour double abssigmax=pres_finale.Max_val_abs(); if ((conver != 2) || (!isfinite(abssigmax)) || (isnan(abssigmax)) ) { // on appel kutta45 sans gestion d'erreur !! double deltat=tfi-tdeb; Vecteur estime_erreur(1); alg_edp.Runge_Kutta_step45(*this,& Hysteresis_bulk::Pression_point,& Hysteresis_bulk::Verif_integrite_Pression ,pres_initiale,derpres_initiale ,tdeb,deltat,pres_finale,estime_erreur); if (estime_erreur(1) >= ConstMath::tresgrand) { // là on ne peut rien faire if (ParaGlob::NiveauImpression() > 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 Hysteresis_bulk::CalculPression_tdt() (..." << endl; // on génère une exception throw ErrNonConvergence_loiDeComportement();break; } else if (Permet_affichage() > 3) {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 Hysteresis_bulk::CalculPression_tdt (..."<< endl; // Sortie(1); // pour le debug // if ((Dabs(estime_erreur(1)) > 10) || (Dabs(pres_finale(1)) > 10)) // alg_edp.Runge_Kutta_step45(*this,& Hysteresis_bulk::Sigma_point,pres_initiale,derpres_initiale // ,tdeb,deltat,pres_finale,estime_erreur); }; double abssigmax=pres_finale.Max_val_abs(); if ((!isfinite(abssigmax)) || (isnan(abssigmax)) ) // dans le cas où la valeur de sig_finale est infinie, ce n'est pas la peine d'aller plus loin { cout << "\n erreur , l'appel directe de Kutta donne une valeur infinie de contrainte ! " << "\n Hysteresis_bulk::CalculPression_tdt (..."<< endl; // on génère une exception throw ErrNonConvergence_loiDeComportement(); Sortie(1); }; }; // récup des résultats MPr_t___tdt=pres_finale(1); delta_MPr_tatdt = MPr_t___tdt - MPr_i___; // variation de -pression=MPr de R à tdt rdelta_MPr_Ratdt = MPr_t___tdt - MPr_R; // on vérifie l'amplitude de la pression calculée {double QdeltaPression = Abs(rdelta_MPr_Ratdt); // calcul de QdeltaPression if ( QdeltaPression > (Qzero*depassement_Q0*wprime)) {if (Permet_affichage() > 0) cout << "\n erreur1005 fatale dans l'algo de la resolution du schema constitutif (en sortie de kutta) " << " concernant le niveau de saturation de la pression " << "\n QdeltaPression =" << QdeltaPression << " au lieu de "<< (Qzero*depassement_Q0*wprime) << "\n Hysteresis_bulk::CalculPression_tdt (..." << endl; // on génère une exception throw ErrNonConvergence_loiDeComportement();break; } // else if ( rdelta_MPr_Ratdt <= 0.) // {if ((ParaGlob::NiveauImpression() > 3)|| (abs(Permet_affichage()) > 0)) // cout << "\n erreur21 fatale dans l'algo de la resolution du schema constitutif (en sortie de Newton) " // << " concernant la nouvelle valeur de pression determinee " // << "\n rdelta_MPr_Ratdt =" << rdelta_MPr_Ratdt << " qui devrait etre >= 0 " << "\n Hysteresis_bulk::CalculPression_tdt (..." << endl; // // on génère une exception // throw ErrNonConvergence_loiDeComportement();break; // }; }; // on vérifie également le niveau de la pression totale calculée (non,**** à voir ) // en fait celle-ci peut-être momentanément supérieur à Qzero, en fait le total // ne peut pas être supérieur à Qzero(1.+wprime), qui est le cas avant coïncidence // d'un point sur une branche secondaire qui démarre près de la saturation {double AbsPression = Abs(MPr_t___tdt); if ( AbsPression > (Qzero*(1.+wprime)*depassement_Q0)) {if (Permet_affichage() > 3) cout << "\n erreur1015 fatale dans l'algo de la resolution du schema constitutif (en sortie de kutta) " << " concernant le niveau de saturation de la pression (meme transitoirement avant coincidence) " << "\n AbsPression =" << AbsPression << " au lieu de "<< (Qzero*(1.+wprime)*depassement_Q0) << "\n Hysteresis_bulk::CalculPression_tdt (..." << endl; // on génère une exception throw ErrNonConvergence_loiDeComportement();break; }; }; break; } case 3: // cas de la linéarisation de l'équation et où on impose la limitation lorsque l'on atteind la saturation {// ----- 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); int nb_incr_total,nb_iter_total; // variables intermédiaires d'indicateurs, pas utilisées pour l'instant // 1== résolution de l'équation constitutive d'avancement discrétisée en euler implicite bool conver = false; // init par défaut try // on met le bloc sous surveillance { conver=alg_zero.Newton_raphson (*this,&Hysteresis_bulk::Residu_constitutif,&Hysteresis_bulk::Mat_tangente_constitutif ,val_initiale,racine,der_at_racine,nb_incr_total,nb_iter_total ,maxi_delta_var_sig_sur_iter_pour_Newton); } catch (ErrSortieFinale) // cas d'une direction voulue vers la sortie // on relance l'interuption pour le niveau supérieur { ErrSortieFinale toto; throw (toto); } catch ( ... ) //(ErrNonConvergence_Newton erreur) { double absracinemax=racine.Max_val_abs(); if (Permet_affichage() > 0) {cout << "\n non convergence 29 sur l'algo de la resolution du schema constitutif" << " abs_racine_max " << absracinemax << "\n Hysteresis_bulk::CalculPression_tdt (..." << endl; }; LibereTenseur(); LibereTenseurQ(); // on génère une exception throw ErrNonConvergence_loiDeComportement();break; }; if(sortie_post) // sauvegarde éventuelle des indicateurs {indicateurs_resolution(1)+=nb_incr_total;indicateurs_resolution(2)+=nb_iter_total;}; // --- debug piloté --- if (Permet_affichage() > 5) cout << "\n Hysteresis_bulk::CalculContrainte_tdt " << "nombreIncre= " << nb_incr_total << " nb_iter_total= " << nb_iter_total << endl; // --- fin debug piloté--- if (!conver) { cout << "\n non convergence sur l'algo de la resolution du schema constitutif" << "\n Hysteresis_bulk::CalculPression_tdt (..."; // on génère une exception throw ErrNonConvergence_loiDeComportement();break; }; delta_MPr_tatdt=racine(1); // récup de la solution MPr_t___tdt = MPr_i___ + delta_MPr_tatdt; // variation de -pression=MPr de R à tdt rdelta_MPr_Ratdt = MPr_t___tdt - MPr_R; // on vérifie l'amplitude de la pression calculée {double QdeltaPression = Abs(rdelta_MPr_Ratdt); // calcul de QdeltaPression if ( QdeltaPression > (Qzero*depassement_Q0*wprime)) {// dans ce cas on impose la saturation QdeltaPression = (Qzero*depassement_Q0*wprime); rdelta_MPr_Ratdt = QdeltaPression * DSigne(rdelta_MPr_Ratdt); MPr_t___tdt = rdelta_MPr_Ratdt + MPr_R; delta_MPr_tatdt = MPr_t___tdt - MPr_i___; if (Permet_affichage() > 0) cout << "\n ** warning dans l'algo de la resolution du schema constitutif (en sortie de Newton) " << " concernant le niveau de saturation de la pression " << "\n QdeltaPression =" << QdeltaPression << " au lieu de "<< (Qzero*depassement_Q0*wprime) << "\n Hysteresis_bulk::CalculPression_tdt (..." << endl; } // else if ( rdelta_MPr_Ratdt <= 0.) // {if ((ParaGlob::NiveauImpression() > 3)|| (abs(Permet_affichage()) > 0)) // cout << "\n erreur20 fatale dans l'algo de la resolution du schema constitutif (en sortie de Newton) " // << " concernant la nouvelle valeur de pression determinee " // << "\n rdelta_MPr_Ratdt =" << rdelta_MPr_Ratdt << " qui devrait etre >= 0 " << "\n Hysteresis_bulk::CalculPression_tdt (..." << endl; // // on génère une exception // throw ErrNonConvergence_loiDeComportement();break; // }; }; // on vérifie également le niveau de la pression totale calculée {double AbsPression = Abs(MPr_t___tdt); if ( AbsPression > (Qzero*depassement_Q0)) { // dans ce cas on impose la saturation AbsPression = (Qzero*depassement_Q0); MPr_t___tdt = AbsPression * DSigne(MPr_t___tdt); if (Permet_affichage() > 0) cout << "\n ** warning dans l'algo de la resolution du schema constitutif (en sortie de Newton) " << " concernant le niveau de saturation de la pression " << "\n AbsPression =" << AbsPression << " au lieu de "<< (Qzero*depassement_Q0) << "\n Hysteresis_bulk::CalculPression_tdt (..." << endl; }; }; 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& Hysteresis_bulk::Residu_constitutif (const double & alpha,const Vecteur & x, int& test) { // tout d'abord on récupère les pressions rdelta_MPr_tatdt = x(1); // accroissement de -pression=MPr : de t à tdt // variation de -pression=MPr de R à tdt rdelta_MPr_Ratdt = MPr_i___ + rdelta_MPr_tatdt - MPr_R; double deux_xmu = 2. * xmu; // pour simplifier // calcul de QdeltaPression double QdeltaPression = Abs(rdelta_MPr_Ratdt); // calcul de Beta: ici ce n'est pas le même qu'en 3D, il est simplifié: voir doc // on l'appel donc beta_P double unsurwprimeQ0_puiss_np = 1./pow(wprime*Qzero,xnp); double beta_P=0.; // init dans le cas où ne le calcul ne fonctionne pas après if (QdeltaPression >= ConstMath::petit) // on ne calcul beta que si la norme de QdeltaSigma est significative // sinon l'opération pow(QdeltaPression,xnp); peut poser pb via le log(QdeltaPression) { beta_P = pow(QdeltaPression,xnp) * unsurwprimeQ0_puiss_np; }; // on considère que D est constant = delta_V/deltat et comme deltat = 1 // ==> = delta_V double deuxmudeltaV = deux_xmu * delta_V ; // calcul du résidu residuBH = rdelta_MPr_tatdt - alpha * deuxmudeltaV * (1.- beta_P); // a priori ici pas de pb de calcul test = 1; // retour du résidu residu(1) = residuBH; 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& Hysteresis_bulk::Mat_tangente_constitutif (const double & alpha,const Vecteur & x, Vecteur& residu, int& test) { // récupération du vecteur delta_Mpr rdelta_MPr_tatdt = x(1); rdelta_MPr_Ratdt = MPr_i___ + rdelta_MPr_tatdt - MPr_R; double deux_xmu = 2. * xmu; // pour simplifier // ne sert plus delta__alpha_V = alpha * delta_V; // limitation de la charge // calcul de QdeltaPression double QdeltaPression = Abs(rdelta_MPr_Ratdt); // calcul de Beta: ici ce n'est pas le même qu'en 3D, il est simplifié: voir doc // on l'appel donc beta_P double unsurwprimeQ0_puiss_np = 1./pow(wprime*Qzero,xnp); double beta_P=0.; // init dans le cas où ne le calcul ne fonctionne pas après double QdeltaPression_puiss_npmoins1 = 0.; // sert pour la matrice tangente if (QdeltaPression >= ConstMath::petit) // on ne calcul beta que si la norme de QdeltaSigma est significative // sinon l'opération pow(QdeltaPression,xnp); peut poser pb via le log(QdeltaPression) { beta_P = pow(QdeltaPression,xnp) * unsurwprimeQ0_puiss_np; QdeltaPression_puiss_npmoins1 = pow(QdeltaPression,xnp-1.); }; // on considère que D est constant = delta_V/deltat et comme deltat = 1 // ==> = delta_V double deuxmudeltaV = deux_xmu * delta_V ; // calcul du résidu residuBH = rdelta_MPr_tatdt - alpha * deuxmudeltaV * (1.- beta_P); residu(1) = residuBH; // maintenant on s'occupe de la matrice tangente double d_res_dMP = 1. - alpha * deuxmudeltaV * unsurwprimeQ0_puiss_np * xnp * QdeltaPression_puiss_npmoins1 * DSigne(rdelta_MPr_Ratdt); // a priori ici pas de pb de calcul test = 1; // retour de la matrice tangente derResidu(1,1)=d_res_dMP; return derResidu; }; // calcul de l'opérateur tangent : dsigma/depsilon, dans le cas d'un repère ortho-normé ou non // T_d_Mpres_d_V: représente la variation de la MPr par rapport à V // dsig_deps : opérateur tangent 3D void Hysteresis_bulk::Dsig_depsilon (double& T_d_Mpres_d_V,bool en_base_orthonormee,const Tenseur3HH & gijHH_tdt, TenseurHHHH* dsig_deps_ ,const double & V_tdt) //TenseurQ1geneBHBH& Hysteresis_bulk::Dsig_depsilon(TenseurQ1geneBHBH& dsig_deps) { // calcul des termes élémentaires de l'équation constitutive double deux_xmu = 2. * xmu; // pour simplifier // calcul de QdeltaPression double QdeltaPression = Abs(delta_MPr_Ratdt); double unsurQdeltaPression; // on limite la valeur par exemple pour le démarrage if ( Dabs(QdeltaPression) > ConstMath::pasmalpetit) {unsurQdeltaPression=1./QdeltaPression;} else {unsurQdeltaPression = ConstMath::grand;}; // calcul de Beta double unsurwprimeQ0_puiss_np = 1./pow(wprime*Qzero,xnp); // calcul de Beta: ici ce n'est pas le même qu'en 3D, il est simplifié: voir doc // on l'appel donc beta_P double beta_P=0.; // init dans le cas où ne le calcul ne fonctionne pas après if (QdeltaPression >= ConstMath::petit) // on ne calcul beta que si la norme de QdeltaSigma est significative // sinon l'opération pow(QdeltaPression,xnp); peut poser pb via le log(QdeltaPression) { beta_P = pow(QdeltaPression,xnp) * unsurwprimeQ0_puiss_np; }; // calcul de la variation de -P / à V T_d_Mpres_d_V = deux_xmu * (1 - beta_P); // -- calcul de dsigma / d_eps if (dsig_deps_ != NULL) {Tenseur3HHHH & dsig_deps = *((Tenseur3HHHH*) dsig_deps_); // le traitement dépends du type de déformation switch (type_de_deformation) {case DEFORMATION_STANDART : case DEFORMATION_POUTRE_PLAQUE_STANDART : // cas d'une déformation d'Almansi { // on a dans ce cas : d I3/ d eps_{ij} = 2 * I3 * g^{ij}, avec I3 = V^2, d'où // d V / d eps_{ij} = V * g^{ij} //Tenseur3HH d_V_d_epsij_HH = V_tdt * gijHH_tdt; // au final : d sig / d eps_{ij} = d sig / d V * d V / d eps_{ij} // mais sig = -P Id = Id * (-P) -> d sig = Id * (- d_P) + (-P) * d_Id d'où // d sig / d eps_{ij} = (- d_P / d V) * (Id tensoriel d V / d eps_{ij}) // + (-P) * (d_Id / d eps_{ij}) // en composante: // d sig^{ij} / d eps_{kl} = (- d_P / d V) * V * (g^{ij} * g^{kl}) // + (-P) * (-2) * (g^{ik}* g^{jl}) I_x_I_HHHH=Tenseur3HHHH::Prod_tensoriel(gijHH_tdt,gijHH_tdt); I_xbarre_I_HHHH=Tenseur3HHHH::Prod_tensoriel_barre(gijHH_tdt,gijHH_tdt); dsig_deps += T_d_Mpres_d_V * I_x_I_HHHH; dsig_deps -= (2. * MPr_t___tdt) * I_xbarre_I_HHHH; }; break; default : cout << "\nErreur : type de deformation qui n'est pas actuellement pris en compte, type= " << Nom_type_deformation(type_de_deformation); cout << "\n Hysteresis_bulk::Dsig_depsilon (... \n"; Sortie(1); }; }; //----- vérife avec dérivée numérique // double divise = delta_V; // if (Dabs(divise) <= ConstMath::pasmalpetit) divise = Signe(divise,ConstMath::pasmalpetit); // double optang_num =(MPr_t___tdt(1,1)-MPr_i___(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; }; // --------------- 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 Hysteresis_bulk::Coincidence(bool & aucun_pt_inversion,double& unSur_wprimeCarre,bool premiere_charge ,SaveResulHysteresis_bulk & 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,bool force_coincidence,const double& MPr_tdt) { // 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 // si cc est positif, cela signifie qu'au pas précédent on n'a pas bien détecté une coincidence // ou que c'est un pb de zero // on considère alors que le point actuel est un point de coïncidence à la précision près et donc // on finit le traitement double tol_coincidence_reelle = tolerance_coincidence; if (tolerance_coincidence < 0) // si l'indication est négative , cela veut dire que l'on veut une grandeur relative tol_coincidence_reelle = MaX(-tolerance_coincidence * (*iafct),-tolerance_coincidence); if ((Abs(W_a - *iafct) <= tol_coincidence_reelle) || force_coincidence || (c >= 0.)) // !!! rajoue du cas c>0 pour voir !!!! {// on est dans le cas où l'on a une coïncidence exacte à la précision "tolerance_coincidence" près // mais dans tous les cas on a rayon_tdt > Q_OiaR sinon on n'aurait pas de coïncidence // on signale qu'il y a une coincidence exacte (pour le retour de la méthode Coincidence) // gestion des pointeurs Gestion_pointeur_coincidence(unSur_wprimeCarre,save_resul,W_a ,aucun_pt_inversion ,iatens,iafct,pt_sur_principal ,iatens_princ,iafct_princ,MPr_tdt); 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_V * delta_MPr_tatdt); double b = 2. * unSur_wprimeCarre * (delta_V * delta_MPr_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 Hysteresis_bulk::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 { if (Permet_affichage() > 0) cout << "\n warning algo coincidence, dans la recherche des racines de l'equa 2 degre " << " la seconde racine " << racine2 << " est superieur a 1" << "\n Hysteresis_bulk::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 { if (Permet_affichage() > 0) 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 if (Permet_affichage() > 0) 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; // normalement racine2 est compris entre 0 et 1 if (Permet_affichage() > 0) {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 Hysteresis_bulk::Coincidence(... "; // on génère une exception throw ErrNonConvergence_loiDeComportement(); } else { cout << "\n nouvelle racine = "<< racine2 ;} ; }; }; // maintenant on traite les infos // -- cas où à l'issue du calcul, racines est très proche de 1 --- // dans ce cas on considère que la coïncidence est exacte if (Dabs((1.-racine2)* delta_MPr_tatdt) <= tol_coincidence_reelle) { // on est dans le cas où l'on a une coïncidence exacte à la précision "tolerance_coincidence" près // mais dans tous les cas on a rayon_tdt > Q_OiaR sinon on n'aurait pas de coïncidence // on signale qu'il y a une coincidence exacte (pour le retour de la méthode Coincidence) // gestion des pointeurs Gestion_pointeur_coincidence(unSur_wprimeCarre,save_resul,W_a ,aucun_pt_inversion ,iatens,iafct,pt_sur_principal ,iatens_princ,iafct_princ,MPr_tdt); return true; }; // calcul de la valeur de sigma au point de coincidence, qui servira pour un nouveau calcul MPr_i___ += racine2 * delta_MPr_tatdt; // calcul du reste d'incrément de déformation qu'il faut utiliser pour la suite delta_V *= (1.-racine2); // mise à jour des différents pointeurs Gestion_pointeur_coincidence(unSur_wprimeCarre,save_resul,W_a ,aucun_pt_inversion ,iatens,iafct,pt_sur_principal ,iatens_princ,iafct_princ,MPr_tdt); // fin et retour, comme le calcul n'est pas fini on retourne false return false; }; // cas particulier d'une inversion et coïncidence : 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 Hysteresis_bulk::Inversion_et_Coincidence(bool & aucun_pt_inversion,double& unSur_wprimeCarre ,SaveResulHysteresis_bulk & save_resul,double& W_a ,List_io ::iterator& iatens,const double& delta_MPr_tatdt ,List_io ::iterator& iafct ,bool& pt_sur_principal,List_io ::iterator& iatens_princ ,List_io ::iterator& iafct_princ ,double& delta_W_a,bool force_coincidence,const double& MPr_tdt) { // on se trouve dans un cas où la fonction d'aide ne peut pas être utilisée car ses valeurs actuelles // n'ont pas pris en compte l'inversion -> du coup on va uniquement travailler avec les pressions // tout d'abord on vérifie que l'on n'est pas exactement à un point de // coincidence à la précision près double tol_coincidence_reelle = tolerance_coincidence; if (tolerance_coincidence < 0) // si l'indication est négative , cela veut dire que l'on veut une grandeur relative tol_coincidence_reelle = MaX(-tolerance_coincidence * MPr_tdt,-tolerance_coincidence); if ((Abs(delta_MPr_Ratdt) <= tol_coincidence_reelle) || force_coincidence) {// on est dans le cas où l'on a une coïncidence exacte à la précision "tolerance_coincidence" près // mais dans tous les cas on a rayon_tdt > Q_OiaR sinon on n'aurait pas de coïncidence // on signale qu'il y a une coincidence exacte (pour le retour de la méthode Coincidence) // gestion des pointeurs Gestion_pointeur_Inversion_et_Coincidence(unSur_wprimeCarre,save_resul,W_a ,aucun_pt_inversion ,iatens,iafct,pt_sur_principal ,iatens_princ,iafct_princ,MPr_tdt); 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 de coïncidence, // en supposant une approximation linéaire de l'évolution de la pression sur le pas de temps: // on va donc séparer la variation de V en fct de la position du pt d'inversion double ratio = Dabs(delta_MPr_Rat / delta_MPr_tatdt); // calcul de la valeur de sigma au point de coincidence, qui servira pour un nouveau calcul // première solution MPr_i___ += ratio * delta_MPr_tatdt; // mais en fait comme on a ici une fonction à une variable, on doit retomber exactement sur la valeur // de référence donc on prend cette valeur MPr_i___ = MPr_R; // calcul du reste d'incrément de déformation qu'il faut utiliser pour la suite delta_V *= (1.-ratio); // mise à jour des différents pointeurs Gestion_pointeur_Inversion_et_Coincidence(unSur_wprimeCarre,save_resul,W_a ,aucun_pt_inversion ,iatens,iafct,pt_sur_principal ,iatens_princ,iafct_princ,MPr_tdt); // fin et retour, comme le calcul n'est pas fini on retourne false return false; }; // gestion d'un dépilement des pointeurs dans les listes, dans le cas d'une coïncidence // met à jour les booléens interne à l'instance : aucun_pt_inversion void Hysteresis_bulk::Gestion_pointeur_coincidence(double& unSur_wprimeCarre ,SaveResulHysteresis_bulk & save_resul,double& W_a ,bool & aucun_pt_inversion ,List_io ::iterator& iatens ,List_io ::iterator& iafct ,bool& pt_sur_principal ,List_io ::iterator& iatens_princ ,List_io ::iterator& iafct_princ ,const double& MPr_tdt) { // 1-- cas de pt de ref: si on pointe sur la liste secondaire on retire les 2 derniers éléments // sauf si on change de sens, dans ce cas il faut dépiler uniquement de 1 if (!pt_sur_principal) { if (iatens != save_resul.MPr_R_t_a_tdt.begin()) { if (Permet_affichage() > 0) cout << "\n incoherence, on devrait avoir ici le premier element de la liste secondaire" << " des Mpr d'inversion" << "\n Hysteresis_bulk::Gestion_pointeur_coïncidence(..."<< endl; if (Permet_affichage() > 4) Affiche_debug(unSur_wprimeCarre,save_resul,iatens,pt_sur_principal ,iatens_princ); // on génère une exception throw ErrNonConvergence_loiDeComportement(); }; // comme on est sur la liste secondaire, cela signifie qu'elle n'est pas vide en point d'inversion // soit on a au moins 2 pt d'inversion, là on peut fermer une boucle // sinon on peut également être dans le cas où on est passer de l'autre coté, c-a-d que l'on a rejoint // la courbe de première charge dans l'autre sens bool moitie_depiler=false; // sert pour un dépilage 1/2 (voir utilisation après) if (save_resul.MPr_R_t_a_tdt.size() >= 2) {// on supprime les éléments inutiles save_resul.MPr_R_t_a_tdt.erase(save_resul.MPr_R_t_a_tdt.begin()); save_resul.MPr_R_t_a_tdt.erase(save_resul.MPr_R_t_a_tdt.begin()); // idem pour la fonction d'aide save_resul.fct_aide_t_a_tdt.erase(save_resul.fct_aide_t_a_tdt.begin()); // on récupère la valeur exacte de la fonction d'aide W_a = *(save_resul.fct_aide_t_a_tdt.begin()); // on met la vrai valeur sans approx // on dépile pour avoir la nouvelle limite de la fonction d'aide save_resul.fct_aide_t_a_tdt.erase(save_resul.fct_aide_t_a_tdt.begin()); } else // sinon on est dans le cas d'une première inversion donc on regarde si on n'est pas passer // de l'autre coté, c-a-d que l'on a rejoint la courbe de première charge dans l'autre sens {double MPr_a_l_inversion = *(save_resul.MPr_R_t_a_tdt.begin()); // la -pression à l'inversion if (MPr_tdt * MPr_a_l_inversion < 0.) // si négatif, on est passé de l'autre coté, c'est ok {// on supprime les éléments inutiles, mais ici il n'y a qu'un seul point //save_resul.MPr_R_t_a_tdt.erase(save_resul.MPr_R_t_a_tdt.begin()); // donc elle est vide save_resul.MPr_R_t_a_tdt.clear(); // même ordre que précédemment mais plus clair // on récupère la valeur exacte de la fonction d'aide W_a = *(save_resul.fct_aide_t_a_tdt.begin()); // on met la vraie valeur sans approx // on dépile pour avoir la nouvelle limite de la fonction d'aide //save_resul.fct_aide_t_a_tdt.erase(save_resul.fct_aide_t_a_tdt.begin()); save_resul.fct_aide_t_a_tdt.clear(); // plus clair } else // sinon là il y a un pb {if (Permet_affichage() > 3) cout << "\n warning il semble que l'on ait une inversion sur liste secondaire puis " << " coïncidence avec liste principale avec " << "\n Hysteresis_bulk::Gestion_pointeur_coïncidence(..." << endl; // cout << "\n incoherence, on devrait avoir au moins 2 element de la liste secondaire" // << " des Mpr d'inversion or on en a: "<< save_resul.MPr_R_t_a_tdt.size() // << ", une raison possible est la valeur de la precision de coincidence qui fait qu'apres une seule" // << " inversion on a tout de suite une coincidence (-> diminuer la tolerance de coincidence ?)" // << "\n fct_aide= "< auparavant on affichait le debug et on génèrait une exception ?? // avec la nouvelle méthode, on entèrine la coincidence et peut-être que l'on va faire du surplace // on supprime les éléments inutiles, mais ici il n'y a qu'un seul point save_resul.MPr_R_t_a_tdt.clear(); // même ordre que précédemment mais plus clair // on récupère la valeur exacte de la fonction d'aide W_a = *(save_resul.fct_aide_t_a_tdt.begin()); // on met la vraie valeur sans approx // on dépile pour avoir la nouvelle limite de la fonction d'aide //save_resul.fct_aide_t_a_tdt.erase(save_resul.fct_aide_t_a_tdt.begin()); save_resul.fct_aide_t_a_tdt.clear(); // plus clair // if ( ((Permet_affichage()==0) && (ParaGlob::NiveauImpression() > 4)) || (abs(Permet_affichage()) > 4)) // Affiche_debug(unSur_wprimeCarre,save_resul,iatens,pt_sur_principal // ,iatens_princ); // // on génère une exception // throw ErrNonConvergence_loiDeComportement(); // je pense que cela veut dire également que l'on doit dépiler une fois en plus sur la liste principale // car en fait on vient de rattraper la liste à la précision de la fonction d'aide du coup la coïncidence // est avec la liste principale moitie_depiler = true; }; }; if (save_resul.MPr_R_t_a_tdt.size()==0) {// il ne reste plus d'élément on revient sur la liste principal pt_sur_principal = true; List_io ::iterator posi0=save_resul.MPr_R.end(); posi0--; // on initialise les pointeurs courants sur les valeurs de la liste principale iatens = iatens_princ;iafct=iafct_princ; if (moitie_depiler) // cas d'un dépilage qui n'est pas fini c'est à dire en fait d'une // coïncidence avec la liste principale: on dépile une fois si c'est possible { if (iatens != posi0) // cela veut dire que l'on est pas sur la première charge {iatens++; // contrairement au point de ref, la fonction d'aide est a dépiler qu'une fois // car la valeur en cours vient de rattraper la précédente qui donc doit-être annulée // et on revient sur celle d'avant iafct++; // // #ifdef MISE_AU_POINT // double W_a_save = W_a; // sauvegarde pour pouvoir l'afficher // #endif W_a = *iafct; // on met la vrai valeur sans approx save_resul.indic_coin.push_back(1); // un seul dépilage à faire save_resul.nb_coincidence++; // on acte la coïncidence // comme on est sur la liste principale, on met à jour les pointeurs de la liste principale // parce que l'on a dépilé 1 fois sur la liste principale iatens_princ = iatens;iafct_princ = iafct; } }; // on regarde si le pointeur est valide // on regarde si le pointeur est valide // on récupère la position de la valeur par défaut if (iatens != posi0) {aucun_pt_inversion = false;} else {aucun_pt_inversion = true;}; } else // cas où il reste des éléments sur les listes secondaires {iatens = save_resul.MPr_R_t_a_tdt.begin();aucun_pt_inversion = false; iafct = save_resul.fct_aide_t_a_tdt.begin(); }; } else // cas où on pointe sur la liste principal, on se déplace dans la liste principal, // de deux en deux, normalement sauf si on change de sens { // sans éliminer d'élément : on update le pointage // on récupère la position de la valeur par défaut des pt d'inversion List_io ::iterator posi0=save_resul.MPr_R.end(); posi0--;//posi0--; // if (iatens != save_resul.MPr_R.end()) // -- on regarde tout d'abord si on n'a pas changé de sens // récup de la -pression d'inversion la plus grande sur la courbe de première charge double MPr_a_l_inversion = *(save_resul.MPr_R.begin()); //(*posi0); List_io ::iterator posi1 = iatens; posi1++; // posi1 c'est la future contrainte List_io ::iterator iafact_posi0=save_resul.fct_aide.end(); iafact_posi0--; List_io ::iterator iafact_posi1 = iafct; iafact_posi1++; if ((MPr_tdt * MPr_a_l_inversion < 0.) && (((iatens != posi0) // cela veut dire que l'on était pas sur la première charge && (posi1 == posi0) // permet de vérifier que l'on vient bien de rattraper la courbe de 1ière && (iafact_posi1 == iafact_posi0)) ) ) // si négatif, on est passé de l'autre coté, c'est ok // par contre il faut dépiler uniquement qu'une fois si on a une qu'une seule inversion // Dans le cas où on a plusieurs inversions enregistrées, il faut d'abord dépiler jusqu'à // la première non infinie, pour ensuite calculer correctement le retour sur la branche principale // dans la direction inverse. C'est notament le cas, quand le pas est beaucoup plus grand que // les boucles enregistrées // { // normalement cela veut dire que l'on vient de rattraper la courbe principale dans l'autre sens // 1) cas où on revient dans la configuration originale // if ((iatens != posi0) // cela veut dire que l'on était pas sur la première charge // && (posi1 == posi0) // permet de vérifier que l'on vient bien de rattraper la courbe de 1ière // && (iafact_posi1 == iafact_posi0)) {iatens=posi0;aucun_pt_inversion = true; // idem pour iafct qui suivent une même logique: on doit avoir rattraper la valeur initiale iafct=iafact_posi0; W_a = *iafct; // on met la valeur initiale save_resul.nb_coincidence++; // on acte la coïncidence save_resul.indic_coin.push_back(1); // signale la coincidence avec un seul dépilage // comme on est sur la liste principale, on met à jour les pointeurs de la liste principale iatens_princ = iatens;iafct_princ = iafct; } // else // sinon il y a un pb car comme c'est une coïncidence il devrait rester un pointeur valide // {if ( ((Permet_affichage()==0) && (ParaGlob::NiveauImpression() > 3)) || (abs(Permet_affichage()) > 0)) // {cout << "\n incoherence, tout ce passe comme si on changeait de sens sur la courbe principale mais: "; // if (iatens == posi0) // cout << " \n on est deja sur la courbe principale !! "; // if (posi1 != posi0) // cout << " \n concernant les contraintes: on n'est pas sur la premiere courbe d'inversion !! "; // if (iafact_posi1 == iafact_posi0) // cout << " \n concernant la fonction d'aide: on n'est pas sur la premiere courbe d'inversion !! "; // cout << "\n Hysteresis_bulk::Gestion_pointeur_coincidence(..." << endl; // }; // if ( ((Permet_affichage()==0) && (ParaGlob::NiveauImpression() > 4)) || (abs(Permet_affichage()) > 4)) // Affiche_debug(unSur_wprimeCarre,save_resul,iatens,pt_sur_principal // ,iatens_princ); // // on génère une exception // throw ErrNonConvergence_loiDeComportement(); // }; } else // sinon on est dans un cas où on reste du même coté définitivement // ou transitoirement le temps de rattraper la dernière inversion // donc il faut dépiler 2 fois { if (iatens != posi0) // cela veut dire que l'on est pas sur la première charge {iatens++; // contrairement au point de ref, la fonction d'aide est a dépiler qu'une fois // car la valeur en cours vient de rattraper la précédente qui donc doit-être annulée // et on revient sur celle d'avant iafct++; // #ifdef MISE_AU_POINT double W_a_save = W_a; // sauvegarde pour pouvoir l'afficher #endif W_a = *iafct; // on met la vrai valeur sans approx if (iatens == posi0) { if (Permet_affichage() > 0) cout << "\n warning: Hysteresis_bulk::Gestion_pointeur_coincidence(.." << " on cherche a appliquer deux depilages, alors qu'un seul n'est possible !! " << " peut-etre un probleme de precision de la coincidence (diminuer la tolerance ?) "; // #ifdef MISE_AU_POINT // cout << "\n debug Hysteresis_bulk::Gestion_pointeur_coincidence( " // << " W_a= "< 4) Affiche_debug(unSur_wprimeCarre,save_resul,iatens,pt_sur_principal ,iatens_princ); // on génère une exception throw ErrNonConvergence_loiDeComportement(); }; }; }; // 3-- traitement particulier dans le cas où on a rejoint la première charge if (aucun_pt_inversion) // cas où l'on est sur la courbe de première charge { wprime = 1; unSur_wprimeCarre = 1./(wprime*wprime); W_a = *save_resul.ip2; // on récupère la valeur de la fct d'aide au deuxième iafct = save_resul.ip2; } else // on met à jour en indiquant que l'on a rattrapé une courbe {//W_a = *iafct; wprime = 2; unSur_wprimeCarre = 1./(wprime*wprime); }; // gestion du paramètre "modif" de saveresult bool inversion = false; Gestion_para_Saveresult_Modif(pt_sur_principal,save_resul,inversion); //--------débug- traçage #ifdef MISE_AU_POINT if (Permet_affichage() == -77) { cout << "\ngestion de pointeur de coïncidence "; cout << " aucun_pt_inversion= " << aucun_pt_inversion << " save_resul.nb_coincidence= " << save_resul.nb_coincidence; cout << " \nsigR_sur_principal= " << pt_sur_principal << " save_resul.modif= " << save_resul.modif << " nb_sigR= "< 4) Affiche_debug(unSur_wprimeCarre,save_resul,iatens,pt_sur_principal ,iatens_princ); // on génère une exception throw ErrNonConvergence_loiDeComportement(); }; // comme on est sur la liste secondaire, cela signifie qu'elle n'est pas vide en point d'inversion // on a au moins 1 pt d'inversion, là on peut fermer une boucle // Important: le cas où on est passer de l'autre coté, c-a-d que l'on a rejoint // la courbe de première charge dans l'autre sens, n'est pas prévu et n'est pas normal a priori // --> message d'erreur // on commence par vérifier que l'on n'est pas passé de l'autre coté {double MPr_a_l_inversion = *(save_resul.MPr_R_t_a_tdt.begin()); // la -pression à l'inversion if (MPr_tdt * MPr_a_l_inversion < 0.) // si négatif, on est passé de l'autre coté, ce n'est pas normal // on met un message d'erreur {if (Permet_affichage() > 0) cout << "\n incoherence sur la liste secondaire, on a change de sens au niveau de la coincidence or " << " ce cas n'est pas prevu lors d'une inversion-coincidence !! " << "\n Hysteresis_bulk::Gestion_pointeur_Inversion_et_Coincidence(..." << endl; if (Permet_affichage() > 4) Affiche_debug(unSur_wprimeCarre,save_resul,iatens,pt_sur_principal ,iatens_princ); // on génère une exception throw ErrNonConvergence_loiDeComportement(); }; }; // --- maintenant on dépile une fois // on supprime les éléments inutiles save_resul.MPr_R_t_a_tdt.erase(save_resul.MPr_R_t_a_tdt.begin()); // idem pour la fonction d'aide // on récupère la valeur exacte de la fonction d'aide W_a = *(save_resul.fct_aide_t_a_tdt.begin()); // on met la vrai valeur sans approx // on dépile pour avoir la nouvelle limite de la fonction d'aide save_resul.fct_aide_t_a_tdt.erase(save_resul.fct_aide_t_a_tdt.begin()); // on regarde s'il ne faut pas revenir sur la liste principal if (save_resul.MPr_R_t_a_tdt.size()==0) {// il ne reste plus d'élément on revient sur la liste principal pt_sur_principal = true; iatens = iatens_princ; iafct=iafct_princ; // on regarde si le pointeur est valide // on récupère la position de la valeur par défaut List_io ::iterator posi0=save_resul.MPr_R.end(); posi0--; if (iatens != posi0) {aucun_pt_inversion = false;} else {aucun_pt_inversion = true;}; } else // cas où il reste des éléments sur les listes secondaires {iatens = save_resul.MPr_R_t_a_tdt.begin();aucun_pt_inversion = false; iafct = save_resul.fct_aide_t_a_tdt.begin(); }; } else if (save_resul.MPr_R.size() == 1) // cas où on pointe sur la liste principal, mais en fait on n'a pas enregistré de nouveau pt de ref // c'est le cas au début du chargement par exemple, donc ici on ne peut pas supprimer de point ! { // comme on est repassé par l'origine, il faut remettre à 0 wa // et s'il y a un morceau encore de calcul, le wa sera updaté du morceau restant W_a = 0.; // le point de coïncidence c'est en fait l'origine // à part cela, il n'y a rien n'a faire, car pour l'instant il n'y a rien d'enregistré dans les listes // donc les pointeurs ne sont pas à changer fin_de_traitement_a_faire = false; // on indique que c'est fini } else // cas où on pointe sur la liste principal, { // on se déplace dans la liste principal de 1, sans éliminer d'élément : on update le pointage // on récupère la position de la valeur par défaut des pt d'inversion List_io ::iterator posi0=save_resul.MPr_R.end(); posi0--; // -- on regarde tout d'abord si on n'a pas changé de sens // ce cas normalement n'est pas prévu dans le cas d'une inversion-coïncidence donc // on met un message d'erreur // récup de la -pression d'inversion la plus grande sur la courbe de première charge double MPr_a_l_inversion = *(save_resul.MPr_R.begin()); //(*posi0); List_io ::iterator posi1 = iatens; posi1++; // posi1 c'est la future contrainte List_io ::iterator iafact_posi0=save_resul.fct_aide.end(); iafact_posi0--; List_io ::iterator iafact_posi1 = iafct; iafact_posi1++; if ((MPr_tdt * MPr_a_l_inversion < 0.) && (((iatens != posi0) // cela veut dire que l'on était pas sur la première charge && (posi1 == posi0) // permet de vérifier que l'on vient bien de rattraper la courbe de 1ière && (iafact_posi1 == iafact_posi0)) ) ) // si négatif, on est passé de l'autre coté, {if (Permet_affichage() > 0) cout << "\n incoherence sur la liste principale, on a change de sens au niveau de la coincidence or " << " ce cas n'est pas prevu lors d'une inversion-coincidence !! " << "\n Hysteresis_bulk::Gestion_pointeur_Inversion_et_Coincidence(..." << endl; if (Permet_affichage() > 4) Affiche_debug(unSur_wprimeCarre,save_resul,iatens,pt_sur_principal ,iatens_princ); // on génère une exception throw ErrNonConvergence_loiDeComportement(); } else // sinon on est dans un cas où on reste du même coté définitivement // ou transitoirement le temps de rattraper la dernière inversion // donc il faut dépiler 1 fois { if (iatens != posi0) // cela veut dire que l'on est pas sur la première charge {iatens++; W_a = *iafct; // on met la vrai valeur sans approx : 10 nov 2017 iafct++; // // W_a = *iafct; // on met la vrai valeur sans approx : 10 nov 2017 if (iatens != posi0) {aucun_pt_inversion = false;} else {aucun_pt_inversion = true;}; save_resul.indic_coin.push_back(1); // un seul dépilage save_resul.nb_coincidence++; // on acte la coïncidence // comme on est sur la liste principale, on met à jour les pointeurs de la liste principale iatens_princ = iatens;iafct_princ = iafct; } else // sinon il y a un pb car comme c'est une coïncidence il devrait rester un pointeur valide {if (Permet_affichage() > 0) cout << "\n incoherence, il devrait rester un pointeur de valide sur la liste de pt de ref" << "\n Hysteresis_bulk::Gestion_pointeur_Inversion_et_Coincidence(..." << endl; if (Permet_affichage() > 4) Affiche_debug(unSur_wprimeCarre,save_resul,iatens,pt_sur_principal ,iatens_princ); // on génère une exception throw ErrNonConvergence_loiDeComportement(); }; }; }; if (fin_de_traitement_a_faire) {// 3-- traitement particulier dans le cas où on a rejoint la première charge if (aucun_pt_inversion) // cas où l'on est sur la courbe de première charge { wprime = 1; unSur_wprimeCarre = 1./(wprime*wprime); W_a = *save_resul.ip2; // on récupère la valeur de la fct d'aide au deuxième iafct = save_resul.ip2; } else // on met à jour en indiquant que l'on a rattrapé une courbe {wprime = 2; unSur_wprimeCarre = 1./(wprime*wprime); }; // gestion du paramètre "modif" de saveresult bool inversion = false; Gestion_para_Saveresult_Modif(pt_sur_principal,save_resul,inversion); }; //--------débug- traçage #ifdef MISE_AU_POINT if (Permet_affichage() == -77) { cout << "\ngestion de pointeur de coïncidence: cas particulier d'une inversion - coincidence "; cout << " aucun_pt_inversion= " << aucun_pt_inversion << " save_resul.nb_coincidence= " << save_resul.nb_coincidence; cout << " \nsigR_sur_principal= " << pt_sur_principal << " save_resul.modif= " << save_resul.modif << " nb_sigR= "< a la saturation = "<< Qzero << "\n ->arbitrairement on limite a la saturation " << endl; MPr_t___tdt = DSigne(MPr_t___tdt) * Qzero; delta_MPr_Ratdt = MPr_t___tdt - MPr_R; }; fin_traitement = true; // la suite sera à supprimer, car on effectue tout à la fin // save_resul.MPr_tdt = MPr_t___tdt; // save_resul.fonction_aide_tdt = W_a; // save_resul.wprime_tdt = wprime; // // calcul de la contrainte de retour // sigHH = gijHH * MPr_t___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(aucun_pt_inversion,unSur_wprimeCarre,false,save_resul,W_a,iatens ,iafct,pt_sur_principal,iatens_princ,iafct_princ,delta_W_a,false,MPr_t___tdt); } else // coincidence avec la courbe de première charge { fin_traitement = Coincidence(aucun_pt_inversion,unSur_wprimeCarre,true,save_resul,W_a,iatens ,iafct,pt_sur_principal,iatens_princ,iafct_princ,delta_W_a,false,MPr_t___tdt); }; // la suite est commentée et devra être supprimée (si pas de pb) car le traitement est effectué uniquement une fois // tout à la fin // if (fin_traitement) // calcul de la contrainte de retour si le traitement est fini // { // --- avant d'enregistrer on filtre si on a dépassé la saturation, ------ // // sinon l'erreur va rester pour toute la suite du calcul // if (Dabs(MPr_t___tdt) > Qzero) // {if (((Permet_affichage()==0) && (ParaGlob::NiveauImpression() > 3)) // || (abs(Permet_affichage()) > 3)) // cout << "\n ** warning dans le calcul de la contrainte a t+dt, on trouve " // << " une valeur de pression -P= (" << MPr_t___tdt // << ") > a la saturation = "<< Qzero // << "\n ->arbitrairement on limite a la saturation " // << endl; // MPr_t___tdt = DSigne(MPr_t___tdt) * Qzero; // }; // sigHH = gijHH * MPr_t___tdt; // save_resul.MPr_tdt = MPr_t___tdt; //////--- debug --- ////#ifdef MISE_AU_POINT //// { cout << "\n debug Hysteresis_bulk::Avancement_temporel( " //// << " à la sauvegarde 2 : MPr_t___tdt= "<< MPr_t___tdt << endl; //// }; ////#endif //////----- fin debug ---- // save_resul.fonction_aide_tdt = W_a; // save_resul.wprime_tdt = wprime; // }; //--------débug- traçage #ifdef MISE_AU_POINT if (Permet_affichage() == -77) { cout << "\ncas d'une coïncidence "; cout << " fin_traitement = " << fin_traitement << " save_resul.modif= " << save_resul.modif << " save_resul.nb_coincidence= " << save_resul.nb_coincidence << endl ; }; #endif //----- fin débug } // -- fin du else du if (phi_1_dt<0.) : évolution normale } // -- fin du else du if (phi_1_dt<0.) : point d'inversion else // cas où on force la fin du traitement: force_fin_traitement = true {fin_traitement = true; double phidt = delta_MPr_Ratdt * delta_V; // dans l'expression suivante, on utilise la règle des trapèzes pour le calcul de l'incrément delta_W_a = (phi_1_dt + phidt) * unSur_wprimeCarre; W_a += delta_W_a; // et ici il y a forcément plus de 1 point d'inversion if (W_a <= *(save_resul.ip2)) { // --- cas d'une coincidence quelconque ou sur première charge --- Coincidence(aucun_pt_inversion,unSur_wprimeCarre,false,save_resul,W_a,iatens ,iafct,pt_sur_principal,iatens_princ,iafct_princ,delta_W_a,false,MPr_t___tdt); // la suite est commentée et devra être supprimée (si pas de pb) car le traitement est effectué uniquement une fois // tout à la fin // // calcul de la contrainte de retour si le traitement est fini // sigHH = gijHH * MPr_t___tdt; } else // coincidence avec la courbe de première charge { Coincidence(aucun_pt_inversion,unSur_wprimeCarre,true,save_resul,W_a,iatens ,iafct,pt_sur_principal,iatens_princ,iafct_princ,delta_W_a,false,MPr_t___tdt); // la suite est commentée et devra être supprimée (si pas de pb) car le traitement est effectué uniquement une fois // tout à la fin // // calcul de la contrainte de retour si le traitement est fini // sigHH = gijHH * MPr_t___tdt; }; //--------débug- traçage #ifdef MISE_AU_POINT if (Permet_affichage() == -77) { cout << "\non force la fin du traitement et la coincidence "; cout << " fin_traitement = " << fin_traitement << " save_resul.modif= " << save_resul.modif << endl ; }; #endif //----- fin débug }; // 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 > Abs(nb_maxInvCoinSurUnPas)) { // dans ce cas cela signifie qu'il y a pb et on arrête en générant éventuellement // une interruption d'erreur de convergence dans une loi de comportement if (Permet_affichage() > 3) cout << "\n warning dans le calcul de la contrainte a t+dt, le nombre de coincidence et/ou" << " d'inversion (" << nb_coin_inver << ") est superieur a la limite fixee: = " << nb_maxInvCoinSurUnPas << endl; // si on est toujours sur la liste principale cela veut dire // que l'on a créé une secondaire puis on la supprimé etc ... boucle infini if (pt_sur_principal) { // on est dans un cas particulier où l'algo fait du sur-place: un coup il trouve une coïncidence // puis le coup après il trouve une inversion --> il revient au même endroit --> boucle infini // dans ce cas on décide de manière arbitraire a imposer la coïncidence ce qui garantie la cohérence de la // succession des rayons on contraire d'imposer l'inversion, qui conduira à un rayon incohérent (ce qui conduit // normalement tout de suite après à une coïncidence) force_fin_traitement = true; if (Permet_affichage() > 3) cout << " on est dans le cas d'aller-retour identiques !! on impose la coincidence " << endl; } else // on regarde s'il faut imposer la valeur finale obtenue ou générer une erreur { if (nb_maxInvCoinSurUnPas > 0) { throw ErrNonConvergence_loiDeComportement();break;} // envoie d'un signal a traiter en dehors else { force_fin_traitement = true; if (Permet_affichage() > 3) cout << " on impose la coincidence " << endl; }; // cas où on impose coûte que coûte (résultat pas sûr !!) }; }; }; // -- fin du While (!fin_traitement) // arrivée ici on considère que la contrainte calculée est correcte // on calcul la contrainte de retour // --- avant d'enregistrer on filtre si on a dépassé la saturation, ------ // sinon l'erreur va rester pour toute la suite du calcul if (Dabs(MPr_t___tdt) > Qzero) {if (Permet_affichage() > 3) cout << "\n ** hysteresis bulk: warning dans le calcul de la contrainte a t+dt, on trouve " << " une valeur de pression -P= (" << MPr_t___tdt << ") > a la saturation = "<< Qzero << "\n ->arbitrairement on limite a la saturation " << endl; // throw ErrNonConvergence_loiDeComportement(); // pour le débug MPr_t___tdt = DSigne(MPr_t___tdt) * Qzero; delta_MPr_Ratdt = MPr_t___tdt - MPr_R; }; // la contrainte de retour sigHH = gijHH * MPr_t___tdt; // puis la sauvegarde dans save_resul save_resul.MPr_tdt = MPr_t___tdt; save_resul.fonction_aide_tdt = W_a; save_resul.wprime_tdt = wprime; //--------débug- traçage #ifdef MISE_AU_POINT if (Permet_affichage() == -77) { cout << " fin_traitement = " << fin_traitement << " save_resul.modif= " << save_resul.modif << " save_resul.nb_coincidence= " << save_resul.nb_coincidence << endl ; }; #endif }; /* // **********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------- */