Herezh_dev/Elements/Geometrie/Frontiere/ElFrontiere.cc
Gérard Rio 568dba18c7 version 7.029: (par rapport à la 7.028)
- modification du calcul des normales sur les facettes et sur les frontières, à l'initialisaion et lors des mises à jour
 - optimisation sur la l'ajout de grandeurs quelconque aux noeuds:
   . les anciennes versions étaient fonctionnelles  mais introduisait un stockage important. Avec  la nouvelle version,  on  économise 500M0 sur 2 Go !!
2024-04-24 10:34:35 +02:00

531 lines
23 KiB
C++

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