Gérard Rio
9692dbd130
- contient les éléments finis, métriques associées, déformations ... intégration du réperoire Géométrie: - contient les géométries 1D 2D et 3D, les frontières des éléments géométriques
436 lines
18 KiB
C++
436 lines
18 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-2021 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;
|
|
};
|
|
|
|
// 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;
|
|
};
|
|
|
|
|