// 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 "ElFrontiere.h" #include "ParaAlgoControle.h" #include "Util.h" //---------------------------------------------------------------- // def des donnees commune a tous les elements //---------------------------------------------------------------- unsigned int ElFrontiere::nrand = 0; // CONSTRUCTEURS : // par defaut ElFrontiere::ElFrontiere () : tabNoeud(),ddlElem(),tabfront(),encomb_min(),encomb_max() { }; // fonction du tableau des noeuds sommet ElFrontiere::ElFrontiere ( const Tableau & tab, const DdlElement& TddlElem,int nbnoeud ) : tabNoeud(tab),ddlElem(TddlElem),tabfront() ,encomb_min(ParaGlob::Dimension()),encomb_max(ParaGlob::Dimension()) { #ifdef MISE_AU_POINT if ((tabNoeud.Taille() != nbnoeud) || (ddlElem.NbNoeud() != nbnoeud)) { cout << "\nErreur : mauvais dimensionnement de tableau" << " \n nbnoeud = " << nbnoeud << ", dim tableau de noeud " << tabNoeud.Taille() << " , dim ddlelem " << ddlElem.NbNoeud() ; cout << "\nElFrontiere::ElFrontiere (int nbnoeud,Tableau &" << " tab,DdlElement& TddlElem,int nbnoeud ) " << endl; Sortie(1); }; #endif // définition de la boite d'encombrement AjourBoiteEncombrement(); }; // de copie ElFrontiere::ElFrontiere( const ElFrontiere& a) : tabNoeud(a.tabNoeud),ddlElem(a.ddlElem),tabfront(a.tabfront) ,encomb_min(a.encomb_min),encomb_max(a.encomb_max) { }; // DESTRUCTEUR : ElFrontiere::~ElFrontiere () {}; // surcharge de l'affectation ElFrontiere& ElFrontiere::operator = ( const ElFrontiere& a) { this->tabNoeud = a.tabNoeud; this->ddlElem = a.ddlElem; this->tabfront = a.tabfront; encomb_min=a.encomb_min;encomb_max=a.encomb_max; return *this; }; // surcharge des tests // ne concerne que le tableau de noeud de l'élément frontière !! bool ElFrontiere::operator == ( const ElFrontiere& a) const { // on ne teste pas l'encombrement car il dépend des points donc a priori en découle // test ok si même maillage et même type d'élément if ( (this->TypeFrontiere() == a.TypeFrontiere()) && (tabNoeud(1)->Num_Mail() == a.tabNoeud(1)->Num_Mail())) {int ta = this->tabNoeud.Taille(); // nb de noeud switch (ta) { case 1 : // cas d'une frontière point, on simplifie if (this->tabNoeud(1) == a.tabNoeud(1)) {return true;} else {return false;} break; case 2 : // cas où la frontière est à 2 noeuds uniquement {if (this->tabNoeud(1) == a.tabNoeud(1)) {if (this->tabNoeud(2) == a.tabNoeud(2)) {return true;} else {return false;}} else if (this->tabNoeud(1) == a.tabNoeud(2)) {if (this->tabNoeud(2) == a.tabNoeud(1)) {return true;} else {return false;}} else {return false;}; break; } default : // les autres cas {// récup de l'élément géométrique const ElemGeomC0 & elemgeom = this->ElementGeometrique(); const Tableau & ind = elemgeom.Ind(); // récup du tableau des tranches int nb_tranche = ind.Taille(); // le nombre d'intervalle de numéros de noeuds constituant la numérotation // vérification que la taille de toutes les tranches est au moins supérieure à 1 #ifdef MISE_AU_POINT if (nb_tranche == 0) {cout << "\n *** erreur, pas de tranche de nb de noeud definie " << "\n ElFrontiere::MemeNoeud(... " << endl; Sortie(1); }; for (int i1=1;i1<=nb_tranche;i1++) if (ind(i1) < 1) {cout << "\n *** erreur, une tranche de nb de noeud est nulle " << " tableau ind: " << ind << "\n ElFrontiere::MemeNoeud(... " << endl; Sortie(1); }; #endif // on balaie chaque intervalle int deb_intervalle = 0; int fin_intervalle = 0; // init for (int iter =1;iter<= nb_tranche;iter++) { int tranche = ind(iter); deb_intervalle = fin_intervalle+1; // mise à jour des bornes de l'intervalle de scrutation fin_intervalle += tranche; // " " // on commence par chercher un premier noeud identique dans l'intervalle bool res = false; int nd; // indice du cote this Noeud * ptNoeud_a = a.tabNoeud(deb_intervalle); for (nd=deb_intervalle; nd<= fin_intervalle; nd++) {if (this->tabNoeud(nd) == ptNoeud_a) { res = true; break; }; }; if (!res) // on arrête de suite si {return false;}; // on n'a pas trouvé de premier noeud !! // s'il n'y a qu'un seule noeud dans la tranche, on a finit cette tranche sinon on continue if (tranche > 1) { // on regarde dans quel sens il faut tourner int ndplus = nd + 1; if (ndplus > fin_intervalle) ndplus -= tranche; int ndmoins = nd - 1; if (ndmoins < deb_intervalle) ndmoins += tranche; if (this->tabNoeud(ndplus) == a.tabNoeud(deb_intervalle+1)) {// on est dans le bon sens en augmentant, et c'est ok pour le 2ième noeud, // continue que s'il y a plus de 2 noeuds dans la tranche if (tranche > 2) { for (int i=1;i<= (tranche-2);i++) { ndplus++; if (ndplus > fin_intervalle) ndplus -= tranche; if (this->tabNoeud(ndplus) != a.tabNoeud(deb_intervalle+i+1)) return false; }; }; // sinon ok, on vient de balayer tous les noeuds, on continue } // le sens 1 ne marche pas , on regarde l'autre sens else if (this->tabNoeud(ndmoins) == a.tabNoeud(deb_intervalle+1)) {// le bon sens est finalement en diminuant // continue que s'il y a plus de 2 noeuds dans la tranche if (tranche > 2) { for (int i=1;i<= (tranche-2);i++) { ndmoins--; if (ndmoins < deb_intervalle) ndmoins += tranche; if (this->tabNoeud(ndmoins) != a.tabNoeud(deb_intervalle+i+1)) return false; }; }; // sinon ok, on vient de balayer tous les noeuds, on continue } else // sinon ne marche pas dans les deux sens { return false;}; }; }; // fin de la boucle sur les tranches }; // fin du cas courant (default du switch) }; // fin du switch sur le nombre de noeuds } else // le type est different et ou le numéro de maillage est différent return false; // si on arrive ici, cela veut dire que toutes les égalités sont bonnes pour un cas autre que // point ou frontière à 2 noeuds return true; }; // retourne un element frontiere ayant une orientation opposee ElFrontiere * ElFrontiere::Oppose() const { int tail = tabNoeud.Taille(); // dim du tableau de noeud Tableau tab(tail); // récup de l'élément géométrique attaché à l'élément frontière const ElemGeomC0 & elemgeom = this->ElementGeometrique(); // récup du tableau de connection de l'inverse de l'élément const Tableau t_inv = elemgeom.InvConnec(); // on definit le tableau tab for (int i=1;i<= tail;i++) tab(i) = tabNoeud(t_inv(i)); // construction des ddl de l'élément Tableau tt(tail); for (int j=1;j<= tail;j++) tt(j) = ddlElem.NbDdl(t_inv(j)); DdlElement dElem(tail,tt); for (int k=1;k<= tail;k++) dElem.Change_un_ddlNoeudElement(k,ddlElem(t_inv(k))); // création de l'élément frontière inverse ElFrontiere * pt = this->NevezElemFront(tab,dElem); return pt; }; // calcul éventuel de la normale à un noeud // ce calcul existe pour les éléments 2D, 1D axi, et aussi pour les éléments 1D // qui possède un repère d'orientation // en retour coor = la normale si coor.Dimension() est = à la dimension de l'espace // si le calcul n'existe pas --> coor.Dimension() = 0 // ramène un entier : // == 1 : calcul normal // == 0 : problème de calcul -> coor.Dimension() = 0 // == 2 : indique que le calcul n'est pas licite pour le noeud passé en paramètre // mais il n'y a pas d'erreur, c'est seulement que l'élément n'est pas ad hoc pour // calculer la normale à ce noeud là // temps: indique à quel moment on veut le calcul int ElFrontiere::CalculNormale_noeud(Enum_dure temps,const Noeud& noe,Coordonnee& coor) { int retour = 1; // init du retour : on part d'un bon a priori Enum_type_geom enutygeom = Type_geom_front(); int dima = ParaGlob::Dimension(); // on exclue les cas à pb if ( ((dima == 3) && (!ParaGlob::AxiSymetrie()) && ((enutygeom == LIGNE)||(enutygeom == POINT_G))) || ((dima == 3) && (ParaGlob::AxiSymetrie()) && (enutygeom == POINT_G)) || ((dima == 2) && (enutygeom == POINT_G)) ) // on n'a pas d'information particulière pour calculer la normale // donc on ne peut pas calculer { retour = 2; } else // sinon le calcul est possible { // on commence par repérer le noeud dans la numérotation locale int nuoe=0; int borne_nb_noeud=tabNoeud.Taille()+1; for (int i=1;i< borne_nb_noeud;i++) {Noeud& noeloc = *tabNoeud(i); if ( (noe.Num_noeud() == noeloc.Num_noeud()) && (noe.Num_Mail() == noeloc.Num_Mail()) ) {nuoe = i; break; }; }; // on ne continue que si on a trouvé le noeud if (nuoe != 0) { ElemGeomC0& elemgeom = ElementGeometrique(); // récup de la géométrie // récup des coordonnées locales du noeuds const Coordonnee& theta_noeud = elemgeom.PtelemRef()(nuoe); // récup de la métrique associée à l'élément Met_abstraite * met = this->Metrique(); // récup des phi et dphi au noeud const Vecteur & phi = elemgeom.Phi_point(theta_noeud); const Mat_pleine& dphi = elemgeom.Dphi_point(theta_noeud); switch (temps) {case TEMPS_0 : {const BaseB& baseB = met->BaseNat_0(tabNoeud,dphi,phi); coor = Util::ProdVec_coor( baseB.Coordo(1), baseB.Coordo(2)); coor.Normer(); break; } case TEMPS_t : {const BaseB& baseB = met->BaseNat_t(tabNoeud,dphi,phi); coor = Util::ProdVec_coor( baseB.Coordo(1), baseB.Coordo(2)); coor.Normer(); break; } case TEMPS_tdt : {const BaseB& baseB = met->BaseNat_tdt(tabNoeud,dphi,phi); coor = Util::ProdVec_coor( baseB.Coordo(1), baseB.Coordo(2)); coor.Normer(); break; } default : cout << "\nErreur : valeur incorrecte du temps demande = " << Nom_dure(temps) << " !\n"; cout << "\n ElFrontiere::CalculNormale_noeud(Enum_dure temps... \n"; retour = 0; Sortie(1); }; } else {cout << "\n *** erreur le noeud demande num= "< 0) : calculée à l'aide // d'une triangulation, puis des produits vectoriels // ramène une valeur nulle, s'il n'y a pas de surface double ElFrontiere::SurfaceApprox() { // si on est en dimension 1, les frontières ne peuvent pas être des surfaces if (ParaGlob::Dimension() == 1) return 0; // on commence par récupérer la triangulation ElemGeomC0 const & elemGeom = this->ElementGeometrique(); // si c'est un élément de dimension 1, il n'y a également pas de notion de surface if (elemGeom.Dimension() == 1) return 0; // maintenant il ne reste que les frontières de dimension 2 dans un espace 2 ou 3 const Tableau > >& tab_tri_lin = elemGeom.Trian_lin(); const Tableau >& tab_tri_lin_1 = tab_tri_lin(1); // car l'élément a une face int nb_tri = tab_tri_lin.Taille(); double surface=0.; // init for (int i=1; i<= nb_tri; i++) { // calcul des deux vecteurs représentants les cotés du triangle Coordonnee AB = tabNoeud(tab_tri_lin_1(i)(2))->Coord2() - tabNoeud(tab_tri_lin_1(i)(1))->Coord2(); Coordonnee AC = tabNoeud(tab_tri_lin_1(i)(3))->Coord2() - tabNoeud(tab_tri_lin_1(i)(1))->Coord2(); // surface de la facette surface += 0.5 * Dabs(Util::Determinant(AB,AC)); }; return surface; }; // ramène la distance maxi entre deux noeuds de l'élément à tdt double ElFrontiere::MaxDiagonale_tdt() { double maxdiagonale = 0; int nbnoeud=tabNoeud.Taille(); for (int i=1;i<=nbnoeud;i++) for (int j=i+1; j<= nbnoeud; j++) { double dist= (tabNoeud(i)->Coord2() - tabNoeud(j)->Coord2()).Norme(); maxdiagonale = MaX(maxdiagonale,dist); }; return maxdiagonale; }; // met à jour la boite d'encombrement void ElFrontiere::AjourBoiteEncombrement() { int tab_taille= tabNoeud.Taille(); for (int i=1;i<=tab_taille;i++) { if (tabNoeud(i)->ExisteCoord1()) // on travaille sur la taille actuelle { Coordonnee co=tabNoeud(i)->Coord1(); encomb_min.Modif_en_min(co);encomb_max.Modif_en_max(co); } else // on travaille avec la taille initiale { Coordonnee co=tabNoeud(i)->Coord0(); encomb_min.Modif_en_min(co); encomb_max.Modif_en_max(co); } } // maintenant on tiend compte d'un facteur majorant pour incertitude Coordonnee delta=(encomb_max- encomb_min) * (ParaGlob::param->ParaAlgoControleActifs().Extra_boite_prelocalisation()-1.); // dans le cas de surface plane on peut avoir une boite avec une épaisseur nulle, // comme il s'agit d'une boite d'encombrement assez grossière on ajoute une valeur par défaut // par défaut on prend une valeur par défaut du max de delta double miniajout = (ParaGlob::param->ParaAlgoControleActifs().Rapport_Extra_boite_mini_prelocalisation()) * delta.Max_val_abs(); delta.Ajout_meme_valeur(miniajout); // mise à jour de l'encombrement encomb_min -= delta; encomb_max += delta; }; // calcul de la projection normale d'un point sur la frontière // ramène true si la projection est effective, si elle est hors de l'élément // ramène false // P : retour du point projeté (s'il existe) bool ElFrontiere::Projection_normale(const Coordonnee& A, Coordonnee& P) { int dim = A.Dimension(); // dimension des points bool retour = false; // init par défaut if (this->TypeFrontiere() == "FrontPointF") {// cas où la frontière est un point // par défaut la projection est le point frontière // il n'y a qu'un seul noeud d'ou le pointeur du noeud frontière Noeud * noe = TabNoeud()(1); if (tabNoeud(1)->ExisteCoord2()) {P = noe->Coord2();} else if (tabNoeud(1)->ExisteCoord1()) {P = noe->Coord1();} else {P = noe->Coord2();}; retour = true; } else {// cas d'un plan ou d'une droite Plan pl(dim); Droite dr(dim); int indic; // def des variables de tangence // constitution d'un plan tangent ou de la droite tangente au point de reference this->TangentRef(dr,pl,indic); // cas général : algorithme de recherche de projection // - initialisation Coordonnee M(dim); // le point cherché d'intersection avec la surface Coordonnee M1; // le point courant de la surface int compteur = 0; double nm = 0.; // grandeurs de contrôle double max_compteur = ParaGlob::param->ParaAlgoControleActifs().Nb_boucle_newton_position_sur_frontiere(); // distance maxi entre le point d'inter du plan et le point correspondant de la surface double dismini = ParaGlob::param->ParaAlgoControleActifs().Precision_pt_sur_front(); //--------------------------------------- // recherche du point d'intersection M //--------------------------------------- do {// calcul de l'intersection if (indic == 1) // cas d'une droite { // calcul du projeté d'un point sur la doite M = dr.Projete(A); } else // cas d'un plan { // calcul du projeté d'un point sur le plan M = pl.Projete(A); }; // calcul du point M1 de la surface correspondant a M // et en même temps calcul de dr ou pl au point M1 this->Tangent(M,M1,dr,pl,indic); nm = (M1 - M).Norme(); // test d'arret si l'on est en dehors de la boite d'encombrement // cout << "\n"; M1.Affiche();elfront->Encom_mini_FR().Affiche();elfront->Encom_maxi_FR().Affiche();cout << endl; // if (!(In_boite_emcombrement(M1))) // break; compteur++; } while ((compteur <= max_compteur) && (nm >= dismini)) ; // ici on recherche avec une précision donnée double extra_in_surface = ParaGlob::param->ParaAlgoControleActifs().PointInternePrecThetaiInterne(); // si le point appartient à l'élément c'est ok sinon non if (InSurf(extra_in_surface)) {// retour du point d'intersection P = M1;retour = true; } else {retour = false;}; }; // retour return retour; }; //----- lecture écriture base info ----- // pour l'instant on ne sauvegarde rien car ce n'est pas une bonne idée, et cela ne servira a rien // lecture base info void ElFrontiere::Lecture_base_info_ElFrontiere(istream& ent) { /*// lecture du type et vérification string nom_type; ent >> nom_type; if (nom_type != "ElFrontiere") Sortie(1); // le tableau de noeud associé à l'élément frontière int taille ; ent >> nom_type >> taille; tabNoeud.Change_taille(taille); int num_noeud,num_mail; for (int i=1;i<=taille;i++) { // l' indicateur pour le numéro de maillage et le numéro de noeud ent >> num_mail >> num_noeud; tabNoeud(i)->Change_num_Mail(num_mail); tabNoeud(i)->Change_num_noeud(num_noeud); } // les ddléléments associés ent >> ddlElem; // le tableau d'éléments frontières ent >> nom_type >> taille; tabfront.Change_taille(taille); for (int i=1;i<=taille;i++) tabfront(i)->Lecture_base_info_ElFrontiere(ent); // la boite d'encombrement ent >> nom_type >> encomb_min >> encomb_max ; */ }; // écriture base info void ElFrontiere::Ecriture_base_info_ElFrontiere(ostream& sort ) { /*// un identificateur de type sort << "ElFrontiere \n"; // le tableau de noeud associé à l'élément frontière int taille = tabNoeud.Taille(); sort << "taille_noeud= " << taille << " \n"; for (int i=1;i<=taille;i++) { // un indicateur pour le numéro de maillage et le numéro de noeud sort << tabNoeud(i)->Num_Mail() << " " << tabNoeud(i)->Num_noeud() << " "; } // les ddléléments associés sort << ddlElem; // le tableau d'éléments frontières taille = tabfront.Taille(); sort << "taille_tabfront= " << taille << " \n"; for (int i=1;i<=taille;i++) tabfront(i)->Ecriture_base_info_ElFrontiere(sort); // la boite d'encombrement sort << "encombrement " << encomb_min << " " << encomb_max << " \n"; */ }; //-------------------- méthode interne -------- // test si le point passé en argument appartient à la boite d'encombrement de la frontière // tous les points sont supposées avoir la même dimension bool ElFrontiere::In_boite_emcombrement(const Coordonnee& M) const { // on va essayer de faire efficace, pour cela on test le minimum #ifdef MISE_AU_POINT if ((M.Dimension() != ParaGlob::Dimension())|| (M.Dimension() != encomb_min.Dimension())) { cout << "\n *** pb de dimensions non coherente !! " << "\n ElFrontiere::In_boite_emcombrement(..."; Sortie(1); }; #endif switch (ParaGlob::Dimension()) { case 3: if ((M(3) encomb_max(3))) return false; case 2: if ((M(2) encomb_max(2))) return false; case 1: if ((M(1) encomb_max(1))) return false; }; // si on arrive ici c'est que le point est interne return true; };