// FICHIER : Maillage.cp // CLASSE : Maillage // 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 "Maillage.h" # include using namespace std; //introduces namespace std #include #include "Sortie.h" #include #include #include "List_io.h" #include "CharUtil.h" //#ifndef SYSTEM_MAC_OS_X_unix // #include "Frontier.h" //#endif #include "ConstMath.h" #include "ReferenceNE.h" #include "ReferenceAF.h" #include "Util.h" #include "MvtSolide.h" #include "Basiques.h" #include "TriaSfe1.h" #include "TriaSfe1_cm5pti.h" #include "TriaSfe2.h" #include "TriaSfe3.h" #include "TriaSfe3_cm3pti.h" #include "TriaSfe3_cm4pti.h" #include "TriaSfe3_cm5pti.h" #include "TriaSfe3_cm6pti.h" #include "TriaSfe3_cm7pti.h" #include "TriaSfe3_cm12pti.h" #include "TriaSfe3_cm13pti.h" #include "TriaSfe3_3D.h" #include "TriaSfe3C.h" #include "TriaQSfe1.h" #include "TriaQSfe3.h" #include //------------------- on s'occupe de 3 classes de travail ---------------------------------- bool Maillage::NBelemEtFace::operator < (const Maillage::NBelemEtFace& c) const {if (nbElem < c.nbElem) {return true;} else if (nbElem > c.nbElem) {return false;} else // ici cas ou nbElem == c.nbElem { return (nbFace < c.nbFace);}; }; bool Maillage::NBelemEtFace::operator > (const Maillage::NBelemEtFace& c) const {if (nbElem > c.nbElem) {return true;} else if (nbElem < c.nbElem) {return false;} else // ici cas ou nbElem == c.nbElem { return (nbFace > c.nbFace);}; }; bool Maillage::NBelemEtArete::operator < (const Maillage::NBelemEtArete& c) const {if (nbElem < c.nbElem) {return true;} else if (nbElem > c.nbElem) {return false;} else // ici cas ou nbElem == c.nbElem { return (nbArete < c.nbArete);}; }; bool Maillage::NBelemEtArete::operator > (const Maillage::NBelemEtArete& c) const {if (nbElem > c.nbElem) {return true;} else if (nbElem < c.nbElem) {return false;} else // ici cas ou nbElem == c.nbElem { return (nbArete > c.nbArete);}; }; bool Maillage::NBelemEtptInteg::operator < (const Maillage::NBelemEtptInteg& c) const {if (nbElem < c.nbElem) {return true;} else if (nbElem > c.nbElem) {return false;} else // ici cas ou nbElem == c.nbElem { return (nbPtInteg < c.nbPtInteg);}; }; bool Maillage::NBelemEtptInteg::operator > (const Maillage::NBelemEtptInteg& c) const {if (nbElem > c.nbElem) {return true;} else if (nbElem < c.nbElem) {return false;} else // ici cas ou nbElem == c.nbElem { return (nbPtInteg > c.nbPtInteg);}; }; bool Maillage::NBelemFAEtptInteg::operator < (const Maillage::NBelemFAEtptInteg& c) const {if (nbElem < c.nbElem) {return true;} else if (nbElem > c.nbElem) {return false;} else // ici cas ou nbElem == c.nbElem { if (nbFA < c.nbFA ) {return true;} else if (nbFA > c.nbFA ) {return false;} else // cas où il ne reste que le pti {return (nbPtInteg < c.nbPtInteg);}; }; }; bool Maillage::NBelemFAEtptInteg::operator > (const Maillage::NBelemFAEtptInteg& c) const {if (nbElem > c.nbElem) {return true;} else if (nbElem < c.nbElem) {return false;} else // ici cas ou nbElem == c.nbElem {if (nbFA > c.nbFA) {return true;} else if (nbFA < c.nbFA) {return false;} else // cas où il ne reste que le pti { return (nbPtInteg > c.nbPtInteg);}; }; }; //----------------------- fin des classes de travail ------------------------------------------ // modification de l'orientation d'éléments void Maillage::Modif_orientation_element(int cas_orientation,LesReferences* lesRef) { switch (cas_orientation) {case -1: case 1: // =-1: on sort une information s'il y a un pb d'orientation, mais on ne fait rien // =1: on modifie automatiquement l'orientation des éléments lorsque le jacobien // est purement négatif { // on va boucler sur les éléments int nbelem = tab_element.Taille(); for (int ie=1;ie<=nbelem;ie++) tab_element(ie)->Modif_orient_elem(cas_orientation); break; } case 2: // cas d'une orientation semi-automatique des faces d'un maillage de coques ou de membranes {cout << "\n processing of the mesh : " << nomDuMaillage << " nB= " << idmail; cout << "\n this re-orientation of elements can be applied only for shell and membrane elements" << " the other elements are ignored. " ; // initialisation des différents tableaux utilisés par Orientation_elements_mitoyens_recursif Init_Orientation_elements_mitoyens_recursif(); // on commence par faire l'acquisition d'un élément particulier dont la direction servira de référence bool fin_boucle_infinie = false; bool calcul_frontiere = false; // pour le calcul une fois des frontières int num_elem=0; // init du numéro de l'élément bool fr = ParaGlob::Francais(); // pour simplifier while (!fin_boucle_infinie) {num_elem=0; // re-init du numéro de l'élément, dans le cas où on bouclerait bool inverse = false; // init de l'indicateur permettant de savoir si l'on souhaite essayer // d'inverser le premier élément, donc tous les éléments de la nouvelle ref if (fr) {cout << "\n --- choix d'un element dont l'orientation servira de reference ------- "; cout << "\n le reperage peut se faire de differentes manieres :" << "\n par un numero d'element dans le maillage -> (choix : 1)" << "\n (rep -1 si l'on veut tenter une orientation inverse pour les elements) " << "\n par des coordonnees d'un point (meme approximatives), " << "\n l'element choisit sera celui le plus proche de ce point -> (choix : 2)" << "\n (rep -2 si l'on veut tenter une orientation inverse pour les elements) " << "\n fin -> (choix : f ou 0)"; } else {cout << "\n --- choise of a reference element for the orientation ------- "; cout << "\n could be done by :" << "\n the number of the element in the mesh -> (choice : 1)" << "\n (choice -1 if an inverse orientation is tried for the elements) " << "\n the coordinates of a point (even approximately) " << "\n the choice will be the nearest element -> (choice : 2)" << "\n (choice -2 if an inverse orientation is tried for the elements) " << "\n end -> (choice : f or 0)"; }; string rep=""; if (fr) {cout << "\n reponse ? ";}else {cout << "\n answer ? ";}; rep= lect_return_defaut(false,"f"); bool a_traiter=false; // pour gérer l'action d'orienter ou non if ((rep == "1") || (rep == "-1")) {if (rep =="-1") inverse = true; // on enregistre le souhait d'une orientation inverse if (fr) { cout << "\n numero de l'element ? "; } else { cout << "\n number of the element ? ";}; rep=lect_chaine(); num_elem = ChangeEntier(rep); } else if ((rep == "2") || (rep =="-2")) {int dim = ParaGlob::Dimension(); if (rep =="-2") inverse = true; // on enregistre le souhait d'une orientation inverse Coordonnee xi(dim); if (fr) {cout << "\n coordonnee ("<> xi(j); std::cin.ignore( std::numeric_limits::max(), '\n' );// purge de cin if (fr) {cout << "\n les coordonnees sont a: t = 0 ou t ou tdt (repondre 0 ou t ou tdt) ? ";} else {cout << "\n the coordinates are at : t = 0 or t or tdt (answer 0 or t or tdt) ? ";} rep = lect_chaine(); bool rep_exploitable = true; Enum_dure enu_temps; if (rep == "0") enu_temps = TEMPS_0; else if (rep == "t") enu_temps = TEMPS_t; else if (rep == "tdt") enu_temps = TEMPS_tdt; else { if (fr) {cout << "\n erreur de syntaxe, on attendait: 0, t ou tdt ";} else {cout << "\n error, the input must be : 0, t or tdt ";} rep_exploitable = false; }; if (rep_exploitable) {const Element * elem_ret = Centre_de_Gravite_Element_le_plus_proche(enu_temps,xi); if (elem_ret == NULL) // cas où l'on ne trouve pas d'élément { if (fr) {cout << "\n pas d'element trouve , pour le temps " << rep << " recommencez ! ";} else {cout << "\n no element founded, for the time " << rep << " do it again ! ";}; } else { if (fr) {cout << "\n le numero de l'element contenant le point: "<< elem_ret->Num_elt_const();} else {cout << "\n the number of the element that contains the point: "<< elem_ret->Num_elt_const();}; num_elem = elem_ret->Num_elt_const(); }; }; } else if ((Minuscules(rep) == "fin") || (Minuscules(rep) == "f") || (Minuscules(rep) == "0")) {fin_boucle_infinie = true; a_traiter=false;} else {if (fr) {cout << "\n choix non valide, recommencez !";} else {cout << "\n the choice is not valide, starts again ! ";}; }; // on vérifie que l'élément est correcte if (!fin_boucle_infinie) // on ne vérifie pas si c'est la fin demandée {if ((num_elem < 1) || (num_elem > tab_element.Taille())) {if (fr) {cout << "\n choix non valide, le numero "<Id_geometrie()) != SURFACE) { if (fr) {cout << "\n choix non valide, l'element "<Id_geometrie()); } else { cout << "\n the choice is not valide, the element "<Id_geometrie()); }; } else if (TestEnum_geom_axisymetrique(tab_element(num_elem)->Id_geometrie())) { if (fr) {cout << "\n choix non valide, l'element "< nums_non_oriente = Orientation_elements_mitoyens_recursif(recursif,nom_ref,num_elem,*lesRef ,angle_maxi,inverse); // dans le cas où la liste de retour n'est pas vide cela signifie qu'il y a des groupes d'éléments mitoyens qui n'ont pas // la même orientation, on demande si l'on veut les orienter if (nums_non_oriente.size() != 0) { if (fr) {cout << "\n des groupes d'elements mitoyens ont ete detectes comme ayant des orientations differentes " << " voulez-vous les orienter (arbitrairement) par groupes mitoyens " << " avec des noms de ref genere automatiquement ? (rep o ou n ) ";} else {cout << "\n we have found set of elements, that have common boundaries, but have no similar orientation," << " do you want to change these orientation (arbirary) for each set " << " with automatic generation of ref name ? (answer y or n )";}; string answer="";answer=lect_o_n(false); if ((fr && (Minuscules(answer) == "o")) || (!fr && (Minuscules(answer) == "y"))) { nom_ref="_";bool recursif = true; while (nums_non_oriente.size()) { nums_non_oriente = Orientation_elements_mitoyens_recursif (recursif,nom_ref,*(nums_non_oriente.begin()),*lesRef,angle_maxi,inverse);} fin_boucle_infinie = true; }; }; }; }; // fin du cas où on n'a pas demandé de finir la boucle infinie }; // -- fin de la boucle infinie sur le choix noeud/élément ou arrêt break; } case 3: // cas d'une orientation en fonction d'un rayon {cout << "\n processing of the mesh : " << nomDuMaillage << " nB= " << idmail; cout << "\n this re-orientation of elements can be applied only for shell and membrane elements" << " the other elements are ignored. " ; // def des tableaux de travail initialisés à rien par défaut int dim_espace(ParaGlob::Dimension()); if (dim_espace != 3) // pour l'instant ne fonctionne qu'en dimension 3 { cout << "\n erreur *** pour l'instant, cette fonction d'orientation ne fonctionne qu'en dimension 3 " << endl; Sortie(2); return; // normalement ne sert à rien }; Tableau indic_G(3,false); Coordonnee3 A; Tableau item_renseigne(3,false); // utilisé pour savoir si tous les items ont été renseignés string zone_a_traiter("_"); bool inverse=false; // on définit les paramètres de l'orientation: on va utiliser un menu string rep("_");bool traitement_pas_pret = true; while (traitement_pas_pret) { while ((Minuscules(rep) != "f")&&(Minuscules(rep) != "0")) { try { cout << "\n (0 ou f) (fin normal avant traitement) " << "\n (1) definition du point de reference " << "\n (2) inversion eventuelle de sens " << "\n (3) definition de la zone a traiter " << "\n (4) arret du processus (sans traitement) " << "\n (5) affichage de la liste des ref existantes (pour info) " << "\n (6) affichage min max sur x y z pour info " << "\n "; rep = lect_return_defaut(false,"f"); // sortie directe si tout est ok, sinon if ((Minuscules(rep) == "f") || (Minuscules(rep) == "0")) {if (item_renseigne(1) && item_renseigne(3)) break; // ok on peut s'arrêter else { cout << "\n la mise en donnee n'est pas complete ! "<= 0)&&(num<7)) { choix_valide=true; } else { cout << "\n Erreur on attendait un entier entre 0 et 6 !!, " << "\n redonnez une bonne valeur" << "\n ou taper f ou 0 pour arreter le programme et valider les choix "; choix_valide=false; } switch (num) { case 0: // sortie { break;} // normalement cela a déjà été filtré avant case 1: // définition du point de référence { cout << "\n\n definition du point de reference \n"; { string nom; cout << "\n coord x du point ou XG pour utiliser le x du G de la facette ? "; nom= lect_chaine();cout << " valeur lue "<< nom<Existe(nom,idmail)) {cout << "\n *** cette reference n'existe pas !! "<Affiche(1); break; } case 6: // affichage min max x y z pour info { cout << "\n affichage min max x y z pour info \n"; // on récupère la boite d'encombrement Tableau tab_vec = Taille_boiteMail(); cout << "\n min_x= "<tablcar,"fusion_elements_superposes_")!=NULL) { if (ParaGlob::NiveauImpression() > 2) cout << "\n beginning of the process to collapse elements which are " << " superposed "<< endl ; entreePrinc->NouvelleDonnee(); // on passe le mot clé // appel de l'algo de collapse Collapse_element_superpose(lesRef); if (ParaGlob::NiveauImpression() > 2) cout << "\n end of the process to collapse elements which are " << " superposed "<< endl ; }; }; // Collapse des éléments supperposés, c-a-d identiques, dans le cas où il en existe void Maillage::Collapse_element_superpose(LesReferences* lesRef) { // création pour chaque noeud de la liste des éléments qui contiennent le noeud Calcul_indice(); // on crée un tableau d'indicateur des éléments à supprimer int nbelem = tab_element.Taille(); Tableau elem_supprime(nbelem,0); // on balaie les éléments, et pour chaque élément, on recherche les éléments supperposés éventuels for (int ine = 1;ine <= nbelem;ine++) // on ne continue que si c'est un élément qui n'a pas déjà été marqué " à supprimé " if (elem_supprime(ine) == 0) {Element * elem = tab_element(ine); // on récupère uniquement le premier noeud Noeud * noe = tab_element(ine)->Tab_noeud()(1); // indice(i) contient la liste des éléments qui contiennent le noeud i List_io < Element*> & li_elem = indice(noe->Num_noeud()); // pour simplifier List_io < Element*>::iterator il,ilfin = li_elem.end(); for (il=li_elem.begin();il != ilfin;il++) // on intervient que si le numéro de l'élément est supérieur à celui de l'élément de base // et que ce n'est pas un élément déjà marqué à supprimé if (((*il)->Num_elt() > ine )&&(elem_supprime((*il)->Num_elt()) == 0)) // on test si les deux éléments ont la même signature, même géométrie et appartiennent au même maillage if (elem->Meme_signature_et_geometrie(*(*il))) // si égalité, on indique que l'élément il est à supprimer, elem_supprime((*il)->Num_elt())=ine; // et on sauvegarde l'élément de substitution }; // on sauvegarde les anciens numéros de noeuds, servira pour contruire nv_num // normalement c'est directement iN mais on ne sait jamais Tableau ancien_num_ele(nbelem); for (int ie=1;ie<=nbelem;ie++) ancien_num_ele(ie) = tab_element(ie)->Num_elt(); // -- maintenant on va vraiment supprimer les éléments en double et mettre à jour les références // 1- tout d'abord on s'intéresse aux éléments, dont on va mettre à jour les références // 2- on supprime les éléments qui ne servent plus int nb_elem_restant = nbelem; // init du nombre d'élément restant après fusion Tableau non_referencer(nbelem,false); // un tableau intermédiaire qui servira pour les ref for (int ine=1;ine<=nbelem;ine++) {if (elem_supprime(ine)) // s'il est différent de 0, on doit supprimer { int num_nelem = elem_supprime(ine); delete tab_element(ine); // on supprime l'élément // -- debug // cout << "\n Maillage::Collapse_element_superpose debug !! à remettre en service la ligne précédente "< 2) cout << "\n collapse element " << ine << " --> " << num_nelem ; ////-- debug //{cout << "\n debug ** Maillage::Collapse_element_superpose() "; // Element * elem1 = tab_element(ine-1); // recup de l'element // cout << " element " << elem1->Num_elt() << " "; // elem1->Affiche(); //// Element * elem2 = tab_element(ine); // recup de l'element //// cout << " element " << elem2->Num_elt() << " "; //// elem2->Affiche(); // Element * elem3 = tab_element(ine+1); // recup de l'element // cout << " element " << elem3->Num_elt() << " "; // elem3->Affiche(); // cout << "\n"< tab_final(nb_elem_restant); int nbelem_restant=0; // le nombre d'éléments restant cout << "\n "; for (int ijn = 1; ijn <= nbelem; ijn++) { if(tab_element(ijn)!=NULL) {nbelem_restant++; tab_final(nbelem_restant) = tab_element(ijn); }; }; // // on redimentionne le tableau final (ses premiers restent !!) // tab_final.Change_taille(nbn_restant); if (ParaGlob::NiveauImpression() >= 0) cout << "\n collapse of " << (nbelem-nbelem_restant) <<" element(s) " << endl; // on met à jour les références lesRef->SupprimeReference("E_tout",idmail); // on supprime la référence E_tout Tableau nv_num(nbelem,0); // nouveau num/ au ancien for (int ia=1;ia <= nbelem_restant; ia++) nv_num(tab_final(ia)->Num_elt()) = ia; // *** modif 15 avril 2016: il faut tous les numéros de remplacement et pas seulement ceux // qui restent car dans la mise à jour on commence par changer les numéros et "ensuite" // on supprime les doublons --> donc on complète // nv_num(num_ancien) = le nouveau numéro d'élément donc celui qui est en coincidence et qui reste // a_fusionner(ancien_num_ele(ia)) -> le nouveau numéro dans l'ancien tableau // qui aura comme nouveau numéro:nv_num(a_fusionner(ancien_num_ele(ia))) for (int ia=1;ia <= nbelem; ia++) if (nv_num(ia) == 0) // cela veut dire que c'est un numéro qui disparait nv_num(ia) = nv_num(elem_supprime(ancien_num_ele(ia))); // -- debug // cout << "\n Maillage::Collapse_element_superpose debug "<Affiche(); cout << endl; // -- fin debug lesRef->Mise_a_jour_ref_element(nv_num,idmail,non_referencer); ////-- debug //{cout << "\n debug ** Maillage::Collapse_element_superpose() "; // Element * elem1 = tab_final(152); // recup de l'element // cout << " element " << elem1->Num_elt() << " "; // elem1->Affiche(); //// Element * elem2 = tab_element(ine); // recup de l'element //// cout << " element " << elem2->Num_elt() << " "; //// elem2->Affiche(); // Element * elem3 = tab_final(153); // recup de l'element // cout << " element " << elem3->Num_elt() << " "; // elem3->Affiche(); // cout << "\n"<Change_num_elt(ia); // on recrée la référence E_tout string Etout("E_tout"); { // on construit le tableau des numéros de noeuds Tableau tabE(nbelem_restant); for (int i=1;i<=nbelem_restant;i++) tabE(i)=i; ReferenceNE* rtout = new ReferenceNE(tabE,idmail,2,Etout);// construction de la référence lesRef->Ajout_reference(rtout); // ajout de la ref, qui est maintenant géré par lesRef }; ////-- debug //{cout << "\n debug ** Maillage::Collapse_element_superpose() "; // Element * elem1 = tab_element(152); // recup de l'element // cout << " element " << elem1->Num_elt() << " "; // elem1->Affiche(); //// Element * elem2 = tab_element(ine); // recup de l'element //// cout << " element " << elem2->Num_elt() << " "; //// elem2->Affiche(); // Element * elem3 = tab_element(153); // recup de l'element // cout << " element " << elem3->Num_elt() << " "; // elem3->Affiche(); // cout << "\n"<Num_elt() << " "; // elem2->Affiche(); // }; // cout << "\n"<tablcar,"fusion_noeuds_proches_")!=NULL) { if (ParaGlob::NiveauImpression() > 2) cout << "\n beginning of the process to collapse nodes which are " << " nearly at the same position "<< endl ; // on lit la distance mini double rayon = 0;string nom; *(entreePrinc->entree) >> nom >> rayon; if (nom != "fusion_noeuds_proches_") { cout << "\n error : we expected the keyword: fusion_noeuds_proches_ but we read \""<= 6) cout << "\n Maillage::LectureEtCollapse_noeuds_proches(..."; Sortie(1); }; if (ParaGlob::NiveauImpression() >= 5) cout << "\n minimum distance = " << rayon; entreePrinc->NouvelleDonnee(); // on passe le mot clé // appel de l'algo de collapse Collapse_noeuds_proches(rayon,lesRef); if (ParaGlob::NiveauImpression() > 2) cout << "\n end of the process to collapse nodes which are " << " nearly at the same position "<< endl ; }; }; // collapse de noeuds très proche: appartenant à des éléments différents // rayon : donne la distance maxi entre les noeuds qui doivent être collapsé void Maillage::Collapse_noeuds_proches(double rayon ,LesReferences* lesRef) {// au niveau de l'algo on va commencer par faire un classement des noeuds pour accélérer les recherches futures // pour cela on découpe le volume de travail en boites contenant un petit nombre de noeuds // et on s'aide de grandeurs de référence int NBN = tab_noeud.Taille(); int dim = ParaGlob::Dimension(); if (ParaGlob::NiveauImpression() >= 0) cout << "\n " << NBN << " node(s) " << tab_element.Taille() << " element(s) " ; // racine3NBN donne une idée du nombre de noeuds dans chaque direction de la dimension int racine3NBN = (int) (exp(1./dim * log(NBN) )); // floor: extraction de la partie entière // on veut environ 3*3*3=27 noeuds par boites en 3D par exemple // en fait les tests on montré que c'était mieux d'avoir plus de boite, donc on ne divise pas par 3 int nb_decoupe = racine3NBN ; // 3;// on pourrait donc avoir nb_decoupe**dim cubes // mais il faut également que la taille du cube de base soit plus grande que le rayon // maintenant on détermine les dimensions max et min qui contiennent tous les noeuds Tableau ta_d = Taille_boiteMail(); Tableau nbboite(dim); int nb_totale=1; // on va calculer les paramètres de stockage et de calculs Coordonnee pas_en_espace(dim); // les pas d'espace dans les différentes directions for (int i=1;i<=dim;i++) {double distance = ta_d(2)(i)-ta_d(1)(i); // en générale le rayon est très petit, donc on va considérer des arêtes de boites > 5 * rayons int nb_mini = (int)(distance / (rayon*5.)); // si nb_mini est négatif cela veut dire que l'on a dépasser la précision et que // le nombre est trop grand on le met également à 1 if ((nb_mini == 0) || (nb_mini < 0) ) nb_mini=1; // au minimum on doit avoir 1 nbboite(i) = MiN(nb_mini,nb_decoupe); // on stocke le nombre de boite dans la direction i pas_en_espace(i) = distance / nbboite(i) + ConstMath::petit; // on stocke le pas d'espace dans la direction i // on a ajouté un chouil pour être sur que le point le plus grand soit calculé dans les boites existantes nb_totale *= nbboite(i); // on stocke le nombre maxi de boite }; if (ParaGlob::NiveauImpression() >= 5) {cout <<"\n nbboite(i)= ";for (int i=1;i<=dim;i++) cout << nbboite(i); cout < > tab_Boite(nb_totale); // dimensionnement des listes de noeuds principale // pour chaque noeud de la liste , il faudra considérer tous les noeuds des boites qui l'entourent // on comparera ce noeud avec ceux des 3 ou 9 ou 27 boites (suivant la dimension) qui entourent la boite du noeud // étant donné que la taille de la boite est au moins plus grande que le rayon de recherche, normalement c'est ok Tableau > tab_BS(NBN); // les listes des numéros de boites qui seront associées à la recherche // défini le décalage que l'on doit avoir pour le calcul des indices Coordonnee M_ref(dim); switch (dim) {case 3: M_ref(3) = ta_d(1)(3); case 2: M_ref(2) = ta_d(1)(2); case 1: M_ref(1) = ta_d(1)(1); }; // maintenant on va remplir les boites = listes de noeuds for (int iN=1;iN<=NBN;iN++) { Noeud& no = *(tab_noeud(iN)); // pour simplifier const Coordonnee& M0 =no.Coord0(); int indica=0; // indice de la boite // soit i j k les indices suivants x y et z, et N1 N2 N3 les maxis de stockage suivants x y z // le noeud est stocké dans (k-1)*N1*N2 + (j-1)*N1 + i // - calcul de l'indice de stockage int nb1,nb2,nb3; // les indices suivant x y z switch (dim) {case 3: {nb3= ((int) ((M0(3)-M_ref(3))/pas_en_espace(3))); // le numéro -1, suivant 3 indica += nb3 * nbboite(3) * nbboite(2); } case 2: {nb2= ((int) ((M0(2)-M_ref(2))/pas_en_espace(2))); // le numéro -1, suivant 2 indica += nb2 * nbboite(2); } case 1: {nb1= ((int) ((M0(1)-M_ref(1))/pas_en_espace(1))); // le numéro -1, suivant 1 indica += nb1 + 1 ; } }; //debug // if ((nb1 > nbboite(1)) || (nb2 > nbboite(2)) || (nb3 > nbboite(3))) // cout << "\n nb1= "<Coord0() << "\n coor il: " << tab_noeud(*il)->Coord0() << endl; }; }; }; }; }; // création pour chaque noeud de la liste des éléments qui contiennent le noeud Calcul_indice(); // on passe en revue les noeuds et on regarde si la fusion est possible // elle est impossible si deux noeuds a fusionner appartiennent à un même élément // dans ce cas on désactive l'indicateur de fusion for (int ine=1;ine<=NBN;ine++) {if (a_fusionner(ine)) // s'il est différent de 0, indique que ine peut-être fusionné {// on regarde si les deux noeuds : ine et a_fusionner(ine) n'appartiennent pas à un même élément List_io < Element*>& indic = indice(ine); // pour simplifier List_io < Element*>::iterator ie,iefin=indic.end(); bool fusion_non_possible = false; // init par défaut int num_maitre = a_fusionner(ine); for (ie=indic.begin();ie!=iefin;ie++) { Element& ele = *(*ie); // récup d'un élément qui contient a priori le numéro ine Tableau& tn = ele.Tab_noeud(); // récup des noeuds de l'élément // maintenant on regarde si l'élément contient également le a_fusionner(ine) int taille_tn=tn.Taille(); for (int j=1;j<=taille_tn;j++) if (tn(j)->Num_noeud() == num_maitre) {fusion_non_possible=true; break;// on sort de la première boucle sur les noeuds de l'élément }; if (fusion_non_possible) break; // on sort de la seconde boucle sur les éléments contenant le noeud }; // if (fusion_non_possible) // cas où la fusion n'est pas possible {a_fusionner(ine)=0;}; }; }; // on sauvegarde les anciens numéros de noeuds, servira pour contruire nv_num // normalement c'est directement iN mais on ne sait jamais Tableau ancien_num_Noe(NBN); for (int iN=1;iN<=NBN;iN++) ancien_num_Noe(iN) = tab_noeud(iN)->Num_noeud(); // -- maintenant on va vraiment fusionner et mettre à jour les références // 1- tout d'abord on s'intéresse aux éléments, dont on va mettre à jour les références // 2- on supprime les noeuds qui ne servent plus int nb_noeud_restant = NBN; // init du nombre de noeud restant après fusion Tableau non_referencer(NBN,false); // un tableau intermédiaire qui servira pour les ref for (int ine=1;ine<=NBN;ine++) {if (a_fusionner(ine)) // s'il est différent de 0, on doit fusionner { int num_ne = a_fusionner(ine); //cout << "\n ine= "<PlusTabDdl(*tab_noeud(ine)); // maintenant il n'y a plus d'élément qui utilise le noeud ine donc on le supprime delete tab_noeud(ine); // on supprime le noeud tab_noeud(ine)=NULL; // on indique qu'il n'y a plus de noeud // non_referencer(ine)=true; // servira pour les ref // *** modif 15 avril 2016: comme il s'agit de noeuds supperposés, cela veut dire qu'il reste un noeud de substitution // qui lui est valide donc on met false, ce qui veut dire que l'on ne veut pas supprimer le noeud dans la ref // car sinon la ref devient différente et incomplète non_referencer(ine)=false; // servira pour les ref nb_noeud_restant--; // on diminue le nombre de noeuds restants if (ParaGlob::NiveauImpression() > 2) cout << "\n collapse node " << ine << " --> "< tab_final(nb_noeud_restant); int nbn_restant=0; // le nombre de noeud restant cout << "\n "; for (int ijn = 1; ijn <= NBN; ijn++) { if(tab_noeud(ijn)!=NULL) {nbn_restant++; tab_final(nbn_restant) = tab_noeud(ijn); }; }; // // on redimentionne le tableau final (ses premiers restent !!) // tab_final.Change_taille(nbn_restant); if (ParaGlob::NiveauImpression() >= 0) cout << "\n collapse of " << (NBN-nbn_restant) <<" node(s) " << endl; // on met à jour les références lesRef->SupprimeReference("N_tout",idmail); // on supprime la référence N_tout Tableau nv_num(NBN,0); // nouveau num/ au ancien, initialisé à 0 for (int ia=1;ia <= nbn_restant; ia++) nv_num(tab_final(ia)->Num_noeud()) = ia; // *** modif 15 avril 2016: il faut tous les numéros de remplacement et pas seulement ceux // qui restent car dans la mise à jour on commence par changer les numéros et "ensuite" // on supprime les doublons --> donc on complète // nv_num(num_ancien) = le nouveau numéro de noeud donc celui qui est en coincidence et qui reste // a_fusionner(ancien_num_Noe(ia)) -> le nouveau numéro dans l'ancien tableau // qui aura comme nouveau numéro:nv_num(a_fusionner(ancien_num_Noe(ia))) for (int ia=1;ia <= NBN; ia++) if (nv_num(ia) == 0) // cela veut dire que c'est un numéro qui disparait nv_num(ia) = nv_num(a_fusionner(ancien_num_Noe(ia))); lesRef->Mise_a_jour_ref_noeud(nv_num,idmail,non_referencer); // on enregistre le nouveau tableau de noeuds tab_noeud = tab_final; // on met à jour les numéros de noeuds for (int ia=1;ia <= nbn_restant; ia++) tab_noeud(ia)->Change_num_noeud(ia); // on recrée la référence N_tout string ntout("N_tout"); { // on construit le tableau des numéros de noeuds Tableau tabN(nbn_restant); for (int i=1;i<=nbn_restant;i++) tabN(i)=i; ReferenceNE* rtout = new ReferenceNE(tabN,idmail,1,ntout);// construction de la référence lesRef->Ajout_reference(rtout); // ajout de la ref, qui est maintenant géré par lesRef }; }; // Lecture et suppression d'elements à 2 noeuds, de distances très proches void Maillage::LectureEtSup_Elem_noeudsConfondu(UtilLecture * entreePrinc, LesReferences* lesRef) { if (strstr(entreePrinc->tablcar,"suppression_elements_2noeuds_tres_proches_")!=NULL) { if (ParaGlob::NiveauImpression() > 2) cout << "\n beginning of the process to collapse element with two nodes which are " << " nearly at the same position "<< endl ; // on lit la distance mini double rayon = 0;string nom; *(entreePrinc->entree) >> nom >> rayon; if (nom != "suppression_elements_2noeuds_tres_proches_") { cout << "\n error : we expected the keyword: suppression_elements_2noeuds_tres_proches_ but we read \""<= 6) cout << "\n Maillage::LectureEtSup_Elem_noeudsConfondu(..."; Sortie(1); }; if (ParaGlob::NiveauImpression() >= 5) cout << "\n minimum distance = " << rayon; entreePrinc->NouvelleDonnee(); // on passe le mot clé // appel de l'algo de suppression Sup_Elem_noeudsConfondu(rayon,lesRef); if (ParaGlob::NiveauImpression() > 2) cout << "\n end of the process to collapse element with two nodes which are " << " nearly at the same position "<< endl ; }; }; // suppression d'elements à 2 noeuds, de distances très proches // rayon : donne la distance maxi entre les noeuds void Maillage::Sup_Elem_noeudsConfondu(double rayon, LesReferences* lesRef) { // on crée un tableau d'indicateur des éléments à supprimer int nbelem = tab_element.Taille(); Tableau elem_supprime(nbelem,0); // on calcul les frontières et les mitoyens de tous les éléments Calcul_tous_les_front_et_leurs_mitoyens(); // on balaie les éléments for (int ine = 1;ine <= nbelem;ine++) // on ne continue que si c'est un élément qui n'a pas déjà été marqué " à supprimé " if (elem_supprime(ine) == 0) {Element * elem = tab_element(ine); // on ne continue que si c'est un éléments à 2 noeuds if (elem->Nombre_noeud() == 2) { // on récupère les deux noeuds Noeud * noe1 = tab_element(ine)->Tab_noeud()(1); Noeud * noe2 = tab_element(ine)->Tab_noeud()(2); // on calcule la distance entre les deux noeuds if ((noe1->Coord0()-noe2->Coord0()).Norme() < rayon) elem_supprime(ine)=1; // on enregistre la suppression } else if (elem->Nombre_noeud() == 3) { // un élément à 3 noeuds peut également être supprimable, mais // pour l'instant il faut qu'il ne soit relié qu'à des éléments également à 3 ou 2 noeuds // .. on itère sur les cotés tout d'abord pour s'assurer que les mitoyens ont au plus 3 ou 2 // noeuds: car si on a un mitoyen à 4 noeuds par exemple, il faut le convertir en // un élément à 3 noeud, c'est possible mais pas directement dans la version actuelle bool continou = true; // init // pour chaque élément "i" , mitoyen_de_chaque_element(i)(j) contient l'élément Front // qui décrit la frontière "j" de l'élément et dedans, les éléments mitoyens de cette frontière // appartenant à d'autres éléments for (int i=0;i<3;i++) {Front& fron = mitoyen_de_chaque_element(ine)(i+1); const Tableau * t_mitoy = fron.TabMitoyen(); if (t_mitoy != NULL) {int taille = t_mitoy->Taille(); for (int j=1;j<= taille; j++) { if( (*t_mitoy)(j)->PtEI()->Nombre_noeud()>3) {continou = false; break;}; // pas bon on arrête }; } if (!continou) break; // arrêt car mitoyen avec plus de 3 noeuds }; // Maintenant on regarde la distance entre les noeuds de chaque frontière // pour l'instant ne fonctionne que pour des frontières à 2 noeuds if (continou) for (int i=0;i<3;i++) { Front& fron = mitoyen_de_chaque_element(ine)(i+1); // on ne continue que s'il s'agit d'un élément frontière à 2 noeuds const Tableau & tlocNoe = fron.Eleme()->TabNoeud(); if (tlocNoe.Taille() == 2) { // on récupère les deux noeuds de l'arête Noeud * noe1 = tlocNoe(1); Noeud * noe2 = tlocNoe(2); // on calcule la distance entre les deux noeuds if ((noe1->Coord0()-noe2->Coord0()).Norme() < rayon) {elem_supprime(ine)=1; // on enregistre la suppression // on doit également suppimer les éléments mitoyens const Tableau * t_mitoy = fron.TabMitoyen(); if (t_mitoy != NULL) { int tail = t_mitoy->Taille(); for (int a=1;a<=tail;a++) {Front* fr = (*t_mitoy)(a); elem_supprime(fr->PtEI()->Num_elt())=1; }; }; }; }; }; }; }; // -- maintenant on va vraiment supprimer les éléments et mettre à jour les références // 1- tout d'abord on s'intéresse aux éléments, dont on va mettre à jour les références // 2- on supprime les éléments qui ne servent plus int nb_elem_restant = nbelem; // init du nombre d'élément restant après suppression Tableau non_referencer(nbelem,false); // un tableau intermédiaire qui servira pour les ref for (int ine=1;ine<=nbelem;ine++) {if (elem_supprime(ine)) // s'il est différent de 0, on doit supprimer { //int num_nelem = elem_supprime(ine); // ne sert à rien ici, j'ai repris l'algo Collapse_noeuds_proches delete tab_element(ine); // on supprime l'élément tab_element(ine)=NULL; // on indique qu'il n'y a plus d'élément non_referencer(ine)=true; // servira pour les ref nb_elem_restant--; // on diminue le nombre d'éléments restants if (ParaGlob::NiveauImpression() > 2) cout << "\n delete element " << ine << " <-- " ; }; }; // maintenant on refait un nouveau tableau d'éléments avec les éléments inutiles supprimés Tableau tab_final(nb_elem_restant); int nbelem_restant=0; // le nombre d'éléments restant cout << "\n "; for (int ijn = 1; ijn <= nbelem; ijn++) { if(tab_element(ijn)!=NULL) {nbelem_restant++; tab_final(nbelem_restant) = tab_element(ijn); }; }; // // on redimentionne le tableau final (ses premières entitées restent !!) // tab_final.Change_taille(nbn_restant); if (ParaGlob::NiveauImpression() >= 0) cout << "\n delete of " << (nbelem-nbelem_restant) <<" element(s) " << endl; // on met à jour les références lesRef->SupprimeReference("E_tout",idmail); // on supprime la référence E_tout Tableau nv_num(nbelem,0); // nouveau num/ au ancien for (int ia=1;ia <= nbelem_restant; ia++) nv_num(tab_final(ia)->Num_elt()) = ia; lesRef->Mise_a_jour_ref_element(nv_num,idmail,non_referencer); // on enregistre le nouveau tableau d'éléments tab_element = tab_final; // on met à jour les numéros d'éléments for (int ia=1;ia <= nbelem_restant; ia++) tab_element(ia)->Change_num_elt(ia); // on recrée la référence E_tout string Etout("E_tout"); { // on construit le tableau des numéros de noeuds Tableau tabE(nbelem_restant); for (int i=1;i<=nbelem_restant;i++) tabE(i)=i; ReferenceNE* rtout = new ReferenceNE(tabE,idmail,2,Etout);// construction de la référence lesRef->Ajout_reference(rtout); // ajout de la ref, qui est maintenant géré par lesRef }; }; // création d'éléments SFE en fonction d'éléments classiques void Maillage::CreationMaillageSFE() { // une première étape est de vérifier que tous les éléments sont des triangles, sinon // arrêt et message d'incompétance if (!OKPourTransSfe()) { cout << "\n error*** the mesh " << idmail << " ( " << nomDuMaillage << " ) " << " does not contain only triangular elements, currently " << " the continuation of the processing is not possible ! --> stop "; return; }; // sinon c'est ok, on n'a que des éléments triangulaires // -- on demande le choix d'éléments bool bonne_lecture=false;Enum_interpol enuSfe=SFE1; while (!bonne_lecture) { cout << "\n give the type of SFE element you want: " << "\n SFE1 (resp 1) SFE1_3D (resp 11) " << "\n SFE2 (resp 2) SFE2_3D (resp 12) " << "\n SFE3 (resp 3) SFE3_3D (resp 13) " << "\n SFE3C (resp 4) SFE3C_3D (resp 14) " << "\n QSFE1 (resp 5) QSFE3 (resp 6) "; string rep; rep = lect_chaine(); if (rep == "1") {enuSfe = SFE1; bonne_lecture=true;} else if (rep == "2") {enuSfe = SFE2; bonne_lecture=true;} else if (rep == "3") {enuSfe = SFE3; bonne_lecture=true;} else if (rep == "4") {enuSfe = SFE3C; bonne_lecture=true;} else if (rep == "5") {enuSfe = QSFE1; bonne_lecture=true;} else if (rep == "6") {enuSfe = QSFE3; bonne_lecture=true;} else if (rep == "11") {enuSfe = SFE1_3D; bonne_lecture=true;} else if (rep == "12") {enuSfe = SFE2_3D; bonne_lecture=true;} else if (rep == "13") {enuSfe = SFE3_3D; bonne_lecture=true;} else if (rep == "14") {enuSfe = SFE3C_3D; bonne_lecture=true;} else { cout << "\n bad answer, we have read : " << rep << "! try again ";}; }; cout << "\n choice read : " << Nom_interpol(enuSfe); // On va créer des éléments en plus que ceux qui existent déjà, puis on supprimera ceux qui // existent, et on les remplacera par ceux que l'on vient de créer int tabelTaille = tab_element.Taille(); Tableau< Tableau > t_noe_ele(tabelTaille); // les noeuds des SFE // on commence par remplir t_noe_ele en considérant qu'il n'y a pas de mitoyen int nbe_C = 3; // init: nb de noeud centraux if ((enuSfe == QSFE3) || (enuSfe == QSFE1)) nbe_C = 6; // interpolation quadratique au centre for (int i4 = 1; i4 <= tabelTaille; i4++) { Tableau & t_noe = t_noe_ele(i4); // pour simplifier Element * elem = tab_element(i4); // recup de l'element t_noe.Change_taille(nbe_C+3); // nbe_C+3 noeuds pour les sfe triangles // les noeuds du centre for (int i=1;i<= nbe_C;i++) t_noe(i) = &(elem->Noeud_elt(i)); // les noeuds externes initialisés aux noeuds centraux des sommets t_noe(nbe_C+1) = &(elem->Noeud_elt(1)); t_noe(nbe_C+2) = &(elem->Noeud_elt(2)); t_noe(nbe_C+3) = &(elem->Noeud_elt(3)); // t_noe(1) = t_noe(4) = &(elem->Noeud_elt(1)); // t_noe(2) = t_noe(5) = &(elem->Noeud_elt(2)); // t_noe(3) = t_noe(6) = &(elem->Noeud_elt(3)); }; // I-- on définit les noeuds externe de chaque triangles // I.1- on crée des tableaux permettant d'accélérer l'algorithme final // pour chaque noeud on regarde quel élément contiend ce noeud int tab_noeud_taille= tab_noeud.Taille(); Tableau > indice(tab_noeud_taille); // indice contiend les éléments succeptibles d'avoir des arrêtes communes // on en profite également pour calculer les normales à chaque élément Tableau tab_Nor(tabelTaille); for (int i22 = 1; i22 <= tabelTaille; i22++) { Element * elem1 = tab_element(i22); // recup de l'element Tableau& tabn = elem1->Tab_noeud(); // récup de son tableau de noeud // int tabntaille = tabn.Taille(); int tabntaille = 3; // on n'utilise que les noeuds sommets !! : 1 mars 2021 for (int ij=1;ij<= tabntaille;ij++) indice((tabn(ij)->Num_noeud())).push_back(elem1); // on balaie ses noeuds // calcul de la normale Coordonnee A=tabn(1)->Coord0();Coordonnee B=tabn(2)->Coord0(); Coordonnee C=tabn(3)->Coord0(); Coordonnee AB(B-A); Coordonnee AC(C-A); tab_Nor(i22)=Util::ProdVec_coor(AB,AC);tab_Nor(i22).Normer(); }; // I.2 - un tableau intermédiaire t_i(i) , d'indiçage local // l'indice i = le numéro du premier noeud de l'arrête, et t_i(i) = le num du noeud externe Tableau t_i(3); t_i(1)=6; t_i(2)=4; t_i(3)=5; // init éléments SFE lineaires if ((enuSfe == QSFE3) || (enuSfe == QSFE1)) {t_i(1)=9; t_i(2)=7; t_i(3)=8; }// interpolation quadratique au centre // également un tableau pour noter les éléments dont la courbure est supérieure à un maxi // c'est à dire qui ont deux facettes avec un produit scalaire des normales suffisamment grand // on choisit arbitrairement 30° c-a-d cos(pi/6) = 0.86603 Tableau elem_courb(tabelTaille,0); // par défaut tous sont ok double limit_inf_prod_scal = 0.86603; // la limite inf -> servira pour les SFE3 // qui sera mesuré par // I.3- maintenant on va de nouveau boucler sur les éléments en regardant // pour chaque arrêtes si elle est effectivement commune for (int i1 = 1; i1 <= tabelTaille; ++i1) {Element * elem1 = tab_element(i1); // recup de l'element // Tableau& tabn = elem1->Tab_noeud(); // récup de son tableau de noeuds //int nb_noe_elem1 = tabn.Taille(); // on boucle sur les 3 arrêtes // normalement les 3 premiers noeuds sont les sommets du triangle for (int ina=1;ina<=3;ina++) { int noe1 = elem1->Noeud_elt(ina).Num_noeud();// num du premier noeud de l'arrête int noe2 = 0; // init pour le second noeud if (ina == 3) { noe2 = elem1->Noeud_elt(1).Num_noeud();} else { noe2 = elem1->Noeud_elt(ina+1).Num_noeud();} List_io & indic1 = indice(noe1); // les éléments à considérer pour noe1 List_io & indic2 = indice(noe2); // les éléments à considérer pour noe2 // on cherche un élément de l'intersection de indic1 et indic2 // qui doit également être différent de l'élément central List_io ::iterator il1,ilfin1=indic1.end(); List_io ::iterator il2,ilfin2=indic2.end(); list candidats; // une liste de travail for (il1=indic1.begin();il1 != ilfin1;il1++) for (il2=indic2.begin();il2 != ilfin2;il2++) if (((*il1) == (*il2)) && ((*il1) != elem1 )) { //ele = (*il1); break; candidats.push_back((*il1)); }; // maintenant parmi tous les candidats on va choisir celui qui conduit à l'élément le plus plat // NB: on considère que les éléments sont correctement orientés dans le même sens Element* ele = NULL; // init a priori if (candidats.size() != 0) { list ::iterator ilfin=candidats.end(); list ::iterator il=candidats.begin(); ele = (*il); il++; // on stocke a priori le premier élément double prodscal = tab_Nor(ele->Num_elt()) * tab_Nor(elem1->Num_elt()); // puis on parcours les éléments suivants et on garde celui dont le prodscal est le + grand for (;il != ilfin;il++) { double prod = tab_Nor((*il)->Num_elt()) * tab_Nor(elem1->Num_elt()); if (prod > prodscal) {prodscal = prod; ele = (*il);}; }; // si le produit scalaire est inférieur à limit_inf_prod_scal, on enregistre if (prodscal < limit_inf_prod_scal) elem_courb(elem1->Num_elt()) = 1; }; // si on a trouvé un candidat if (ele != NULL) {// on recherche le noeud qui n'est pas commun avec l'arrête for (int j=1;j<=3;j++) // on boucle sur les 3 noeuds sommets de l'élément {int num = ele->Noeud_elt(j).Num_noeud(); if ((num != noe1) && (num != noe2)) { // on met à jour le tableau des noeuds externes t_noe_ele(i1)(t_i(ina)) = &(ele->Noeud_elt(j)); break; }; }; }; // si aucun candidat n'est trouvé, t_noe_ele reste tel_quel }; }; // II-- on crée les éléments SFE // bool bonne_lecture=false;Enum_interpol enuSfe=SFE1; // while (!bonne_lecture) // { cout << "\n give the type of SFE element you want: " // << "\n SFE1 (resp 1) SFE1_3D (resp 11) " // << "\n SFE2 (resp 2) SFE2_3D (resp 12) " // << "\n SFE3 (resp 3) SFE3_3D (resp 13) " // << "\n SFE3C (resp 4) SFE3C_3D (resp 14) "; // string rep; rep = lect_chaine(); // if (rep == "1") {enuSfe = SFE1; bonne_lecture=true;} // else if (rep == "2") {enuSfe = SFE2; bonne_lecture=true;} // else if (rep == "3") {enuSfe = SFE3; bonne_lecture=true;} // else if (rep == "4") {enuSfe = SFE3C; bonne_lecture=true;} // // else if (rep == "11") {enuSfe = SFE1_3D; bonne_lecture=true;} // else if (rep == "12") {enuSfe = SFE2_3D; bonne_lecture=true;} // else if (rep == "13") {enuSfe = SFE3_3D; bonne_lecture=true;} // else if (rep == "14") {enuSfe = SFE3C_3D; bonne_lecture=true;} // // // else { cout << "\n bad answer, we have read : " << rep << "! try again ";}; // }; // cout << "\n choice read : " << Nom_interpol(enuSfe); Element * toto = NULL; Tableau tab_SFE(tabelTaille,toto); // création de pointeurs nuls // cas d'info annexe string str_precision(""); // stockage info annexe de choix, initié à rien par défaut bonne_lecture=false; int num_pti=0; // init if (enuSfe == SFE3 ) // on a des nombres de pti variables que pour les SFE3 while (!bonne_lecture) { cout << "\n give eventually le number of integration point : " << "\n 2, 3, 4, 5, 6, 7, 12, 13 : "; string rep; rep = lect_chaine(); if (rep == "2") {str_precision="";bonne_lecture=true;num_pti=2;} else if (rep == "3") {str_precision="_cm3pti";bonne_lecture=true;num_pti=3;} else if (rep == "4") {str_precision="_cm4pti";bonne_lecture=true;num_pti=4;} else if (rep == "5") {str_precision="_cm5pti";bonne_lecture=true;num_pti=5;} else if (rep == "6") {str_precision="_cm6pti";bonne_lecture=true;num_pti=6;} else if (rep == "7") {str_precision="_cm7pti";bonne_lecture=true;num_pti=7;} else if (rep == "12") {str_precision="_cm12pti";bonne_lecture=true;num_pti=12;} else if (rep == "13") {str_precision="_cm13pti";bonne_lecture=true;num_pti=13;} else { cout << "\n bad answer, we have read : " << rep << "! try again ";}; }; if (enuSfe == SFE1 ) // idem pour les SFE1 while (!bonne_lecture) { cout << "\n give eventually le number of integration point : " << "\n 2, 5 : "; string rep; rep = lect_chaine(); if (rep == "2") {str_precision="";bonne_lecture=true;num_pti=2;} else if (rep == "5") {str_precision="_cm5pti";bonne_lecture=true;num_pti=5;} else { cout << "\n bad answer, we have read : " << rep << "! try again ";}; }; for (int i5 = 1; i5 <= tabelTaille; ++i5) {ElemMeca * el = ((ElemMeca *) tab_element(i5)); // recup de l'element Tableau & t_noe = t_noe_ele(i5); // pour simplifier Tableau& tabn = el->Tab_noeud(); // récup de son tableau de noeuds // coordonnées du centre de gravité Coordonnee G = (tabn(1)->Coord0()+tabn(2)->Coord0()+tabn(3)->Coord0())/3.; double epaisseur = el->Epaisseurs(TEMPS_0,G); // épaisseur à G switch (enuSfe) { case SFE1: switch (num_pti) { case 2: tab_SFE(i5) = new TriaSfe1(epaisseur,idmail,el->Num_elt(),t_noe); break; case 5: tab_SFE(i5) = new TriaSfe1_cm5pti(epaisseur,idmail,el->Num_elt(),t_noe);break; default: cout << "*** pb, le cas "<Num_elt(),t_noe); break; case SFE3: // si la courbure de l'élément est trop grande on bascule vers un SFE1 if (elem_courb(i5)) { if (ParaGlob::NiveauImpression() > 0) cout << " attention l'element " << Nom_interpol(enuSfe)<< " "<Num_elt(),t_noe); break; case 5: tab_SFE(i5) = new TriaSfe1_cm5pti(epaisseur,idmail,el->Num_elt(),t_noe);break; default: if (ParaGlob::NiveauImpression() > 2) cout << " attention le cas "<Num_elt(),t_noe);break; }; } else // cas normal {switch (num_pti) { case 2: tab_SFE(i5) = new TriaSfe3(epaisseur,idmail,el->Num_elt(),t_noe);break; case 3: tab_SFE(i5) = new TriaSfe3_cm3pti(epaisseur,idmail,el->Num_elt(),t_noe);break; case 4: tab_SFE(i5) = new TriaSfe3_cm4pti(epaisseur,idmail,el->Num_elt(),t_noe);break; case 5: tab_SFE(i5) = new TriaSfe3_cm5pti(epaisseur,idmail,el->Num_elt(),t_noe);break; case 6: tab_SFE(i5) = new TriaSfe3_cm6pti(epaisseur,idmail,el->Num_elt(),t_noe);break; case 7: tab_SFE(i5) = new TriaSfe3_cm7pti(epaisseur,idmail,el->Num_elt(),t_noe);break; case 12: tab_SFE(i5) = new TriaSfe3_cm12pti(epaisseur,idmail,el->Num_elt(),t_noe);break; case 13: tab_SFE(i5) = new TriaSfe3_cm13pti(epaisseur,idmail,el->Num_elt(),t_noe);break; default: cout << "*** pb, le cas "<Num_elt(),t_noe); break; default: cout << "*** pb, le cas "< 0) cout << " attention l'element " << Nom_interpol(enuSfe)<< " "<Num_elt(),t_noe); break; default: if (ParaGlob::NiveauImpression() > 2) cout << " attention le cas "<Num_elt(),t_noe);break; }; } else // cas normal {switch (num_pti) { case 2: tab_SFE(i5) = new TriaQSfe3(epaisseur,idmail,el->Num_elt(),t_noe);break; default: cout << "*** pb, le cas "<Num_elt(),t_noe); break; // case SFE1_3D: tab_SFE(i5) = new TriaSfe1(epaisseur,idmail,el->Num_elt(),t_noe); break; // case SFE2_3D: tab_SFE(i5) = new TriaSfe2(epaisseur,idmail,el->Num_elt(),t_noe); break; case SFE3_3D: tab_SFE(i5) = new TriaSfe3_3D(idmail,el->Num_elt(),t_noe); break; // case SFE3C_3D: tab_SFE(i5) = new TriaSfe3C(epaisseur,idmail,el->Num_elt(),t_noe); break; default: cout << "*** pb, le cas "< valeur par defaut : " << inf_normale << endl;} else {inf_normale = ChangeReel(rep); cout << "--> valeur lue "<< inf_normale << endl; }; break; } default: break; }; // pour l'instant on se fixe une limite inf mais ensuite il faudra demander à l'utilisateur for (int i5 = 1; i5 <= tabelTaille; ++i5) { SfeMembT& el = *((SfeMembT*) tab_SFE(i5)); // pour simplifier Tableau & t_noe = t_noe_ele(i5); // pour simplifier Tableau& tabn = el.Tab_noeud(); // récup de son tableau de noeuds // coordonnées du centre de gravité Coordonnee G = (tabn(1)->Coord0()+tabn(2)->Coord0()+tabn(3)->Coord0())/3.; double epaisseur = el.Epaisseurs(TEMPS_0,G); // épaisseur à G int verif_courb = el.Test_courbure_anormale3(TEMPS_0,inf_normale); // si pb inconnue if (verif_courb == -1) { cout << "\n *** pb verification de l'element "< 1) el.Affiche(2); } else // sinon cas normal de vérif avec remplacement éventuel {// si pb on modifie éventuellement les éléments, a minima on signale if (!verif_courb) {// on crée un nouvel element intermédiaire Element * eleinter; switch (enuSfe) { case SFE1: case SFE2: case QSFE1: cout << "*** pb, la verification de l'element "< 0) cout << " attention l'element " << Nom_interpol(enuSfe)<< " "< 2) cout << " attention le cas "< 0) cout << " attention l'element " << Nom_interpol(enuSfe)<< " "< 2) cout << " attention le cas "<Test_courbure_anormale3(TEMPS_0,inf_normale))) cout << "*** pb, la verification de l'element "<Id_geometrie() != TRIANGLE) // || (tab_element(i2)->Id_interpolation() != LINEAIRE)) // 1 mars 2021 ==> maintenant on peut avoir des triangles internes quadratique if (tab_element(i2)->Id_geometrie() != TRIANGLE) {on_continue = false;break;}; if (!on_continue) { cout << "\n Warning *** the mesh " << idmail << " ( " << nomDuMaillage << " ) " << " does not contain only linear triangular element, currently " << " the continuation of the processing is not possible ! --> stop "; }; // retour return on_continue; }; // lecture et suppression éventuelle des noeuds, non référencés par les éléments et les références void Maillage::LectureEtSuppressionNoeudNonReferencer(UtilLecture * entreePrinc,LesReferences& lesRef) { if (strstr(entreePrinc->tablcar,"suppression_noeud_non_references_")!=NULL) // cas d'une demande de suppression { if (ParaGlob::NiveauImpression() > 2) cout << "\n beginning of the process to delete nodes which are " << " not connected with an element "<< endl ; entreePrinc->NouvelleDonnee(); // on passe le mot clé // appel de l'algo de suppression SuppressionNoeudNonReferencer(lesRef); if (ParaGlob::NiveauImpression() > 2) cout << "\n end of the process to delete nodes which are " << " not connected with an element "<< endl ; }; }; // uniquement suppression éventuelle des noeuds, non référencés par les éléments et les références void Maillage::SuppressionNoeudNonReferencer(LesReferences& lesRef) { if (ParaGlob::NiveauImpression() > 2) cout << "\n delete of nodes which are not referred in the mesh " << nomDuMaillage << endl; // on commence par chercher les noeuds non référencés int taille_tabnoeud=tab_noeud.Taille(); Tableau non_referencer(taille_tabnoeud,true); // on passe en revue tous les éléments, pour marquer les noeuds référencés int nbelem = tab_element.Taille(); for (int ie = 1; ie <= nbelem ; ie++) { Tableau& tnel = tab_element(ie)->Tab_noeud(); int dim_tnel = tnel.Taille(); for (int ib = 1; ib <= dim_tnel; ib++) non_referencer(tnel(ib)->Num_noeud())=false; }; // idem les références // le cas des références fonctionnent, mais en fait, ce n'est pas // une bonne idée de garder les noeuds référencés dans les références seulement // a priori, il vaut mieux ne garder que les noeuds référencés par les éléments // et supprimer tous les autres, d'où la mise en commentaires du cas des références /* const Reference* refe = lesRef.Init_et_Premiere(); while (refe != NULL) { if (refe->Indic() == 1) { // cas d'une référence de noeud ReferenceNE* refeN = ((ReferenceNE*) refe); // on récupère la référence if (refeN->Nom() != "N_tout") {int nb_el = refeN->Taille(); for (int i=1; i<= nb_el; i++) { int num_noeud = refeN->Numero(i); if ((num_noeud > taille_tabnoeud ) || (num_noeud < 1)) { cout << "\n erreur: pendant la suppression des noeuds non references, " << " le numero " << num_noeud << " de la reference " << refeN->Nom() << " n'est pas correcte, peut-etre que le noeud n'existe pas ? " << " on arrete la procedure "; return; } else { non_referencer(num_noeud)=false;}; }; }; }; // on passe à la référence suivante refe = lesRef.Reference_suivante(); }; */ // maintenant on refait un nouveau tableau de noeud avec les noeuds inutiles supprimés Tableau tab_final(taille_tabnoeud); int nbn_restant=0; // le nombre de noeud restant cout << "\n "; for (int ijn = 1; ijn <= taille_tabnoeud; ijn++) {if (non_referencer(ijn) == false) {nbn_restant++; tab_final(nbn_restant) = tab_noeud(ijn); } else { if (ParaGlob::NiveauImpression() > 2) cout << "\n delete of the node " << ijn ; //<< endl; delete tab_noeud(ijn); // suppression du noeud }; }; if (ParaGlob::NiveauImpression() > 2) cout << " \n " << (taille_tabnoeud-nbn_restant) << " nodes have been deleted " << endl; // on redimentionne le tableau final (les premiers restent !!) tab_final.Change_taille(nbn_restant); // on met à jour les références lesRef.SupprimeReference("N_tout",idmail); // on supprime la référence N_tout Tableau nv_num(taille_tabnoeud); // nouveau num/ au ancien for (int ia=1;ia <= nbn_restant; ia++) nv_num(tab_final(ia)->Num_noeud()) = ia; lesRef.Mise_a_jour_ref_noeud(nv_num,idmail,non_referencer); // on enregistre le nouveau tableau de noeud tab_noeud = tab_final; // on met à jour les numéros de noeuds for (int ia=1;ia <= nbn_restant; ia++) tab_noeud(ia)->Change_num_noeud(ia); string ntout("N_tout"); // on recrée la référence N_tout { // on construit le tableau des numéros de noeuds Tableau tabN(nbn_restant); for (int i=1;i<=nbn_restant;i++) tabN(i)=i; ReferenceNE* rtout = new ReferenceNE(tabN,idmail,1,ntout);// construction de la référence lesRef.Ajout_reference(rtout); // ajout de la ref, qui est maintenant géré par lesRef }; }; // Affichage des noeuds, non référencés par les éléments void Maillage::AffichageNoeudNonReferencer() { //if (ParaGlob::NiveauImpression() > 2) // cout << "\n print nodes which are not referred in the mesh " // << nomDuMaillage << endl; // on commence par chercher les noeuds non référencés int taille_tabnoeud=tab_noeud.Taille(); Tableau non_referencer(taille_tabnoeud,true); // on passe en revue tous les éléments, pour marquer les noeuds référencés int nbelem = tab_element.Taille(); for (int ie = 1; ie <= nbelem ; ie++) { Tableau& tnel = tab_element(ie)->Tab_noeud(); int dim_tnel = tnel.Taille(); for (int ib = 1; ib <= dim_tnel; ib++) non_referencer(tnel(ib)->Num_noeud())=false; }; bool existe_non_reference = false; // init par défaut // on regarde s'il faut afficher un warning for (int ijn = 1; ijn <= taille_tabnoeud; ijn++) {if (non_referencer(ijn)) {existe_non_reference=true;break;}; }; if (existe_non_reference) {for (int ijn = 1; ijn <= taille_tabnoeud; ijn++) {if (non_referencer(ijn)) { cout << "\n --- print nodes which are not referred in the mesh --- " << nomDuMaillage ; cout << "\n ** warning: the node " << tab_noeud(ijn)->Num_noeud() << " is not link with an element ! "; } }; }; }; // lecture et création éventuelle d'une ref sur les noeuds, non référencés par les éléments void Maillage::LectureEtCreationRefNoeudNonReferencer(UtilLecture * entreePrinc,LesReferences& lesRef) { if (strstr(entreePrinc->tablcar,"creation_ref_noeud_non_references_")!=NULL) // cas d'une demande de création { if (ParaGlob::NiveauImpression() > 2) cout << "\n beginning of the process of building a new reference on nodes which are " << "\n not connected with an element "<< endl ; entreePrinc->NouvelleDonnee(); // on passe le mot clé // appel de l'algo de suppression CreationRefNoeudNonReferencer(lesRef); if (ParaGlob::NiveauImpression() > 2) cout << "\n end of the process of building a new reference on nodes which are " << "\n not connected with an element "<< endl ; }; }; // création éventuelle d'une référence sur les noeuds, non référencés par les éléments void Maillage::CreationRefNoeudNonReferencer(LesReferences& lesRef) { if (ParaGlob::NiveauImpression() > 2) cout << "\n new ref of nodes which are not referred by an element " << nomDuMaillage << endl; // on commence par chercher les noeuds non référencés int taille_tabnoeud=tab_noeud.Taille(); Tableau non_referencer(taille_tabnoeud,true); // on passe en revue tous les éléments, pour marquer les noeuds référencés int nbelem = tab_element.Taille(); for (int ie = 1; ie <= nbelem ; ie++) { Tableau& tnel = tab_element(ie)->Tab_noeud(); int dim_tnel = tnel.Taille(); for (int ib = 1; ib <= dim_tnel; ib++) non_referencer(tnel(ib)->Num_noeud())=false; }; // maintenant on refait une nouvel liste de noeud avec les noeuds non référencés list list_final; cout << "\n "; for (int ijn = 1; ijn <= taille_tabnoeud; ijn++) {if (non_referencer(ijn) == true) {list_final.push_back(tab_noeud(ijn));}; }; int taille = list_final.size(); if ((ParaGlob::NiveauImpression() > 2) && (taille != 0)) cout << " \n " << taille << " nodes are not referred by an element " << " new ref = N_noeud_libre "<< endl; string nlibre("N_noeud_libre"); // on recrée la référence N_noeud_libre { // on construit le tableau des numéros de noeuds list ::iterator il,ilfin=list_final.end(); Tableau tabN(taille); int i=1; // init for (il = list_final.begin(); il != ilfin;il++,i++) {tabN(i)= (*il)->Num_noeud();} ReferenceNE* rlibre = new ReferenceNE(tabN,idmail,1,nlibre);// construction de la référence lesRef.Ajout_reference(rlibre); // ajout de la ref, qui est maintenant géré par lesRef }; }; // ------------------------------ partie relative à la renumérotation -------------- // lecture et renumérotation éventuelle des noeuds // ici la renumérotation permet d'optimiser la largeur de bande relative au maillage uniquement // par exemple on ne tiens pas compte de relations linéaires éventuelles entre des noeuds void Maillage::LectureEtRenumerotation(UtilLecture * entreePrinc,LesReferences& lesRef) { if (strstr(entreePrinc->tablcar,"renumerotation_des_noeuds_")!=NULL) // cas d'une demande de renumérotation { if (ParaGlob::NiveauImpression() > 2) cout << "\n beginning of the new numbering of nodes " << endl ; entreePrinc->NouvelleDonnee(); // on passe le mot clé // appel de l'algo de renumérotation Tableau > condCLL; // un tableau par défaut bool calcul_ok = Renumerotation(lesRef,condCLL); if (!calcul_ok) // si retour false, pas de renumerotation effectué { cout << "\n ** warning: no new numbering of nodes !! ";}; if (ParaGlob::NiveauImpression() > 2) cout << "\n end of the new numbering of nodes \n" << endl ; }; }; // renumérotation des noeuds du maillage, en fonction de conditions linéaires éventuelles // ramène false si rien n'a changé (à cause d'un pb ou parce que la renumérotation n'est pas meilleure), vrai sinon // nv_tab est tel que : nv_tab(i) est le nouveau numéro qui avait auparavant le numéro "i" bool Maillage::Renumerotation(LesReferences& lesRef ,const Tableau >& condCLL) { bool retour = false; // par défaut pour le retour int taille_tabnoeud=tab_noeud.Taille(); // on calcul le point de départ Tableau < LaLIST_io > t_voisin; // def du tableau des voisins list < list < Noeud_degre > > lis_descent; // stockage des descendants // --- suppression de la création d'éléments frontières, // c'est fait avant, dans l'algo appelant, car certaine fois on ne veut pas que cela change // CreeElemFront(); // on crée tous les éléments frontières if (listFrontiere.size()==0) CreeElemFront(); // on essaie en conditionnel // on crée un tableau intermédiaire pour appeler les routines suivantes qui sont static Tableau *> tt_noeud_front(1); tt_noeud_front(1) = &tab_noeud_front; bool calcul_ok = true; // init par défaut Noeud* noe_dep = Point_de_depart(tab_element,tab_noeud,tt_noeud_front,t_voisin,lis_descent,condCLL,calcul_ok); if (!calcul_ok) // il y a un pb, on sort en le signalant { retour = false;} else // sinon on continue {// on appelle l'algorithme de Cuthill Mac Kee Tableau tab_N_final = Cuthill_Mac_Kee(taille_tabnoeud,noe_dep,t_voisin,lis_descent); // --- maintenant on regarde l'évolution de la largeur de bande en noeud // tout d'abord la largeur initiale int largeur_initiale = LargeurBandeEnNoeuds(tab_element); // on change la numérotation for (int ia=1;ia <= taille_tabnoeud; ia++) tab_N_final(ia)->Change_num_noeud(ia); // nouvelle largeur de bande int largeur_Cuthill = LargeurBandeEnNoeuds(tab_element); // on change en numérotation inverse : Cuthill Mac Kee inverse Tableau tab_N_inverse(tab_N_final); for (int ia=1;ia <= taille_tabnoeud; ia++) {tab_N_final(ia)->Change_num_noeud(1+taille_tabnoeud-ia); tab_N_inverse(1+taille_tabnoeud-ia) = tab_N_final(ia); }; int largeur_Cuthill_inverse = LargeurBandeEnNoeuds(tab_element); if (ParaGlob::NiveauImpression() > 2) { cout << "\n 1/2 largeur de bande en noeud, initiale " << largeur_initiale << " apres l'algo de Cuthill Mac Kee " << largeur_Cuthill << " idem en inverse " << largeur_Cuthill_inverse ; }; // normalement le dernier est le meilleur, en tout cas il est meilleur que le cas 2 if ( (largeur_initiale <= largeur_Cuthill_inverse) && (largeur_initiale <= largeur_Cuthill ) ) // bizarre, cela veut dire qu'il faut revenir à la forme initiale { for (int ia=1;ia <= taille_tabnoeud; ia++) tab_noeud(ia)->Change_num_noeud(ia); if (ParaGlob::NiveauImpression() > 2) cout << "\n pas d'amelioration, on garde la numerotation initiale "<< endl ; retour = false; // pour signaler que rien n'a changé } else if (largeur_Cuthill < largeur_Cuthill_inverse) // bizarre, cela veut dire qu'il faut revenir à la forme de Cuthill directe { for (int ia=1;ia <= taille_tabnoeud; ia++) tab_N_final(ia)->Change_num_noeud(ia); Tableau nv_num(taille_tabnoeud); // nouveau num/ au ancien for (int ia=1;ia <= taille_tabnoeud; ia++) nv_num(ia) = tab_noeud(ia)->Num_noeud(); lesRef.Mise_a_jour_ref_noeud(nv_num,idmail); tab_noeud = tab_N_final; if (ParaGlob::NiveauImpression() > 2) cout << "\n on utilise la numerotation obtenue avec l'algo Cuthill direct "<< endl ; retour = true; // pour signaler qu'il y a eu un changement } else // c'est le cas normale { Tableau nv_num(taille_tabnoeud); // nouveau num/ au ancien for (int ia=1;ia <= taille_tabnoeud; ia++) nv_num(ia) = tab_noeud(ia)->Num_noeud(); lesRef.Mise_a_jour_ref_noeud(nv_num,idmail); tab_noeud = tab_N_inverse; if (ParaGlob::NiveauImpression() > 2) cout << "\n on utilise la numerotation obtenue avec l'algo Cuthill inverse "<< endl ; retour = true; // pour signaler qu'il y a eu un changement }; }; // retour return retour; }; // méthode static: c-a-d indépendante des données du maillage (elle est générale) // calcul du point de départ, en fonction de conditions linéaires éventuelles // il y a également calcul des voisins et de la descendance du noeud de retour // s'il y a un pb, calcul_ok en retour est false Noeud* Maillage::Point_de_depart(const Tableau& tab_elem ,const Tableau& tab_noe ,const Tableau *> tt_noeud_front ,Tableau < LaLIST_io >& t_voisin , list < list < Noeud_degre > > & lis_descent ,const Tableau >& condCLL,bool& calcul_ok) { // début de l'algorithme de gibbs, suivant la description faite par Pironneau // cf. Pironneau_coursWebFR.pdf page 81 // • x,y,... sont les noeuds de la triangulation Th, // • y est voisin de x s'il existe un élément T de Th tel que x et y en soient des noeuds, // • le degré d'un noeud x est le nombre de ses voisins, il est noté d(x), // • les voisins de x constituent N1(x) le niveau 1 de sa descendance, // • les voisins des noeuds de N1(x) non encore répertoriés forment N2(x), // le niveau 2 de la descendance de x (un noeud apparaît au plus une fois // dans Nk(x) et par suite nt peut être dans les autres niveaux), // • l'ensemble des niveaux Nk(x) constitue la descendance, notée D(x) du noeud x, // • la profondeur p de la descendance est le nombre de niveaux de celle-ci, // --- on recherche un bon point de départ // " Parmi les noeuds du contour du domaine, on sélectionne le noeud x " // " tel que d(x) soit minimal: d(x) " calcul_ok=false; // init par défaut Noeud* noe_dep=NULL; // par défaut Maillage::Voisins(tab_elem,tab_noe,t_voisin,condCLL,calcul_ok); // on crée les voisins if (calcul_ok) // on ne continue que si Voisins s'est bien passé {// on définit le degré de chaque noeuds int taille_tabnoeud=tab_noe.Taille(); Tableau dx(taille_tabnoeud,1000000); // un tableau de dx initialisé à très grand for (int idx = 1; idx <= taille_tabnoeud; idx++) { dx(idx) = t_voisin(idx).size(); }; // maintenant on cherche le plus petit dx des noeuds frontière int nb_front = tt_noeud_front.Taille(); int numnoeuddep = 1; int dx_mini=dx((*(tt_noeud_front(1)))(1)->Num_noeud()); for (int ifron = 1; ifron <= nb_front; ifron++) { Tableau & tab_noeud_front = *(tt_noeud_front(ifron)); // pour simplifier int taille_noe_front = tab_noeud_front.Taille(); for (int io =1; io <= taille_noe_front; io++) {int num_noe=tab_noeud_front(io)->Num_noeud(); if (dx(num_noe) < dx_mini) { numnoeuddep = num_noe; dx_mini = dx(num_noe);}; }; }; // Soient D(x) sa descendance et Np(x) son dernier niveau; on réalise alors l'algorithme suivant: // • Début // • Pour tous les noeuds y de Np(x), dans l'ordre croissant de leur degré d(y), // on calcule D(y) et sa profondeur p(y), // • Si p(y) est supérieur à p(x) alors y est sélectionné, on fait x=y et on va à Début, // Sinon on teste un nouvel y. int compt=1; // un compteur pour éviter une boucle infinie noe_dep = tab_noe(numnoeuddep); Descendance (taille_tabnoeud,noe_dep,t_voisin,lis_descent,calcul_ok); if (calcul_ok) // on ne continue que si Voisins s'est bien passé {int profondeur = lis_descent.size(); bool premier_elem_ok = true; do { premier_elem_ok = true; // init dans le cas d'une nouvelle boucle // on calcul la descendance du noeud // récup de son Np(x) list < Noeud_degre >& lis_Npx = *(lis_descent.begin()); lis_Npx.sort(); // classement list < Noeud_degre >::iterator ic,icfin=lis_Npx.end(); for (ic=lis_Npx.begin();ic != icfin; ic++) { list < list < Noeud_degre > > lis_des_inter; Noeud * noei = (*ic).noe; // pour simplifier Descendance (taille_tabnoeud,noei,t_voisin,lis_des_inter,calcul_ok); if (!calcul_ok) break; // si pb dans le calcul de la Descendance on sort de la boucle if (lis_des_inter.size() > profondeur) { // cas où on a trouvé une p(y) plus grand que p(x) profondeur = lis_des_inter.size(); noe_dep = noei; lis_descent = lis_des_inter; premier_elem_ok = false; // pour que l'on refasse une boucle dans le do break; }; }; if (!calcul_ok) break; // si pb dans le calcul de la Descendance on sort de la boucle compt++; // pour éviter une boucle infinie } while ((compt < 1000) && (!premier_elem_ok)); if (calcul_ok) // on ne continue que si Voisins s'est bien passé { // normalement ici soit on a fait 1000 passages et ça n'a pas marché, ou soit c'est ok if (compt == 1000) { cout << "\n Maillage::Renumerotation: on a fait 1000 passage on retiens le premier !! "; // on repasse en revue uniquement la liste des noeuds de lis_Npx pour le premier noeud noe_dep = tab_noe(numnoeuddep); Descendance (taille_tabnoeud,noe_dep,t_voisin,lis_descent,calcul_ok); if (calcul_ok) // on ne continue que si Voisins s'est bien passé {profondeur = lis_descent.size(); bool cestLePoint_deDepart = true; // récup de son Np(x) list < Noeud_degre >& lis_Npx = *(lis_descent.begin()); lis_Npx.sort(); // classement list < Noeud_degre >::iterator ic,icfin=lis_Npx.end(); list < list < Noeud_degre > > lis_des_inter; for (ic=lis_Npx.begin();ic != icfin; ic++) { Noeud * noei = (*ic).noe; // pour simplifier Descendance (taille_tabnoeud,noei,t_voisin,lis_des_inter,calcul_ok); if (!calcul_ok) break; // si pb dans le calcul de la Descendance on sort de la boucle if (lis_des_inter.size() > profondeur) { // cas où on a trouvé une p(y) plus grande que p(x) profondeur = lis_des_inter.size(); noe_dep = noei; cestLePoint_deDepart=false; }; }; if (calcul_ok) {// si la descendance n'est pas sauvegardée if (!cestLePoint_deDepart) lis_descent = lis_des_inter; // ici on est sur de sortir avec le plus petit p(x) }; }; }; }; }; }; // retour return noe_dep; }; // calcul des voisins de tous les noeuds du maillage en fonction des éléments et de conditions // linéaires éventuelles, // la méthode est en static, c-a-d qu'elle est indépendante des données du maillage (elle est générale) // en entrée/sortie t_voisin // restriction: il faut que tab_noe contienne tous les noeuds référencés dans tab_elem et tt_t_condCLL // s'il y a un pb, calcul_ok en retour est false Tableau < LaLIST_io >& Maillage::Voisins(const Tableau& tab_elem ,const Tableau& tab_noe ,Tableau < LaLIST_io >& t_voisin ,const Tableau >& t_t_condCLL,bool& calcul_ok) { // on efface les voisins existants int taille_tabnoeud=tab_noe.Taille(); t_voisin.Change_taille(taille_tabnoeud); for (int it = 1; it <= taille_tabnoeud; it++) t_voisin(it).erase(t_voisin(it).begin(),t_voisin(it).end()); // on passe en revue tous les éléments int nbelem = tab_elem.Taille(); for (int ie = 1; ie <= nbelem ; ie++) { Tableau& tnel = tab_elem(ie)->Tab_noeud(); int dimtab = tnel.Taille(); LaLIST_io listlocal; for (int ine = 1; ine <= dimtab; ine++) // création d'une liste locale de tous les { listlocal.push_front(tnel(ine)); }; // noeud de l'élément listlocal.sort(); // on trie la liste locale // on remplit la liste globale for (int ine = 1; ine <= dimtab; ine++) { int ne = tnel(ine)->Num_noeud(); // le numéro du noeud LaLIST_io list_inter = listlocal; // recopie, list_inter est donc également ordonnée t_voisin(ne).merge(list_inter); // on ajoute en gardant l'ordre dans la liste résultante }; }; // on fait de même avec toutes les conditions linéaires int nb_gr_CLL = t_t_condCLL.Taille(); for (int ig=1;ig<=nb_gr_CLL; ig++) {Tableau & t_condCLL = t_t_condCLL(ig); // pour simplifier int taille_t_condCLL = t_condCLL.Taille(); for (int ic = 1; ic<= taille_t_condCLL; ic++) {const Tableau& tnel = t_condCLL(ic).TabNoeud(); int dimtab = tnel.Taille(); LaLIST_io listlocal; for (int ine = 1; ine <= dimtab; ine++) // création d'une liste locale de tous les { listlocal.push_front(tnel(ine)); }; // noeud de l'élément listlocal.sort(); // on trie la liste locale // on remplit la liste globale for (int ine = 1; ine <= dimtab; ine++) { int ne = tnel(ine)->Num_noeud(); // le numéro du noeud LaLIST_io list_inter = listlocal; // recopie, list_inter est donc également ordonnée t_voisin(ne).merge(list_inter); // on ajoute en gardant l'ordre dans la liste résultante }; }; }; // on supprime maintenant tous les doublons dans les listes des voisins // sachant que t_voisin(i) est déjà ordonné, because on a utilisé merge() for (int inn = 1; inn <= taille_tabnoeud; inn++) t_voisin(inn).unique(); // on supprime les doublons //on vérifie que tous les noeuds ont un voisin calcul_ok = true; // init par défaut if (ParaGlob::NiveauImpression() > 3) {for (int i=1;i<=taille_tabnoeud;i++) if (t_voisin(i).size() == 0) {cout << "\n erreur dans Voisins(.., le noeud " << i << " n'a pas de voisins ! " << endl; calcul_ok = false; // là pb, on signale qu'il y a un pb }; }; // retour return t_voisin; }; // méthode static: c-a-d indépendante des données du maillage (elle est générale) // calcul de la descendance d'un noeud noeu, // on considère que les voisins sont dèjà correctement définis // en entrée la liste qui est modifiée en interne et retournée en sortie // le premier élément de la liste c'est la dernière descendance // s'il y a un pb, calcul_ok en retour est false list < list < Maillage::Noeud_degre > > & Maillage::Descendance(const int& taille_tabnoeud ,Noeud * noeu, Tableau < LaLIST_io >& t_voisin ,list < list < Maillage::Noeud_degre > > & lient,bool& calcul_ok) { //int taille_tabnoeud=tab_noeud.Taille(); int nb_noeud_restant = taille_tabnoeud; // le nombre de noeud a considérer Tableau utiliser(taille_tabnoeud); // un tableau indiquant si le noeud est utilisé ou pas // init du niveau 0 avec le noeud utiliser(noeu->Num_noeud())=true; nb_noeud_restant--; // on vide la liste d'entrée lient.erase(lient.begin(),lient.end()); int dxxy = t_voisin(noeu->Num_noeud()).size(); // le degre du noeud list list_pere; list_pere.push_back(Maillage::Noeud_degre(noeu,dxxy)); // pour le noeud, on rempli le premier niveau LaLIST_io ::iterator kl,klfin; list ::iterator jl,jlfin; // on boucle while ((nb_noeud_restant > 0) && (list_pere.size() != 0)) { list liste_enfant; // l'étage courant jlfin=list_pere.end(); // init // on balaie la liste des peres for (jl=list_pere.begin();jl != jlfin; jl++) { Noeud & noe = *((*jl).noe); // pour simplifier LaLIST_io & lesvoisins = t_voisin(noe.Num_noeud()); klfin=lesvoisins.end(); for (kl=lesvoisins.begin();kl != klfin; kl++) { Noeud * novoi = (*kl); // pour simplifier int num_novoi = novoi->Num_noeud(); // le numéro de novoi if (! (utiliser(num_novoi))) // si le noeud n'a pas déjà été utilisé { int dxx = t_voisin(num_novoi).size(); // le degre du noeud liste_enfant.push_back(Maillage::Noeud_degre(novoi,dxx)); utiliser(num_novoi)=true;nb_noeud_restant--; }; }; }; // on enregistre les enfants lient.push_front(liste_enfant); // on init la liste des peres pour la boucle suivante list_pere = liste_enfant; }; // vérification calcul_ok = true; // init par défaut if (nb_noeud_restant != 0) { if (ParaGlob::NiveauImpression()>3) {cout << "\n problem dans le calcul de la descendance (algorithme de gibbs) !!! il y a " << nb_noeud_restant << " noeuds, qui ne sont pas " << " references par les elements, l'algorithme de renumeration ne fonctionne pas " << " dans ce cas !! "; for (int i=1;i<=taille_tabnoeud;i++) if (!utiliser(i)) cout << "\n c'est le numero : " << i << endl; }; calcul_ok = false; // là pb, on signale }; // retour return lient; }; // méthode static: c-a-d indépendante des données du maillage (elle est générale) // algorithme de Cuthill Mac Kee directe // noeu : le noeud de départ // lis_descent : les descendants de noeud, qui va être modifié dans le processus // ramène une nouvelle numérotation (mais les numéros internes des noeuds ne sont pas changé Tableau Maillage::Cuthill_Mac_Kee(const int& taille_tabnoeud,Noeud * noeu ,Tableau < LaLIST_io >& t_voisin , list < list < Noeud_degre > >& lis_descent) { // un tableau intermédiaire qui permet de savoir si un noeud a déjà été numéroté ou pas Tableau numeroter(taille_tabnoeud,false); // le tableau de retour Tableau tab_N_final(taille_tabnoeud); tab_N_final(1)=noeu; // le premier noeud numeroter(noeu->Num_noeud())=true; int numero_courant=2; // le numéro courant de prochain noeud à numéroter // la boucle // on parcourt la liste des descendants list < list < Noeud_degre > >::reverse_iterator il,ilfin = lis_descent.rend(); list ::iterator it,itfin; LaLIST_io ::iterator kl,klfin; int nb_couche = 1; for (il=lis_descent.rbegin(); il != ilfin; il++,nb_couche++) { list & list_pere = (*il); //cout << "\n couche nb " << nb_couche << endl; // on passe en revue les noeuds des peres et on recalcule le degré en tenant compte // des noeuds déjà numérotés, qui ne doivent pas être pris en compte itfin = list_pere.end(); for (it= list_pere.begin();it != itfin; it++) { Noeud* no = (*it).noe; // pour simplifier LaLIST_io & lesvoisins = t_voisin(no->Num_noeud()); klfin=lesvoisins.end(); int& degre = (*it).degre; // pour simplifier degre = 0; for (kl=lesvoisins.begin();kl != klfin; kl++) { Noeud * novoi = (*kl); // pour simplifier int num_novoi = novoi->Num_noeud(); // le numéro de novoi if (! (numeroter(num_novoi))) // si le noeud n'a pas déjà été numéroté { degre++; }; // on incrémente le degré }; }; // maintenant on ordonne la list_pere en fonction des degrés list_pere.sort(); // et on numérote à partir ce ceux qui ont le plus haut degré, c'est-à-dire le plus // de voisin non encore numéroté list ::reverse_iterator rit,ritfin=list_pere.rend(); //int deb_num = numero_courant; // numéro le plus bas de la couche //cout << "\n "; for (rit= list_pere.rbegin();rit != ritfin; rit++) { //cout << (*rit).degre << " "; tab_N_final(numero_courant)=(*rit).noe; numero_courant++; }; //int fin_num = numero_courant; // numéro le plus haut de la couche // la largeur de bande finale est fonction du maxi de (fin_num - deb_num) //cout << "\n largeur en noeud de la couche = " << fin_num - deb_num << endl; }; // retour return tab_N_final; }; // méthode static: c-a-d indépendante des données du maillage (elle est générale) // calcul de la largeur de bande en noeuds int Maillage::LargeurBandeEnNoeuds(const Tableau& tab_elem) { // def du retour int largeur = 0; // on passe en revue tous les éléments int nbelem = tab_elem.Taille(); for (int ie = 1; ie <= nbelem ; ie++) { Tableau& tnel = tab_elem(ie)->Tab_noeud(); int dim_tnel = tnel.Taille(); for (int ix = 1; ix <= dim_tnel; ix++) for (int iy = ix+1; iy <= dim_tnel; iy++) largeur = MaX(largeur, Abs(tnel(ix)->Num_noeud() - tnel(iy)->Num_noeud()) ); }; // retour return largeur; }; // création automatique des références globales de frontière si demandé dans le .info void Maillage::CreationRefFrontiere(UtilLecture * entreePrinc,LesReferences& lesRef) { if (strstr(entreePrinc->tablcar,"def_auto_ref_frontiere_")!=NULL) { entreePrinc->NouvelleDonnee(); // on passe le mot clé // on demande de créer les références CreationRefFrontiere(lesRef); }; }; // demande de création automatique des références globales de frontière void Maillage::CreationRefFrontiere(LesReferences& lesRef) { if (ParaGlob::NiveauImpression() > 2) cout << " automatic definition of frontier references " << endl ; bool creationEffective=false; // pour savoir s'il y a une création effective // on regarde si les frontières existent, si oui on ne fait rien sinon on les crées ////---- debug // { cout << "\n listFrontiere taille: " << listFrontiere.size() ; // LaLIST ::iterator ili, ilifin = listFrontiere.end(); // // on balaie les éléments frontière, et on récupère les éléments associé // for (ili=listFrontiere.begin();ili!=ilifin;ili++) // (*(ili)).Affiche(); // cout << endl; // }; ////---- fin debug if (listFrontiere.size()==0) CreeElemFront(); ////---- debug // { cout << "\n listFrontiere taille: " << listFrontiere.size() ; // LaLIST ::iterator ili, ilifin = listFrontiere.end(); // // on balaie les éléments frontière, et on récupère les éléments associé // for (ili=listFrontiere.begin();ili!=ilifin;ili++) // (*(ili)).Affiche(); // cout << endl; // }; ////---- fin debug // --- maintenant on commence par définir la référence des noeuds frontières // c-a-d de tous les noeuds contenues dans les éléments frontières string ntoutfront("N_tout_front"); if (!lesRef.Existe(ntoutfront,idmail)) { // on construit le tableau des numéros de noeuds int nbNf = tab_noeud_front.Taille(); Tableau tabN(nbNf); for (int i=1;i<=nbNf;i++) tabN(i)=tab_noeud_front(i)->Num_noeud(); ReferenceNE* rtoutfront = new ReferenceNE(tabN,idmail,1,ntoutfront);// construction de la référence lesRef.Ajout_reference(rtoutfront); // ajout de la ref, qui est maintenant géré par lesRef if (ParaGlob::NiveauImpression() >= 8) rtoutfront->Affiche(); if (ParaGlob::NiveauImpression() >= 4) cout << " N_tout_front "; creationEffective = true; }; // --- on continue par une référence sur les éléments de la frontière string etoutfront("E_front"); if (!lesRef.Existe(etoutfront,idmail)) { // on définit une liste de stockage transitoire, because plusieurs éléments frontières // peuvent se rattacher à un même élément fini list listE; LaLIST ::iterator ili, ilifin = listFrontiere.end(); // on balaie les éléments frontière, et on récupère les éléments associé for (ili=listFrontiere.begin();ili!=ilifin;ili++) listE.push_back((*ili).PtEI()->Num_elt()); listE.sort();// on ordonne la listE listE.unique();// on supprime les doublons // on crée un tableau correspondant à la liste // on construit le tableau des numéros des éléments int nbEfront = listE.size(); Tableau tabE(nbEfront); list ::iterator ibi, ibifin = listE.end(); int i=1; for (ibi=listE.begin();ibi!=ibifin;ibi++,i++) tabE(i)=(*ibi); ReferenceNE* retoutfront = new ReferenceNE(tabE,idmail,2,etoutfront);// construction de la référence lesRef.Ajout_reference(retoutfront); // ajout de la ref, qui est maintenant géré par lesRef if (ParaGlob::NiveauImpression() >= 8) retoutfront->Affiche(); if (ParaGlob::NiveauImpression() >= 4) cout << " ref E_front "; creationEffective = true; }; // ---- puis une référence sur les faces des éléments de la frontière string ftoutfront("F_front"); if (!lesRef.Existe(ftoutfront,idmail)) { // on construit le tableau des numéros d'éléments + numéros de face //int nbEfront = listFrontiere.size(); // on définit une liste de stockage transitoire, because on ne connait pas a priori // le nombre de face frontière list listEface; LaLIST ::iterator ili, ilifin = listFrontiere.end(); // on balaie les éléments fronitère NBelemEtFace nu; // une grandeur de travail for (ili=listFrontiere.begin();ili!=ilifin;ili++) { Enum_type_geom type_front = (*ili).Eleme()->Type_geom_front(); // le type de frontière // on récupère le numéro de la face (éventuellement 0) nu.nbFace = (*ili).Num_frontiere(); // on n'intervient que si la frontière est une face if (type_front == SURFACE) { nu.nbElem = (*ili).PtEI()->Num_elt(); // le numéro de l'élément // on considère que la face existe listEface.push_back(nu); }; }; listEface.sort();// on ordonne la listE listEface.unique();// on supprime les doublons // maintenant on va s'occuper de créer la référence int numElFace = listEface.size(); if (numElFace != 0) { // création des tableaux de numéros d'éléments et faces Tableau tabelem(numElFace); // éléments Tableau tabface(numElFace); // idem pour les face list ::iterator itn,itnfin=listEface.end(); int i=1; for (itn=listEface.begin();itn!=itnfin;itn++,i++) {tabelem(i)=(*itn).nbElem;tabface(i) = (*itn).nbFace;}; ReferenceAF* refAF = new ReferenceAF(tabelem,tabface,idmail,3,ftoutfront);// création de la référence lesRef.Ajout_reference( refAF); if (ParaGlob::NiveauImpression() >= 8) refAF->Affiche(); if (ParaGlob::NiveauImpression() >= 4) cout << " F_front "; creationEffective = true; }; }; // ---- et enfin une référence sur les lignes des éléments // ainsi qu'une référence des seules noeuds des lignes frontières string atoutfront("A_front");string nAreteFront("N_A_front"); //bool n_a_front_existe = lesRef.Existe(nAreteFront,idmail); if (!lesRef.Existe(atoutfront,idmail)) { // on construit le tableau des numéros d'éléments + numéros des arrêtes //int nbEfront = listFrontiere.size(); // on définit une liste de stockage transitoire, because on ne connait pas a priori // le nombre de face frontière list listEArrete; list listN_Arrete; // idem pour les noeuds LaLIST ::iterator ili, ilifin = listFrontiere.end(); // on balaie les éléments frontières NBelemEtArete nu; // une grandeur de travail for (ili=listFrontiere.begin();ili!=ilifin;ili++) { Enum_type_geom type_front = (*ili).Eleme()->Type_geom_front(); // le type de frontière // on récupère le numéro de l'arête (éventuellement 0) nu.nbArete = (*ili).Num_frontiere(); // on n'intervient que si la frontière est une arête if (type_front == LIGNE) { nu.nbElem = (*ili).PtEI()->Num_elt(); // le numéro de l'élément // on considère que l'arête existe listEArrete.push_back(nu); // idem pour les noeuds const Tableau ttn = (*ili).Eleme()->TabNoeud_const(); int taillettn = ttn.Taille(); for (int k=1;k<=taillettn;k++) listN_Arrete.push_back(ttn(k)->Num_noeud()); }; }; // maintenant on va s'occuper de créer la référence d'arêtes int numElArrete = listEArrete.size(); if (numElArrete != 0) { // création des tableaux de numéros d'éléments et d'arêtes Tableau tabelem(numElArrete); // éléments Tableau tabArrete(numElArrete); // idem pour les arêtes list ::iterator itn,itnfin=listEArrete.end(); int i=1; for (itn=listEArrete.begin();itn!=itnfin;itn++,i++) {tabelem(i)=(*itn).nbElem;tabArrete(i) = (*itn).nbArete;}; ReferenceAF* refAF = new ReferenceAF(tabelem,tabArrete,idmail,4,atoutfront);// création de la référence lesRef.Ajout_reference( refAF); if (ParaGlob::NiveauImpression() >= 8) refAF->Affiche(); if (ParaGlob::NiveauImpression() >= 4) cout << " A_front "; creationEffective = true; }; // même chose pour la référence de noeuds listN_Arrete.sort();// on ordonne la listE listN_Arrete.unique();// on supprime les doublons int numNArrete = listN_Arrete.size(); if (numNArrete != 0) { // création des tableaux de numéros de noeuds Tableau tabNArete(numNArrete); list ::iterator itn,itnfin=listN_Arrete.end(); int i=1; for (itn=listN_Arrete.begin();itn!=itnfin;itn++,i++) tabNArete(i)=(*itn); ReferenceNE* refNE = new ReferenceNE(tabNArete,idmail,1,nAreteFront);// création de la référence lesRef.Ajout_reference( refNE); if (ParaGlob::NiveauImpression() >= 4) refNE->Affiche(); if (ParaGlob::NiveauImpression() >= 4) cout << " N_A_front "; creationEffective = true; }; }; if (creationEffective) cout << endl; if (ParaGlob::NiveauImpression() >= 4) cout << " end of automatic definition of frontier references " << endl ; }; // vérification que toutes les références de noeuds, d'éléments, d'arêtes et de faces sont valides // c-a-d se réfèrent à des éléments existants void Maillage::VerifReference(LesReferences& lesRef) { // on va balayer toutes les références const Reference* refi = lesRef.Init_et_Premiere(); // init du balyage while (refi != NULL) { // on prend en compte que si c'est le bon maillage if (refi->Nbmaille() == idmail) { // choix en fonction du type de référence switch (refi->Indic()) {case 1: // cas de noeuds {const ReferenceNE & refN = *((ReferenceNE *) refi); int reftaille = refN.Taille(); int nbN = tab_noeud.Taille(); for (int nn =1; nn<= reftaille;nn++) if ((refN.Numero(nn) < 0) || (refN.Numero(nn) > nbN)) {cout << "\n **** error, the node number " << refN.Numero(nn) << " don't belong to the range of nodes of the mesh " << nomDuMaillage << " [1,"<< nbN << "] " << "\n error of definition of the reference of nodes : " << refi->Nom() << endl; if (ParaGlob::NiveauImpression() >= 6) cout << "\n Maillage::VerifReference(LesReferences& lesRef) "; Sortie(1); } } break; case 2: // cas d'éléments {const ReferenceNE & refE = *((ReferenceNE *) refi); int reftaille = refE.Taille(); int nbE = tab_element.Taille(); for (int nn =1; nn<= reftaille;nn++) if ((refE.Numero(nn) < 0) || (refE.Numero(nn) > nbE)) {cout << "\n **** error, the element number " << refE.Numero(nn) << " don't belong to the range of element of the mesh " << nomDuMaillage << " [1,"<< nbE << "] " << "\n error of definition of the reference of elements : " << refi->Nom() << endl; if (ParaGlob::NiveauImpression() >= 6) cout << "\n Maillage::VerifReference(LesReferences& lesRef) "; Sortie(1); } } break; case 3: // cas de surfaces d'éléments {const ReferenceAF & refF = *((ReferenceAF *) refi); int reftaille = refF.Taille(); int nbE = tab_element.Taille(); for (int nn =1; nn<= reftaille;nn++) { // récupération du numéro de face et élément const int numE = refF.NumeroElem(nn); const int numF = refF.NumeroFA(nn); if ((numE < 0) || (numE > nbE)) {cout << "\n **** error, the element number " << numE << " don't belong to the range of element of the mesh " << nomDuMaillage << " [1,"<< nbE << "] " << "\n error of definition of the reference of elements surfaces : " << refi->Nom() << endl; if (ParaGlob::NiveauImpression() >= 6) cout << "\n Maillage::VerifReference(LesReferences& lesRef) "; Sortie(1); }; // arrivé ici, cela signifie que l'élément existe, maintenant on regarde si la face peut exister int nbface = tab_element(numE)->ElementGeometrique().NbFe(); if ((numF < 0) || (numF > nbface)) {cout << "\n **** error, the surface number " << numF << " of the element " << numE << " don't belong to the range of possible surfaces numbers of the element [1,"<< nbface << "] " << "\n error of definition of the reference of elements surfaces : " << refi->Nom() << endl; if (ParaGlob::NiveauImpression() >= 6) cout << "\n Maillage::VerifReference(LesReferences& lesRef) "; Sortie(1); }; }; } break; case 4: // cas d'arêtes d'éléments {const ReferenceAF & refA = *((ReferenceAF *) refi); int reftaille = refA.Taille(); int nbE = tab_element.Taille(); for (int nn =1; nn<= reftaille;nn++) { // récupération du numéro d'arête et élément const int numE = refA.NumeroElem(nn); const int numA = refA.NumeroFA(nn); if ((numE < 0) || (numE > nbE)) {cout << "\n **** error, the element number " << numE << " don't belong to the range of element of the mesh " << nomDuMaillage << " [1,"<< nbE << "] " << "\n error of definition of the reference of elements edge : " << refi->Nom() << endl; if (ParaGlob::NiveauImpression() >= 6) cout << "\n Maillage::VerifReference(LesReferences& lesRef) "; Sortie(1); }; // arrivé ici, cela signifie que l'élément existe, maintenant on regarde si l'arête peut exister int nbArete = tab_element(numE)->ElementGeometrique().NbSe(); if ((numA < 0) || (numA > nbArete)) {cout << "\n **** error, the edge number " << numA << " of the element " << numE << " don't belong to the range of possible edges numbers of the element [1,"<< nbArete << "] " << "\n error of definition of the reference of elements edges : " << refi->Nom() << endl; if (ParaGlob::NiveauImpression() >= 6) cout << "\n Maillage::VerifReference(LesReferences& lesRef) "; Sortie(1); }; }; } break; default: if (ParaGlob::NiveauImpression() >= 6) cout << " \n indication ! no verification for reference on element node and on gauss point "; break; } } refi = lesRef.Reference_suivante(); } }; // méthode pour initialiser les différents tableaux utilisés par Orientation_elements_mitoyens_recursif void Maillage::Init_Orientation_elements_mitoyens_recursif() { // - on initialise le tableau d'indicateur, permettant de savoir si un élément a été traité ou pas int nb_elem = tab_element.Taille(); ind_elem.Change_taille(nb_elem,false); // def et init à 0, c-à-d non traité // --- à chaque fois que l'on va inverser le sens d'un élément, le sens de ses frontières normalement devrait également changer // mais comme on ne les recalcule pas, on utilise un tableau pour s'en rappeler et s'en servir, au début = 1 pour tous les éléments tab_sens_element.Change_taille(nb_elem,1.); // ensuite quand le sens change, la valeur passe à -1 }; // ** pour l'instant ne fonctionne que pour les éléments surfaces // orientation automatique des éléments mitoyens, à l'élément num_elem, et ensuite // récursivement à tous les éléments mitoyens des mitoyens jusqu'à ce que la chaine s'arrête // L'ensemble des éléments est alors groupé dans une référence qui est construit // . soit à partir du numéro num_elem // . soit à partir d'un nom passé en paramètre (cf. nom_ref, ci dessous) // et qui est ajouté aux refs déjà existantes // ensuite, le programme passe en revue les éléments restants, et regarde s'ils font parti // d'un ensemble homogène orienté, si non, ramène la liste des éléments hors ensemble orienté // *** ne concerne que les éléments surfaces, les autres sont ignorés // ind_elem(i) : (tableau interne) indique si l'élément i a été traité (=true) ou non (=false) // ind_elem: est pris en compte puis mis à jour par le programme, c-a-d que les nouveaux éléments orienté // passe de false à true, par contre tous les éléments déjà à true, ne sont pas pris en compte dans le traitement // angle_maxi : angle maximum entre deux éléments, au dessus duquel on considère qu'il y a une rupture de la mitoyenneté // nom_ref : s'il est différent de "_", donne le nom de base voulu à la série de référence qui va être construite // inverse : indique si l'on souhaite partir d'une orientation inverse de celle existante avant application de l'algo // ceci pour l'élément de départ: au premier appel, l'opération est toujours possible, ensuite cela dépend // si l'élément trouvé a déjà été traité ou pas // recursiv : indique si l'on se situe dans une suite récursive d'appel de la méthode // si oui, seule les éléments non déjà pris en compte dans les appels précédents, sont examiné // c'est ind_elem qui permet de s'en assurer // si non: cas par exemple d'un angle_maxi qui change, on réexamine tous les éléments, cependant // la ré_orientation éventuelle n'est faite qu'une seule fois (toujours via ind_elem) list Maillage::Orientation_elements_mitoyens_recursif(bool recursif,string& nom_base_ref,int num_elem ,LesReferences& lesRef,double& angle_maxi,bool inverse) { double cos_angle_maxi = cos(angle_maxi); // pour simplifier // --- à chaque fois que l'on va inverser le sens d'un élément, le sens de ses frontières normalement devrait également changer // mais comme on ne les recalcule pas, on utilise le tableau tab_sens_element (tableau interne) // pour s'en rappeler et s'en servir, au début = 1 pour tous les éléments // ensuite quand le sens change, la valeur passe à -1 int nb_elem = tab_element.Taille(); int compteur_elem_inverse=0; // pour compter le nombre d'éléments inversés // --- def de la liste des éléments qui ont été traité list ele_oriente; // la liste des éléments qui ont été orienté, donc qui ont la même orientation // servira à la fin pour créer les références // --- définition et initialisation de la liste d'éléments qu'il faut traiter list ele_a_traiter; // la liste des éléments qu'il faut traiter ele_a_traiter.push_back(num_elem); // on initialise la liste à l'aide du numéro du premier élément à traiter // si c'est possible on change l'orientation de l'élément si c'est demandé if (inverse && (ind_elem(num_elem)==false)) { tab_element(num_elem)->Modif_orient_elem(0); tab_sens_element(num_elem)=-1.; }; #ifdef MISE_AU_POINT if (ParaGlob::NiveauImpression() > 4) { cout << "\n racine element nb: "< > t_front_deja_traite(nb_elem); for (int i=1;i<=nb_elem;i++) t_front_deja_traite(i).Change_taille(mitoyen_de_chaque_element(i).Taille(),false); // --- on va passer en revue tous les éléments de manière récursive, ceci à partir du premier élément: num_elem // on n'intervient que pour les éléments surfaces en 3D et ligne en 2D int dim = ParaGlob::Dimension(); // la dimension de l'espace de travail Coordonnee A,B,C,AB,AC,N; // des variables de travail Coordonnee Ap,Bp,Dp,ApBp,ApDp,N_mitoy; // """ while (ele_a_traiter.size() != 0) { list ::iterator iter_elem_a_traiter = ele_a_traiter.begin(); // récup du premier élément int num_elem_a_traiter = (*iter_elem_a_traiter); // récup du numéro de l'élément ele_a_traiter.erase(iter_elem_a_traiter); // on supprime l'élément de la liste à traiter // récup des Front de l'élément Tableau & mtce = mitoyen_de_chaque_element(num_elem_a_traiter); // et on ne contine que s'il s'agit d'un élément surface (ajout 15 avril 2016) en 3D // ou une ligne en 2D if ( (Type_geom_generique(tab_element(num_elem_a_traiter)->Id_geometrie()) == SURFACE) || ((dim == 2)&& (Type_geom_generique(tab_element(num_elem_a_traiter)->Id_geometrie()) == LIGNE)) ) {int dim_mtce = mtce.Taille(); // on passe en revue tous les Front for (int i=1;i<= dim_mtce;i++) { // on ne va s'intéresser qu'aux frontières lignes, qui sont celles qui joignent les éléments de surface // + aux cas où il existe des éléments mitoyens + au cas où le Front n'a pas été traité Front& fro = mtce(i); // pour simplifier const Tableau * tabmitoy = fro.TabMitoyen(); if ((fro.Eleme()->Type_geom_front() == LIGNE) && (tabmitoy != NULL) && (!t_front_deja_traite(num_elem_a_traiter)(fro.Num_frontiere())) ) { int tail_tabmitoy = tabmitoy->Taille(); // --- on calcul une normale à l'élément à l'aide des extrémités de la frontière ligne et d'un autre point // ou d'un déterminant, dans le cas 2D Tableau & tab_N_fro = fro.Eleme()->TabNoeud(); // récup des noeuds de la frontière ligne int tail_N_fro=tab_N_fro.Taille(); // récup de la taille A=tab_N_fro(1)->Coord0(); B= tab_N_fro(tail_N_fro)->Coord0(); // def des extrémités // on récupère les numéros de A et B int num_A=tab_N_fro(1)->Num_noeud();int num_B=tab_N_fro(tail_N_fro)->Num_noeud(); // on cherche un troisième noeud de l'élément, différent Tableau& tab_N_elem = tab_element(num_elem_a_traiter)->Tab_noeud(); int NBN=tab_N_elem.Taille(); bool trouve_C=false; for (int j=1;j<=NBN;j++) { Noeud * pt_noeud=tab_N_elem(j); if ((pt_noeud->Num_noeud() != num_A) && (pt_noeud->Num_noeud() != num_B)) { C=pt_noeud->Coord0(); trouve_C=true;break;}; }; if (!trouve_C) { cout << "\n *** erreur dans l'algo Orientation_elements_mitoyens_recursif, on n'a pas" << " trouve de point C ?? l'orientation automatique risque d'etre erronee " << endl; }; // calcul des vecteurs intermédiaire AB=B-A;AC=C-A; double deter_centrale = 0.; // util pour le cas 2D if (dim == 3) // dans le cas 3D on utilise la normal { N=Util::ProdVec_coor(AB,AC); N.Normer();} // calcul de la normale, normée else { deter_centrale = Util::Determinant(AB,AC);}; // cas 2D: on utilise le déterminant pour déterminer le sens Front* fro_retenu=NULL; // init du Front que l'on retiendra au final bool mitoyen_meme_sens = true; // par défaut on initialise l'élément mitoyen comme étant de même sens // dans le cas où on a plusieurs éléments Front mitoyen, et que l'on est en dimension 3, on choisit un seul, // celui qui correspond à un Element qui est le plus dans le prolongement de l'élément num_elem_a_traiter // en dimension 2, il n'y a pas de choix possible, on prend le premier élément if ((tail_tabmitoy > 1) && (dim == 3)) { // --- on passe en revue tous les mitoyens et on retiend celui donc la normale est le plus // avec celle de l'élément // donc dont le produit scalaire avec la normale, est le plus important double projection = 0.; int num_front_mitoy = 0; for (int j=1;j<=tail_tabmitoy;j++) { Front& fri = *(*tabmitoy)(j); // pour simplifier // si on est récursif, on ne continue que si l'élément associé n'a pas été traité int num_e = fri.PtEI()->Num_elt(); // récup du numéro de l'élément // et on ne contine que s'il s'agit d'un élément surface (ajout 15 avril 2016) en 3D if (Type_geom_generique(tab_element(num_e)->Id_geometrie()) == SURFACE) {if (!ind_elem(num_e) || !recursif ) { Tableau & tab_N_fri = fri.Eleme()->TabNoeud(); // récup des noeuds de la frontière ligne int tail_N_fri=tab_N_fri.Taille(); // récup de la taille Ap=tab_N_fri(1)->Coord0(); Bp= tab_N_fri(tail_N_fro)->Coord0(); // def des extrémités // on récupère les numéros de Ap et Bp int num_Ap=tab_N_fri(1)->Num_noeud();int num_Bp=tab_N_fri(tail_N_fri)->Num_noeud(); // on cherche un troisième noeud de l'élément, différent des deux noeuds centraux Tableau & tab_N_elem_mitoy = fri.PtEI()->Tab_noeud(); // récup des noeuds de l'élément mitoyen int NBN_mitoyen=tab_N_elem_mitoy.Taille(); bool trouve_Dp = false; for (int k=1;k<=NBN_mitoyen;k++) { Noeud * pt_n=tab_N_elem_mitoy(k); if ((pt_n->Num_noeud() != num_Ap) && (pt_n->Num_noeud() != num_Bp)) { Dp=pt_n->Coord0(); trouve_Dp = true; break;}; }; if (!trouve_Dp) { cout << "\n *** erreur dans l'algo Orientation_elements_mitoyens_recursif, on n'a pas" << " trouve de point Dp ?? l'orientation automatique risque d'etre erronee " << endl; }; // calcul de la nouvelle normale, normée ici pour que ça soit indépendant de la surface de ABD // (on utilise que la normale, car ici on doit être en 3D vue le test en début de block) ApBp=Bp-Ap; ApDp=Dp-Ap; N_mitoy=Util::ProdVec_coor(ApBp,ApDp);N_mitoy.Normer(); // si l'élément de référence (num_elem_a_traiter) a eu un sens modifié, on en tient compte à l'aide de tab_sens_element double essai_projection = tab_sens_element(num_elem_a_traiter) * N * tab_sens_element(num_e) * N_mitoy; // fro.Affiche(); fri.Affiche(); // cout << "\n tab_sens_element(num_elem_a_traiter) " << tab_sens_element(num_elem_a_traiter) << " essai_projection "<< essai_projection; // cout << "\n ele= "<< fro.PtEI()->Num_elt() << " elem_mitoy= " << fri.PtEI()->Num_elt() // << "\n A:" << A << " B: " << B << " C: " << C << " N: " << N // << "\n Ap: "< Abs(projection)) {projection = essai_projection; num_front_mitoy = j;}; // cout << "\n ** num_front_mitoy= " << num_front_mitoy << " projection= " << projection << endl; }; }; }; // arrivée ici on a la projection de l'élément associé, dans le cas où l'on a trouvé un élément non déjà traité if((num_front_mitoy != 0) && (Abs(projection) > Abs(cos_angle_maxi))) { fro_retenu = (*tabmitoy)(num_front_mitoy); // on enregiste le front de l'élément mitoyen if (projection < 0) {mitoyen_meme_sens = false;} // on regarde le sens else {mitoyen_meme_sens = true;}; }; } else if (tail_tabmitoy != 0) { // si on est récursif, on ne continue que si l'élément associé n'a pas été traité int num_e = (*tabmitoy)(1)->PtEI()->Num_elt(); // récup du numéro de l'élément // et on ne contine que s'il s'agit d'un élément surface (ajout 15 avril 2016) en 3D // ou une ligne en 2D if ( (Type_geom_generique(tab_element(num_e)->Id_geometrie()) == SURFACE) || ((dim == 2)&& (Type_geom_generique(tab_element(num_e)->Id_geometrie()) == LIGNE)) ) { if (!ind_elem(num_e) || !recursif ) { fro_retenu = (*tabmitoy)(1); // enreg // on calcul une normale, pour ensuite avec la projection, savoir si le sens des éléments mitoyens est identique ou pas Tableau & tab_N_fro_retenu = fro_retenu->Eleme()->TabNoeud(); // récup des noeuds de la frontière ligne int tail_N_fro_retenu=tab_N_fro_retenu.Taille(); // récup de la taille Ap=tab_N_fro_retenu(1)->Coord0(); Bp= tab_N_fro_retenu(tail_N_fro)->Coord0(); // def des extrémités // on récupère les numéros de Ap et Bp int num_Ap=tab_N_fro_retenu(1)->Num_noeud();int num_Bp=tab_N_fro_retenu(tail_N_fro_retenu)->Num_noeud(); // on cherche un troisième noeud de l'élément, différent des deux noeuds centraux Tableau & tab_N_elem_mitoy = fro_retenu->PtEI()->Tab_noeud(); // récup des noeuds de l'élément mitoyen int NBN_mitoyen=tab_N_elem_mitoy.Taille(); for (int k=1;k<=NBN_mitoyen;k++) { Noeud * pt_n=tab_N_elem_mitoy(k); if ((pt_n->Num_noeud() != num_Ap) && (pt_n->Num_noeud() != num_Bp)) { Dp=pt_n->Coord0(); break;}; }; // on différencie ici les cas 2D et 3D ApBp=Bp-Ap; ApDp=Dp-Ap; if (dim == 3) { // calcul de la nouvelle normale, normée ici pour que ça soit indépendant de la surface de ABD N_mitoy=Util::ProdVec_coor(ApBp,ApDp);N_mitoy.Normer(); // si l'élément de référence (num_elem_a_traiter) a eu un sens modifié, on en tient compte à l'aide de tab_sens_element // idem avec l'élément considéré double projection = tab_sens_element(num_elem_a_traiter) * N * tab_sens_element(num_e) * N_mitoy; if ( projection < 0) {mitoyen_meme_sens = false;} // on regarde le sens else {mitoyen_meme_sens = true;}; // si l'angle est trop grand on ne tiens pas compte de la mitoyenneté if (Abs(projection) < Abs(cos_angle_maxi)) fro_retenu = NULL; // fro.Affiche(); fro_retenu->Affiche(); // cout << "\n tab_sens_element(num_elem_a_traiter) " << tab_sens_element(num_elem_a_traiter) << " projection "<PtEI()->Num_elt(); // récup du numéro de l'élément if (!t_front_deja_traite(num_e)(fro_retenu->Num_frontiere())) { // si il faut un changement d'orientation et que c'est possible on change bool suite_traitement_possible = true; // init par défaut if (!mitoyen_meme_sens) // changement d'orientation au cas où { if (!ind_elem(num_e)) {tab_element(num_e)->Modif_orient_elem(0); // " tab_sens_element(num_e)=-1.; compteur_elem_inverse++; } else {suite_traitement_possible = false; #ifdef MISE_AU_POINT if (ParaGlob::NiveauImpression() > 4) { cout << "\n ref "<Num_frontiere())=true; // pour l'élément fils }; }; }; }; }; }; }; if (ParaGlob::NiveauImpression() > 3) {if (ParaGlob::Francais()) {cout << "\n nombre d'element inverses " << compteur_elem_inverse;} else {cout << "\n number of elements with change of orientation : " << compteur_elem_inverse;}; }; // --- définit une nouvelle référence qui contient tous les éléments qui ont été traité // s'il s'agit bien de surface // on définit un nom possible pour la référence if ( (Type_geom_generique(tab_element(num_elem)->Id_geometrie()) == SURFACE) || ((dim == 2)&& (Type_geom_generique(tab_element(num_elem)->Id_geometrie()) == LIGNE)) ) {string nom_ref; if (nom_base_ref == "_") { nom_ref = "E_ref_de_meme_orientation_que_ele_"+ChangeEntierSTring(num_elem);} else // cas où l'on spécifie un nom { nom_ref = "E_"+nom_base_ref;}; while (lesRef.Existe(nom_ref,idmail)) // on regarde si le nom existe, si oui on ajoute un tiret nom_ref = nom_ref+"-"; ReferenceNE* refeE = new ReferenceNE(ele_oriente,idmail,2, nom_ref); // création de la ref lesRef.Ajout_reference(refeE); // ajout de la ref // idem pour une référence de face string nom_refS; if (nom_base_ref == "_") { nom_refS = "F_ref_de_meme_orientation_que_ele_"+ChangeEntierSTring(num_elem);} else // cas où l'on spécifie un nom { nom_refS = "F_"+nom_base_ref;}; while (lesRef.Existe(nom_refS,idmail)) // on regarde si le nom existe, si oui on ajoute un tiret nom_refS = nom_refS+"-"; // il faut définir une deuxième liste de 1 pour définir des faces list list_face(ele_oriente.size(),1); ReferenceAF* refeS = new ReferenceAF(ele_oriente,list_face,idmail,2, nom_refS); // création de la ref lesRef.Ajout_reference(refeS); // ajout de la ref if (ParaGlob::NiveauImpression() > 0) {if (ParaGlob::Francais()) {cout << "\n creation de la reference : " << nom_ref << " ("<Taille() <<" elements )"; cout << "\n creation de la reference : " << nom_refS << " ("<Taille() <<" surfaces )";} else {cout << "\n creation of the reference : " << nom_ref << " ("<Taille() <<" elements )"; cout << "\n creation of the reference : " << nom_refS << " ("<Taille() <<" surfaces )";}; }; }; // --- on construit la liste des éléments qui n'ont pas été orienté list list_elem_non_orient; // def de la liste des éléments non orientés for (int i=1;i<=nb_elem;i++) if (!ind_elem(i)) list_elem_non_orient.push_back(i); // retour return list_elem_non_orient; }; // change le numéro de maillage void Maillage::Change_numero_maillage(int new_num) { if (idmail != new_num) { idmail = new_num; // on s'occupe des noeuds, int nbn = tab_noeud.Taille(); for (int i=1;i<=nbn;i++) tab_noeud(i)->Change_num_Mail(new_num); // puis les éléments int nbelem = tab_element.Taille(); for (int ie=1;ie<=nbelem;ie++) tab_element(ie)->Change_num_maillage(new_num); // la liste des noms de maillages listeNomMail[nomDuMaillage]=idmail; }; }; // méthode pour orienter des éléments en fonction d'un rayon: AG, A étant un point donné, G étant le centre de // gravité d'une facette // A : coordonnée d'un point, si indic_G(i) est true, alors A(i) est remplacé par la coordonnée G(i) du centre // de gravité de la facette // zone_a_traiter: le nom de la référence des éléments a traiter // inverse : si true, l'orientation des normales des facettes est identique à celles de AG, sinon c'est l'inverse void Maillage::Orientation_via_rayon(const Tableau & indic_G,const Coordonnee & A ,const string& zone_a_traiter, LesReferences& lesRef, bool inverse) { // on commence par récupérer la liste des éléments sur laquelle on va travailler const ReferenceNE & refa = ((ReferenceNE &) lesRef.Trouve(zone_a_traiter,idmail)); const Tableau& tabN_de_Ref = refa.Tab_num(); // le tableau des éléments int dim = ParaGlob::Dimension(); if (dim != 3) { cout << "\n la dimension de l'espace n'est pas 3, le traitement n'est pas possible !! "; if (ParaGlob::NiveauImpression() > 2) cout << "\n Maillage::Orientation_via_rayon(..."; cout << endl; return; }; // on parcours tous les éléments int nbn_ref = tabN_de_Ref.Taille(); for (int i = 1; i <= nbn_ref;i++) { int nbE = tabN_de_Ref(i); // on récupère le numéro de l'élément // on n'effectue le traitement que s'il s'agit d'une surface Element& ele = *(tab_element(nbE)); ////--- debug //if (ele.Num_elt()==316) // cout << "\n debug Maillage::Orientation_via_rayon(..." << endl; // ////--- fin debug if (Type_geom_generique(ele.Id_geometrie()) == SURFACE) { //calcul du centre de gravité Coordonnee3 G(dim); Tableau taN = ele.Tab_noeud(); int nbn = taN.Taille(); for (int inn = 1;inn<=nbn;inn++) {Noeud* noe = taN(inn); G += noe->Coord0(); }; G /= nbn; // calcul du vecteur rayon Coordonnee3 AG(dim); for (int ia = 1;ia<=dim;ia++) { if (indic_G(ia)) {AG(ia)=0.; } // c'est le cas où on utilise la coordonnée du centre de gravité else {AG(ia) = G(ia) - A(ia);}; }; AG.Normer(); // // on calcul une normale moyenne à la surface // // tout d'abord on choisit un premier point pour un premier segment // Coordonnee3 B = taN(1)->Coord0(); // Coordonnee3 GB(B-G); // Coordonnee3 N; // // on parcours tous les noeuds et on ajoute les normales // for (int inn = 2;inn<=nbn;inn++) // {Noeud* noe = taN(inn); // Coordonnee3 C(noe->Coord0()); // Coordonnee3 GC(C-G); // N += Util::ProdVec_coor(GB,GC); // }; // N /= nbn-1; // en fait la précédente méthode peut poser des pb, // on choisit une méthode plus simple // une première arête Coordonnee3 A1A2(taN(2)->Coord0()-taN(1)->Coord0()); // une seconde Coordonnee3 A2A3(taN(3)->Coord0()-taN(2)->Coord0()); Coordonnee3 N(Util::ProdVec_coor(A1A2,A2A3)); N.Normer(); // on test le sens bool a_inverser = false; // init if ( (AG * N) > 0.) // même sens {if (inverse) a_inverser = true; else a_inverser = false; } else // sens inverse {if (inverse) a_inverser = false; else a_inverser = true; }; ////--- debug //{if (ele.Num_elt()==1) // cout << "\n debug Maillage::Orientation_via_rayon(..." << endl; // cout << "\n element: " << ele.Num_elt() <<", AG * N = " << AG * N << " a_inverser= "<< a_inverser // << " AG= "<