Herezh_dev/Util/MathUtil2.cc

1091 lines
42 KiB
C++
Raw Normal View History

// 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 "MathUtil2.h"
#include "nrutil.h"
#include "jacobi.h"
#include "Util.h"
#include "ConstMath.h"
#include "MathUtil.h"
#ifndef MATHUTIL2_H_deja_inclus
// vecteurs propre et valeurs propres d'une matrice nxn c-a-d : en particulier 3 3 ou 2 2 ou 1 1
// avec une limitation sur n (< 50)
// *** dans le cas où les matrices sont symétriques !! ***
// la méthode est itérative, --> méthode de Jacobi
// la matrice A est ecrasee, a la sortie chaque colonne represente un vecteur propre
// le vecteur de retour contiend les valeurs propres
// s'il y a une erreur dans le calcul : cas = -1
#ifndef MISE_AU_POINT
inline
#endif
Vecteur MathUtil2::VectValPropre(Mat_pleine& A,int& cas)
{ // appel d'une routine jacobi
int nbcol = A.Nb_colonne();
#ifndef MISE_AU_POINT
int nblign = A.Nb_ligne();
if (nbcol != nblign)
{ cout << "\n erreur dans VectValPropre(Mat_pleine& A), la matrice n'est pas carré"
<< endl;
Sortie(1);
}
#endif
// 1) def de la matrice et du vecteur contenant les vecteurs propre et les valeurs propres
Vecteur d(nbcol);
Mat_pleine V(nbcol,nbcol);
int nrot =0; // nombre de rotation dans jacobi
// 3) appel du programme de la bibliothèque (fortement modifié)
Jacobi jaco; // une instance par défaut
jaco.jacobi(A,d,V,nrot);
// traitement erreur
if (nrot == -1) {cas = -1;} else {cas = 1;};
// 4) mise en ordre des valeurs et vecteurs propres
jaco.eigsrt(d,V);
// 5) sauvegarde et retour
A = V;
return d;
};
//calcul des vecteurs propres pour une matrice 3x3 réel uniquement, pas forcément symétrique
// dans le cas où on connait déjà les valeurs propres
// --> Utilisable que pour des matrices carrées
// en entrée : VP, de dimension 3, Vp contient les valeurs propres
// les 3 valeurs propres sont considérées classées: VP(1) >= VP(2) >= VP(3)
// cas : qui indique le type de valeurs propres
// , cas = 1 si 3 val propres différentes (= 3 composantes du vecteur de retour)
// , cas = 0 si les 3 sont identiques (= la première composantes du vecteur de retour),
// , cas = 2 si Vp(1)=Vp(2) ( Vp(1), et Vp(3) dans les 2 premières composantes du retour)
// , cas = 3 si Vp(2)=Vp(3) ( Vp(1), et Vp(2) dans les 2 premières composantes du retour)
// en sortie : le tableau des vecteurs propres, rangés selon l'ordre d'entrée
// cas = le cas de l'entrée, sauf s'il y a eu un pb, dans ce cas: cas = -1, et les vecteurs propres sont
// dans ce cas quelconques
//
#ifndef MISE_AU_POINT
inline
#endif
Tableau <Coordonnee> MathUtil2::V_Propres3x3(const Mat_pleine& A,const Coordonnee & VP, int & cas )
{
#ifdef MISE_AU_POINT
if ((A.Nb_colonne() != 3)|| (A.Nb_ligne() != 3))
{cout << "\n erreur de dimension: Nb_colonne()= " << A.Nb_colonne() << ", Nb_ligne()= ";
cout << A.Nb_ligne() << " alors que cette methode ne s'applique qu'aux matrices 3x3 ! "
<< "\n V_Propres3x3(..." << endl;
Sortie(1);
};
if (VP.Dimension() != 3)
{cout << "\n erreur il n'y a pas 3 valeurs propres ! " << endl;
Sortie(1);
};
if ( (VP(1) < VP(2)) || (VP(2) < VP(3)) || (VP(1) < VP(3)) )
{cout << "\n erreur les trois valeurs propres ne sont pas dans le bon ordre (VP(1) >= VP(2) >= VP(3)) "
<< "\n VP(1)= " << VP(1) << ", VP(2)= " << VP(2) << ", VP(3)= " << VP(3)
<< "\n V_Propres3x3(..." << endl;
Sortie(1);
};
#endif
// initialisation
Coordonnee3 zero(3);
Tableau <Coordonnee> Vpropre(3,zero); // les vecteurs propres
// on utilise une copie de la matrice d'entrée
Mat_pleine B(A);
// dans tous les cas il y a au moins une valeur propre, on démarre avec celle là
// on calcul la matrice B-lambda*Id qui normalement doit-être singulière
////---debug
//cout << "\n debug V_Propres3x3 ";
//cout <<"\n B: ";
//B.Affiche();
//// --- fin debug
for (int i=1;i<=3;i++)
B(i,i) -= VP(1);
////---debug
//cout << "\n debug V_Propres3x3 apres B(i,i) -= VP(1)";
//cout <<"\n B: ";
//B.Affiche();
//// --- fin debug
// on calcul le rang de la matrice
int rang_B = CalculRangMatrice3x3Singuliere(B);
//---debug
//cout << "\n debug V_Propres3x3 ";
//cout <<"\n rangB: "<<rang_B;
//// --- fin debug
switch (rang_B)
{ case 0: // si le rang = 0, la matrice B est uniformément nulle, et on peut choisir n'importe quel base
// on prend la base identité
// cela veut dire aussi que la matrice A initiale était sphérique !! donc que les trois valeurs propres sont identiques
{Vpropre(1)(1)=1.;Vpropre(2)(2)=1.;Vpropre(3)(3)=1.;
#ifdef MISE_AU_POINT
{// en débug on vérifie que le rang est également nulle pour les deux autres valeurs
Mat_pleine B2(A);
for (int i=1;i<=3;i++)
B2(i,i) -= VP(2);
Mat_pleine B3(A);
for (int i=1;i<=3;i++)
B3(i,i) -= VP(3);
if ((CalculRangMatrice3x3Singuliere(B2) != 0) || (CalculRangMatrice3x3Singuliere(B3) != 0))
{cout << "\n ** erreur au niveau des rangs de matrice: rang_B == 0,"
<< " mais rang_B2 = " << CalculRangMatrice3x3Singuliere(B2)
<< " et rang_B3= " << CalculRangMatrice3x3Singuliere(B3)
<< "\n V_Propres3x3(..." << endl;
cas = -1; // on signale l'erreur
break; // et on arrête
};
if (cas != 0)
{cout << "\n ** erreur au niveau des rangs de matrice: rang_B == 0,"
<< " normalement on devrait avoir cas = 0, et en fait on a : cas= " << cas
<< " (pour voir les matrices utiliser un niveau de commentaire > 7) "
<< "\n VP: ";
if (ParaGlob::NiveauImpression() > 7)
{ VP.Affiche();
cout <<"\n A: ";
A.Affiche();
cout <<"\n B: ";
B.Affiche();
cout <<"\n rangB: "<<rang_B ;
cout << "\n cas= "<< cas << " VP= "<<VP;
cout << "\n les val propres: " << VP ;
};
cout << "\n V_Propres3x3(..." << endl;
cas = -1; // on signale l'erreur
break; // et on arrête
};
};
#endif
// vérif pour le cas non débug
if (cas != 0)
{cas = -1; // on signale l'erreur
break; // et on arrête
};
break;
}
case 1: // on n'a qu'une seule direction de libre
// cela veut dire également que l'on a deux valeurs propres identiques
{ // on récupère la première ligne de la matrice qui est la direction libre
Coordonnee3 V(B(1).Coordo()); // c-a-d la première direction libre
// on définit les 2 vecteurs du plan normal
Def_vecteurs_plan(V, Vpropre(2), Vpropre(3));
// et le premier, qui correspond à la première valeur propre (celle qui a servi au calcul de B)
Vpropre(1) = Util::ProdVec_coor(Vpropre(2),Vpropre(3));
#ifdef MISE_AU_POINT
// en débug que l'on a bien deux valeurs propres identiques
if ((cas == 0) || (cas == 1))
{cout << "\n erreur au niveau du rang de matrice: rang_B == 1,"
<< " on devrait donc avoir deux valeurs propres identiques alors que cas = " << cas
<< " (pour voir les matrices utiliser un niveau de commentaire > 7) "
<< "\n VP: ";
if (ParaGlob::NiveauImpression() > 7)
{ VP.Affiche();
cout <<"\n A: ";
A.Affiche();
cout <<"\n B: ";
B.Affiche();
cout <<"\n rangB: "<<rang_B ;
cout << "\n cas= "<< cas << " VP= "<<VP;
cout << "\n les val propres: " << VP ;
};
cout << "\n V_Propres3x3(..." << endl;
cas = -1; // on signale l'erreur
break; // et on arrête
};
#endif
// vérif pour le cas non débug
if ((cas == 0) || (cas == 1))
{cas = -1; // on signale l'erreur
break; // et on arrête
};
break;
}
case 2:
{ // on récupère les deux première lignes de la matrice qui forme le plan normal à la direction principale
Coordonnee3 V1 = B(1).Coordo(); // premier vecteur
Coordonnee3 V2 = B(2).Coordo(); // second vecteur
// on définit le vecteur du plan normal
Vpropre(1) = Util::ProdVec_coor(V1,V2);
Vpropre(1).Normer();
// on s'occupe maintenant de la seconde valeur propre
Mat_pleine B2(A);
for (int i=1;i<=3;i++)
B2(i,i) -= VP(2);
int rang_B2 = CalculRangMatrice3x3Singuliere(B2);
#ifdef MISE_AU_POINT
// rang_B2 ne peut pas être nul
if (rang_B2 == 0)
{cout << "\n erreur au niveau du rang de matrice: rang_B == 2,"
<< " et on a trouve rang_B2= 0 !! "
<< " (pour voir les matrices utiliser un niveau de commentaire > 7) "
<< "\n VP: ";
if (ParaGlob::NiveauImpression() > 7)
{ VP.Affiche();
cout <<"\n A: ";
A.Affiche();
cout <<"\n B: ";
B.Affiche();
cout <<"\n rangB: "<<rang_B << ", rangB2= "<< rang_B2;
cout << "\n cas= "<< cas << " VP= "<<VP;
cout << "\n les val propres: " << VP ;
};
cout << "\n V_Propres3x3(..." << endl;
cas = -1; // on signale l'erreur
break; // et on arrête
};
#endif
// vérif pour le cas non débug
if (rang_B2 == 0)
{cas = -1; // on signale l'erreur
break; // et on arrête
};
if (rang_B2 == 1)
{// cela veut dire que les deux dernières valeurs propres sont identiques
// donc on peut choisir un plan normal au premier vecteur
// produit 2 vecteurs normés
Def_vecteurs_plan(Vpropre(1), Vpropre(2), Vpropre(3));
#ifdef MISE_AU_POINT
// en débug que l'on a bien deux valeurs propres identiques
if ((cas == 0) || (cas == 1))
{cout << "\n erreur au niveau du rang de matrice: rang_B1 == 2 et rang_B2 == 1,"
<< " on devrait donc avoir deux valeurs propres identiques alors que cas = " << cas
<< " (pour voir les matrices utiliser un niveau de commentaire > 7) "
<< "\n VP: ";
if (ParaGlob::NiveauImpression() > 7)
{ VP.Affiche();
cout <<"\n A: ";
A.Affiche();
cout <<"\n B: ";
B.Affiche();
cout <<"\n rangB: "<<rang_B << ", rangB2= "<< rang_B2;
cout << "\n cas= "<< cas << " VP= "<<VP;
cout << "\n les val propres: " << VP ;
};
cout << "\n V_Propres3x3(..." << endl;
cas = -1; // on signale l'erreur
break; // et on arrête
};
#endif
// vérif pour le cas non débug
if ((cas == 0) || (cas == 1))
{cas = -1; // on signale l'erreur
break; // et on arrête
};
}
else
{ // sinon rang_B2 == 2
Coordonnee3 W1 = B2(1).Coordo(); // premier vecteur
Coordonnee3 W2 = B2(2).Coordo(); // second vecteur
// on définit le vecteur du plan normal
Vpropre(2) = Util::ProdVec_coor(W1,W2);
Vpropre(2).Normer();
// puis le dernier vecteur propre: produit de deux vecteurs normés
// donc a priori est normé également par le produit vectoriel
Vpropre(3) = Util::ProdVec_coor(Vpropre(1),Vpropre(2));
#ifdef MISE_AU_POINT
if (cas == 0)
{cout << "\n warning au niveau du rang de matrice: rang_B == 2 et rang_B2 == 2,"
<< " bizarre les trois valeurs propres sont identiques cas = " << cas
<< "\n V_Propres3x3(..." << endl;
if (ParaGlob::NiveauImpression() > 7)
{cout << "\n matrice initiale: ";
A.Affiche();
cout << "\n B: ";
B.Affiche();
cout << "\n B2: ";
B2.Affiche();
cout <<"\n rangB: "<<rang_B << ", rangB2= "<< rang_B2;
cout << "\n cas= "<< cas << " VP= "<<VP;
cout << "\n les val propres: " << VP ;
};
};
#endif
// vérif pour le cas non débug
// if (cas == 0)
// {cas = -1; // on signale l'erreur
// break; // et on arrête
// };
};
break;
}
default:
{cout << "\n erreur au niveau du rang de matrice: rang_B doit etre compris entre 0 et 2 "
<< " ici = " << rang_B
<< "\n V_Propres3x3(..." << endl;
Sortie(1);
};
break;
};
#ifdef MISE_AU_POINT
// en débug que les valeurs propres fonctionnent correctement
// , cas = 1 si 3 val propres différentes (= 3 composantes du vecteur de retour)
// , cas = 0 si les 3 sont identiques (= la première composantes du vecteur de retour),
// , cas = 2 si Vp(1)=Vp(2) ( Vp(1), et Vp(3) dans les 2 premières composantes du retour)
// , cas = 3 si Vp(2)=Vp(3) ( Vp(1), et Vp(2) dans les 2 premières composantes du retour)
{ Vecteur Vp(3);
switch (cas)
{ case 0: Vp(1)=Vp(2)=Vp(3)=VP(1); break;
case 1: Vp = VP.Vect(); break;
case 2: Vp(1)=Vp(2)=VP(1); Vp(3)=VP(3); break;
case 3: Vp(2)=Vp(3)=VP(2); Vp(1)=VP(1); break;
};
for (int i=1;i<=3;i++)
{ Coordonnee3 W6 = A.Prod_mat_vec(Vpropre(i).Vect());
// normalement W6 = Vp(i)*Vpropre(i)
double diff = (W6 - (Vpropre(i) * Vp(i))).Norme();
if (Dabs(diff) > ConstMath::unpeupetit)
{cout << "\n erreur le vecteur (i= " << i <<") propre ";
Vpropre(i).Affiche();
cout << "\n n'est pas un vecteur propre pour la valeur propre: "
<< Vp(i) << " concernant la matrice: ";
A.Affiche();
cout << "\n A*V = "<<W6 << "(Vpropre(i) * Vp(i))= " << (Vpropre(i) * Vp(i))
<< " diff= "<<diff;
cout << "\n cas= "<< cas << " VP= "<<VP;
cout << "\n les val propres: " << Vp ;
cout << "\n V_Propres3x3(..." << endl;
};
};
};
#endif
// retour
return Vpropre;
};
// calcul du rang d'une matrice 3x3 singulière, qui peut être 0, 1 ou 2
// modifie la matrice en conséquence
// en sortie:
// rang : = le rang de la matrice
// rang = 0 ==> veut dire que c'est la matrice nulle (à ConstMath::pasmalpetit près)
// rang = 1 ==> la première ligne de la matrice contient la direction libre, génératrice
// rang = 2 ==> les deux premières ligne de la matrice contiennent les deux directions libres
#ifndef MISE_AU_POINT
inline
#endif
int MathUtil2::CalculRangMatrice3x3Singuliere( Mat_pleine& A)
{
#ifdef MISE_AU_POINT
if ((A.Nb_colonne() != 3)|| (A.Nb_ligne() != 3))
{cout << "erreur de dimension: Nb_colonne()= " << A.Nb_colonne() << ", Nb_ligne()= ";
cout << A.Nb_ligne() << " alors que cette methode ne s'applique qu'aux matrices 3x3 ! "
<< "\n CalculRangMatrice(..." << endl;
Sortie(1);
}
#endif
int rang = 0; // init par défaut
// on récupère la norme maxi de la matrice
int imax,jmax;
double maxMat = A.MaxiValAbs(imax,jmax);
// si maxMat == 0, la matrice est nulle -> rang = 0
if (maxMat <= ConstMath::pasmalpetit)
{ rang = 0; }
else
{// sinon le rang sera au moins égal à 1 et maxMat n'est pas trop petit
double unSurmaxMat = 1./maxMat;
// on permute la première ligne, pour qu'elle contienne le maxi
if (imax != 1)
{// sauvegarde de la ligne du maxi, avec normalisation à 1
Vecteur ligne_du_maxi = unSurmaxMat * A(imax);
A.Ligne_set(imax) = A(1); // permutation
A.Ligne_set(1) = ligne_du_maxi; // """"
};
// maintenant on fait une réduction, c-a-d on transforme la matrice pour qu'il n'y ait que (1,0,0) dans la colonne du maxi
Vecteur col_du_maxi = A(jmax); // récup de la colonne du maxi
A.Ligne_set(2) = A(2) - col_du_maxi(2)*A(1);
A.Ligne_set(3) = A(3) - col_du_maxi(3)*A(1);
// arrivé ici normalement on a A(imax,2) = A(imax,3) = 0.
// on calcul le maxi des composantes de la deuxième et troisième ligne de la matrice
// -- tout d'abord la deuxième ligne
int imax2=2; // init
int jmax2; double maxMat2 = A(2).Max_val_abs(jmax2);
// -- idem troisième ligne
int imax3=3; // init
int jmax3; double maxMat3 = A(3).Max_val_abs(jmax3);
// les maxi finaux sont avec les indices 2 (imax2, jmax2, maxMat2)
if (maxMat3 > maxMat2)
{imax2=imax3;jmax2=jmax3;maxMat2 = maxMat3;
};
// si le maxi est très petit, on n'a qu'une seule direction de libre
if (maxMat2 <= ConstMath::pasmalpetit)
{ rang = 1; }
else
{ // sinon on refait la même manip entre les lignes 2 et 3
double unSurmaxMat2 = 1./maxMat2;
// on permute la dernière ligne, si besoin, pour que la deuxième contienne le maxi
if (imax2 != 2)
{// sauvegarde de la ligne du maxi, avec normalisation à 1
Vecteur ligne_du_maxi = unSurmaxMat2 * A(imax2);
A.Ligne_set(imax2) = A(2); // permutation
A.Ligne_set(2) = ligne_du_maxi; // """"
};
// le rang est 2 et les 2 premières lignes contiennent deux vecteurs indépendants
rang = 2;
}
};
// retour
return rang;
};
// --- cas d'une dimension d'espace == 3
// calcul deux vecteurs perpendiculaires à un troisième, générateurs d'un plan, et orthonormée
// Les deux vecteurs résultats sont quelconques a priori
#ifndef MISE_AU_POINT
inline
#endif
void MathUtil2::Def_vecteurs_plan(const Coordonnee& V1, Coordonnee& V2, Coordonnee& V3)
{ double norme_V1= V1.Norme();
#ifdef MISE_AU_POINT
if (norme_V1 < ConstMath::trespetit)
{cout << "erreur le vecteur V1 a une norme nulle (= " << norme_V1 << ") ";
cout << " le calcul du plan normal est impossible ! "
<< "\n Def_vecteurs_plan(const Coordonnee& V1,..." << endl;
Sortie(1);
};
if ((V1.Dimension() != 3)|| (V2.Dimension() != 3) || (V3.Dimension() != 3))
{cout << "erreur de dimension dans les vecteurs, qui devraient etre de dimension 3: V1.Taille()= " << V1.Dimension()
<< ", V2.Dimension()= " << V2.Dimension() << ", V3.Dimension()= " << V3.Dimension()
<< "\n Def_vecteurs_plan(const Coordonnee& V1,..." << endl;
Sortie(1);
}
#endif
Coordonnee V(V1/norme_V1); // on normalise
// on va chercher l'ordre des composantes de V
int un,deux,trois;
if (Dabs(V(2)) > Dabs(V(1)))
{ if (Dabs(V(2)) > Dabs(V(3)))
{un = 2;
if (Dabs(V(1)) > Dabs(V(3)))
{deux = 1; trois = 3;}
else
{deux = 3; trois = 1;}
}
else
{un = 3; deux= 2; trois = 1;}
}
else
{ if ((Dabs(V(3)) > Dabs(V(1))))
{un = 3; deux = 1; trois = 2; }
else
{if ((Dabs(V(3)) > Dabs(V(2))))
{un = 1; deux = 3; trois = 2;}
else
{un = 1; deux = 2; trois = 3;};
}
};
// on calcule les vecteurs en fonction
double unsurlongue = 1./sqrt(V(un)*V(un) + V(trois)*V(trois));
V2(un) = -V(trois) * unsurlongue;
V2(deux) = 0.;
V2(trois) = V(un) * unsurlongue;
V3(un) = V(deux) * V2(trois);
V3(deux) = V(trois) * V2(un) - V(un) * V2(trois);
V3(trois) = -V(deux) * V2(un);
#ifdef MISE_AU_POINT
if (ParaGlob::NiveauImpression() > 8)
{// on vérifie l'algo de tri
bool erreur = false;
if ((Dabs(V(deux)) > Dabs(V(un))) || (Dabs(V(trois)) > Dabs(V(un)))
|| (Dabs(V(trois)) > Dabs(V(deux)))
)
{cout << "\n *** erreur dans le tri des composantes de V: "
<< "un= "<< un << "deux= "<< deux <<" trois= "<< trois
<< " V= "<< V ;
erreur = true;
};
// on vérifie les normes et les produits scalaires
if ((V.Norme()-1.) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V a une norme " << V.Norme() << " diff de 1 ";
erreur = true;
}
if ((V2.Norme()-1) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V2 a une norme " << V2.Norme() << " diff de 1 ";
erreur = true;
}
if ((V3.Norme()-1) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V3 a une norme " << V3.Norme() << " diff de 1 ";
erreur = true;
}
if (Dabs(V * V2) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V2 n'est pas normal a V1 "
<< "\n V1= " << V1 << " V2= " << V2 ;
erreur = true;
}
if (Dabs(V * V3) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V3 n'est pas normal a V1 "
<< "\n V1= " << V << " V3= " << V3 ;
erreur = true;
}
if (Dabs(V2 * V3) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V3 n'est pas normal a V2 "
<< "\n V3= " << V3 << " V2= " << V2 ;
erreur = true;
}
if (erreur)
{ cout << "\n Def_vecteurs_plan(const Coordonnee& V1,..." << flush;
Sortie(1);
};
};
#endif
/* // calcul original, qui ne fonctionne pas pour le cas V(1) et V(3) nulle ensemble !!
//
if (Dabs(V(1)) >= Dabs(V(3)))
{ double unsurlongue = 1./sqrt(V(1)*V(1) + V(3)*V(3));
// si V(3) est nulle, alors V(1) est forcément non nulle ==>longue est non nul
// si (V(3) est non nulle, alors ==>longue est non nul
V2(1) = -V(3) * unsurlongue;
V2(2) = 0.;
V2(3) = V(1) * unsurlongue;
V3(1) = V(2) * V2(3);
V3(2) = V(3) * V2(1) - V(1) * V2(3);
V3(3) = -V(2) * V2(1);
}
else
{ double unsurlongue = 1./sqrt(V(2)*V(2) + V(3)*V(3));
// ici V(2) prend le rôle de V(1)
V2(1) = 0.;
V2(2) = V(3) * unsurlongue;
V2(3) = -V(2) * unsurlongue;
V3(1) = V(2) * V2(3) - V(3) * V2(2);
V3(2) = -V(1) * V2(3);
V3(3) = V(1) * V2(2);
};
*/
};
// calcul deux vecteurs perpendiculaires à un troisième, générateurs d'un plan, et orthonormée
// Les deux vecteurs résultats sont quelconques a priori
#ifndef MISE_AU_POINT
inline
#endif
void MathUtil2::Def_vecteurs_plan(const CoordonneeB& V1, CoordonneeB& V2, CoordonneeB& V3)
{ double norme_V1= V1.Norme();
#ifdef MISE_AU_POINT
if (norme_V1 < ConstMath::trespetit)
{cout << "erreur le vecteur V1 a une norme nulle (= " << norme_V1 << ") ";
cout << " le calcul du plan normal est impossible ! "
<< "\n Def_vecteurs_plan(const CoordonneeB& V1,..." << endl;
Sortie(1);
};
if ((V1.Dimension() != 3)|| (V2.Dimension() != 3) || (V3.Dimension() != 3))
{cout << "erreur de dimension dans les vecteurs, qui devraient etre de dimension 3: V1.Taille()= " << V1.Dimension()
<< ", V2.Dimension()= " << V2.Dimension() << ", V3.Dimension()= " << V3.Dimension()
<< "\n Def_vecteurs_plan(const CoordonneeB& V1,..." << endl;
Sortie(1);
}
#endif
CoordonneeB V(V1/norme_V1); // on normalise
// on va chercher l'ordre des composantes de V
int un,deux,trois;
if (Dabs(V(2)) > Dabs(V(1)))
{ if (Dabs(V(2)) > Dabs(V(3)))
{un = 2;
if (Dabs(V(1)) > Dabs(V(3)))
{deux = 1; trois = 3;}
else
{deux = 3; trois = 1;}
}
else
{un = 3; deux= 2; trois = 1;}
}
else
{ if ((Dabs(V(3)) > Dabs(V(1))))
{un = 3; deux = 1; trois = 2; }
else
{if ((Dabs(V(3)) > Dabs(V(2))))
{un = 1; deux = 3; trois = 2;}
else
{un = 1; deux = 2; trois = 3;};
}
};
// on calcule les vecteurs en fonction
double unsurlongue = 1./sqrt(V(un)*V(un) + V(trois)*V(trois));
V2(un) = -V(trois) * unsurlongue;
V2(deux) = 0.;
V2(trois) = V(un) * unsurlongue;
V3(un) = V(deux) * V2(trois);
V3(deux) = V(trois) * V2(un) - V(un) * V2(trois);
V3(trois) = -V(deux) * V2(un);
#ifdef MISE_AU_POINT
if (ParaGlob::NiveauImpression() > 8)
{// on vérifie l'algo de tri
bool erreur = false;
if ((Dabs(V(deux)) > Dabs(V(un))) || (Dabs(V(trois)) > Dabs(V(un)))
|| (Dabs(V(trois)) > Dabs(V(deux)))
)
{cout << "\n *** erreur dans le tri des composantes de V: "
<< "un= "<< un << "deux= "<< deux <<" trois= "<< trois
<< " V= "<< V ;
erreur = true;
};
// on vérifie les normes et les produits scalaires
if ((V.Norme()-1.) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V a une norme " << V.Norme() << " diff de 1 ";
erreur = true;
}
if ((V2.Norme()-1.) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V2 a une norme " << V2.Norme() << " diff de 1 ";
erreur = true;
}
if ((V3.Norme()-1.) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V3 a une norme " << V3.Norme() << " diff de 1 ";
erreur = true;
}
if (Dabs(V.Coor() * V2.Coor()) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V2 n'est pas normal a V1 "
<< "\n V1= " << V1 << " V2= " << V2 ;
erreur = true;
}
if (Dabs(V.Coor() * V3.Coor()) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V3 n'est pas normal a V1 "
<< "\n V1= " << V << " V3= " << V3 ;
erreur = true;
}
if (Dabs(V2.Coor() * V3.Coor()) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V3 n'est pas normal a V2 "
<< "\n V3= " << V3 << " V2= " << V2 ;
erreur = true;
}
if (erreur)
{ cout << "\n Def_vecteurs_plan(const CoordonneeB& V1,..." << flush;
Sortie(1);
};
}
#endif
/* // calcul original, qui ne fonctionne pas pour le cas V(1) et V(3) nulle ensemble !!
if (Dabs(V(1)) >= Dabs(V(3)))
{ double unsurlongue = 1./sqrt(V(1)*V(1) + V(3)*V(3));
// si V(3) est nulle, alors V(1) est forcément non nulle ==>longue est non nul
// si (V(3) est non nulle, alors ==>longue est non nul
V2(1) = -V(3) * unsurlongue;
V2(2) = 0.;
V2(3) = V(1) * unsurlongue;
V3(1) = V(2) * V2(3);
V3(2) = V(3) * V2(1) - V(1) * V2(3);
V3(3) = -V(2) * V2(1);
}
else
{ double unsurlongue = 1./sqrt(V(2)*V(2) + V(3)*V(3));
// ici V(2) prend le rôle de V(1)
V2(1) = 0.;
V2(2) = V(3) * unsurlongue;
V2(3) = -V(2) * unsurlongue;
V3(1) = V(2) * V2(3) - V(3) * V2(2);
V3(2) = -V(1) * V2(3);
V3(3) = V(1) * V2(2);
};
*/
};
// calcul deux vecteurs perpendiculaires à un troisième, générateurs d'un plan, et orthonormée
// Les deux vecteurs résultats sont quelconques a priori
#ifndef MISE_AU_POINT
inline
#endif
void MathUtil2::Def_vecteurs_plan(const CoordonneeH& V1, CoordonneeH& V2, CoordonneeH& V3)
{ double norme_V1= V1.Norme();
#ifdef MISE_AU_POINT
if (norme_V1 < ConstMath::trespetit)
{cout << "erreur le vecteur V1 a une norme nulle (= " << norme_V1 << ") ";
cout << " le calcul du plan normal est impossible ! "
<< "\n Def_vecteurs_plan(const CoordonneeH& V1,..." << endl;
Sortie(1);
};
if ((V1.Dimension() != 3)|| (V2.Dimension() != 3) || (V3.Dimension() != 3))
{cout << "erreur de dimension dans les vecteurs, qui devraient etre de dimension 3: V1.Taille()= " << V1.Dimension()
<< ", V2.Dimension()= " << V2.Dimension() << ", V3.Dimension()= " << V3.Dimension()
<< "\n Def_vecteurs_plan(const CoordonneeH& V1,..." << endl;
Sortie(1);
}
#endif
CoordonneeH V(V1/norme_V1); // on normalise
// on va chercher l'ordre des composantes de V
int un,deux,trois;
if (Dabs(V(2)) > Dabs(V(1)))
{ if (Dabs(V(2)) > Dabs(V(3)))
{un = 2;
if (Dabs(V(1)) > Dabs(V(3)))
{deux = 1; trois = 3;}
else
{deux = 3; trois = 1;}
}
else
{un = 3; deux= 2; trois = 1;}
}
else
{ if ((Dabs(V(3)) > Dabs(V(1))))
{un = 3; deux = 1; trois = 2; }
else
{if ((Dabs(V(3)) > Dabs(V(2))))
{un = 1; deux = 3; trois = 2;}
else
{un = 1; deux = 2; trois = 3;};
}
};
// on calcule les vecteurs en fonction
double unsurlongue = 1./sqrt(V(un)*V(un) + V(trois)*V(trois));
V2(un) = -V(trois) * unsurlongue;
V2(deux) = 0.;
V2(trois) = V(un) * unsurlongue;
V3(un) = V(deux) * V2(trois);
V3(deux) = V(trois) * V2(un) - V(un) * V2(trois);
V3(trois) = -V(deux) * V2(un);
#ifdef MISE_AU_POINT
if (ParaGlob::NiveauImpression() > 8)
{// on vérifie l'algo de tri
bool erreur = false;
if ((Dabs(V(deux)) > Dabs(V(un))) || (Dabs(V(trois)) > Dabs(V(un)))
|| (Dabs(V(trois)) > Dabs(V(deux)))
)
{cout << "\n *** erreur dans le tri des composantes de V: "
<< "un= "<< un << "deux= "<< deux <<" trois= "<< trois
<< " V= "<< V ;
erreur = true;
};
// on vérifie les normes et les produits scalaires
if ((V.Norme()-1.) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V a une norme " << V.Norme() << " diff de 1 ";
erreur = true;
}
if ((V2.Norme()-1.) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V2 a une norme " << V2.Norme() << " diff de 1 ";
erreur = true;
}
if ((V3.Norme()-1.) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V3 a une norme " << V3.Norme() << " diff de 1 ";
erreur = true;
}
if (Dabs(V.Coor() * V2.Coor()) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V2 n'est pas normal a V1 "
<< "\n V1= " << V1 << " V2= " << V2 ;
erreur = true;
}
if (Dabs(V.Coor() * V3.Coor()) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V3 n'est pas normal a V1 "
<< "\n V1= " << V << " V3= " << V3 ;
erreur = true;
}
if (Dabs(V2.Coor() * V3.Coor()) > ConstMath::petit)
{cout << "erreur le nouveau vecteur V3 n'est pas normal a V2 "
<< "\n V3= " << V3 << " V2= " << V2 ;
erreur = true;
}
if (erreur)
{ cout << "\n Def_vecteurs_plan(const CoordonneeH& V1,..." << flush;
Sortie(1);
};
}
#endif
/* // calcul original, qui ne fonctionne pas pour le cas V(1) et V(3) nulle ensemble !!
if (Dabs(V(1)) >= Dabs(V(3)))
{ double unsurlongue = 1./sqrt(V(1)*V(1) + V(3)*V(3));
// si V(3) est nulle, alors V(1) est forcément non nulle ==>longue est non nul
// si (V(3) est non nulle, alors ==>longue est non nul
V2(1) = -V(3) * unsurlongue;
V2(2) = 0.;
V2(3) = V(1) * unsurlongue;
V3(1) = V(2) * V2(3);
V3(2) = V(3) * V2(1) - V(1) * V2(3);
V3(3) = -V(2) * V2(1);
}
else
{ double unsurlongue = 1./sqrt(V(2)*V(2) + V(3)*V(3));
// ici V(2) prend le rôle de V(1)
V2(1) = 0.;
V2(2) = V(3) * unsurlongue;
V2(3) = -V(2) * unsurlongue;
V3(1) = V(2) * V2(3) - V(3) * V2(2);
V3(2) = -V(1) * V2(3);
V3(3) = V(1) * V2(2);
};
*/
};
// --- cas d'une dimension d'espace == 2
// calcul d'un vecteur perpendiculaire à un premier, et orthonormée
#ifndef MISE_AU_POINT
inline
#endif
void MathUtil2::Def_vecteurs_plan(const Coordonnee& V1, Coordonnee& V2)
{ double norme_V1= V1.Norme();
#ifdef MISE_AU_POINT
if (norme_V1 < ConstMath::trespetit)
{cout << "erreur le vecteur V1 a une norme nulle (= " << norme_V1 << ") ";
cout << " le calcul du plan normal est impossible ! "
<< "\n Def_vecteurs_plan(const Coordonnee& V1,..." << endl;
Sortie(1);
};
if ((V1.Dimension() != 2)|| (V2.Dimension() != 2) )
{cout << "erreur de dimension dans les vecteurs, qui devraient etre de dimension 2: V1.Taille()= " << V1.Dimension()
<< ", V2.Dimension()= " << V2.Dimension()
<< "\n Def_vecteurs_plan(const Coordonnee& V1,..." << endl;
Sortie(1);
}
#endif
Coordonnee V(V1/norme_V1); // on normalise
// le vecteur V2 est normale à V
// et V,V2 -> sens direct (inverse de l'horloge)
V2(1) = - V1(2);
V2(2) = V1(1);
};
// calcul d'un vecteur perpendiculaire à un premier, et orthonormée
#ifndef MISE_AU_POINT
inline
#endif
void MathUtil2::Def_vecteurs_plan(const CoordonneeB& V1, CoordonneeB& V2)
{ double norme_V1= V1.Norme();
#ifdef MISE_AU_POINT
if (norme_V1 < ConstMath::trespetit)
{cout << "erreur le vecteur V1 a une norme nulle (= " << norme_V1 << ") ";
cout << " le calcul du plan normal est impossible ! "
<< "\n Def_vecteurs_plan(const Coordonnee& V1,..." << endl;
Sortie(1);
};
if ((V1.Dimension() != 2)|| (V2.Dimension() != 2) )
{cout << "erreur de dimension dans les vecteurs, qui devraient etre de dimension 2: V1.Taille()= " << V1.Dimension()
<< ", V2.Dimension()= " << V2.Dimension()
<< "\n Def_vecteurs_plan(const Coordonnee& V1,..." << endl;
Sortie(1);
}
#endif
CoordonneeB V(V1/norme_V1); // on normalise
// le vecteur V2 est normale à V
// et V,V2 -> sens direct (inverse de l'horloge)
V2(1) = - V1(2);
V2(2) = V1(1);
};
// calcul d'un vecteur perpendiculaire à un premier, et orthonormée
#ifndef MISE_AU_POINT
inline
#endif
void MathUtil2::Def_vecteurs_plan(const CoordonneeH& V1, CoordonneeH& V2)
{ double norme_V1= V1.Norme();
#ifdef MISE_AU_POINT
if (norme_V1 < ConstMath::trespetit)
{cout << "erreur le vecteur V1 a une norme nulle (= " << norme_V1 << ") ";
cout << " le calcul du plan normal est impossible ! "
<< "\n Def_vecteurs_plan(const Coordonnee& V1,..." << endl;
Sortie(1);
};
if ((V1.Dimension() != 2)|| (V2.Dimension() != 2) )
{cout << "erreur de dimension dans les vecteurs, qui devraient etre de dimension 2: V1.Taille()= " << V1.Dimension()
<< ", V2.Dimension()= " << V2.Dimension()
<< "\n Def_vecteurs_plan(const Coordonnee& V1,..." << endl;
Sortie(1);
}
#endif
CoordonneeH V(V1/norme_V1); // on normalise
// le vecteur V2 est normale à V
// et V,V2 -> sens direct (inverse de l'horloge)
V2(1) = - V1(2);
V2(2) = V1(1);
};
//// calcul deux vecteurs perpendiculaires à un troisième, générateurs d'un plan, et orthonormées
//// Les deux vecteurs résultats sont quelconques a priori
//// calcul également la variation des vecteurs résultats
//// en entrée: V1 et d_Vi(1)(ddl) : vecteur donné et sa variation
//// en sortie: V2 et V3, d_Vi(2)(ddl) et d_Vi(3)(ddl) : vecteurs résultats et leurs variations
//#ifndef MISE_AU_POINT
// inline
//#endif
//void Def_vecteurs_plan_et_variation(const CoordonneeB& V1, CoordonneeB& V2, CoordonneeB& V3
// ,Tableau <BaseB>& d_Vi)
// { double norme_V1= V1.Norme();
// #ifdef MISE_AU_POINT
// if (norme_V1 < ConstMath::trespetit)
// {cout << "erreur le vecteur V1 a une norme nulle (= " << norme_V1 << ") ";
// cout << " le calcul du plan normal est impossible ! "
// << "\n Def_vecteurs_plan_et_variation(const CoordonneeB& V1,..." << endl;
// Sortie(1);
// };
// if ((V1.Dimension() != 3)|| (V2.Dimension() != 3) || (V3.Dimension() != 3))
// {cout << "erreur de dimension dans les vecteurs, qui devraient etre de dimension 3: V1.Taille()= " << V1.Dimension()
// << ", V2.Dimension()= " << V2.Dimension() << ", V3.Dimension()= " << V3.Dimension()
// << "\n Def_vecteurs_plan_et_variation(const CoordonneeB& V1,..." << endl;
// Sortie(1);
// }
// #endif
// CoordonneeB V(V1/norme_V1); // on normalise
// //
// if (Dabs(V(1)) >= Dabs(V(3)))
// { double unsurlongue = 1./sqrt(V(1)*V(1) + V(3)*V(3));
// // si V(3) est nulle, alors V(1) est forcément non nulle ==>longue est non nul
// // si (V(3) est non nulle, alors ==>longue est non nul
// V2(1) = -V(3) * unsurlongue;
// V2(2) = 0.;
// V2(3) = V(1) * unsurlongue;
//
// V3(1) = V(2) * V2(3);
// V3(2) = V(3) * V2(1) - V(1) * V2(3);
// V3(3) = -V(2) * V2(1);
//
// // et les variations
// int taille = d_Vi.Taille();
// for (int i=1; i<= taille; i++)
// { BaseB & d_Vi_i = d_Vi(i); // pour simplifier
// CoordonneeB & d_V1 = d_Vi_i(1); // "
// double d_denom = 2. * (d_V1(1) * V(1) + d_V1(3) * V(3));
// double d_unsurlongue =
// 1./sqrt(V(1)*V(1) + V(3)*V(3));
//
// CoordonneeB & d_V2 = d_Vi_i(2); // "
//
//
// };
// }
// else
// { double unsurlongue = 1./sqrt(V(2)*V(2) + V(3)*V(3));
// // ici V(2) prend le rôle de V(1)
// V2(1) = 0.;
// V2(2) = V(3) * unsurlongue;
// V2(3) = -V(2) * unsurlongue;
//
// V3(1) = V(2) * V2(3) - V(3) * V2(2);
// V3(2) = -V(1) * V2(3);
// V3(3) = V(1) * V2(2);
// };
// };
//
// calcul d'un changement de base: ceci n'est pas fait dans la classe Coordonnee car il faut y adjoindre
// la classe Mat_pleine qui intègre beaucoup de chose, du coup la classe Coordonnee deviendrait une usine
// changement de base (cf. théorie) : la matrice beta est telle que:
// gpB(i) = beta(i,j) * gB(j) <==> gp_i = beta_i^j * g_j
// et la matrice gamma telle que:
// gamma(i,j) represente les coordonnees de la nouvelle base duale gpH dans l'ancienne gH
// gpH(i) = gamma(i,j) * gH(j), i indice de ligne, j indice de colonne
// c-a-d= gp^i = gamma^i_j * g^j
// rappel des différentes relations entre beta et gamma
// [beta]^{-1} = [gamma]^T ; [beta]^{-1T} = [gamma]
// [beta] = [gamma]^{-1T} ; [beta]^{T} = [gamma]^{-1}
// changement de base pour de une fois covariant:
// [Ap_k] = [beta] * [A_i]
#ifndef MISE_AU_POINT
inline
#endif
void MathUtil2::ChBase(const CoordonneeB& A_B,const Mat_pleine& beta,CoordonneeB& Ap_B)
{ int dim = A_B.Dimension();
#ifdef MISE_AU_POINT
if ((beta.Nb_ligne() != dim) || (beta.Nb_colonne() != dim))
{ cout << "\nErreur : matrice beta n'a pas les memes dimensions beta.Nb_ligne(): "
<< beta.Nb_ligne() <<" et beta.Nb_colonne(): " << beta.Nb_colonne()
<< " que le vecteur de dim: "
<< dim << " ! le changement de base n'est pas possible ! \n";
cout << "ChBase(const CoordonneeB& A_B,const Mat_pleine& beta,CoordonneeB& Ap_B) \n";
Sortie(1);
};
#endif
Vecteur inter = beta.Prod_mat_vec(A_B.Vect());
for (int i=1; i<= dim;i++)
Ap_B(i) = inter(i);
};
// changement de base pour de une fois contravariant:
// [Ap^k] = [gamma] * [A^i]
#ifndef MISE_AU_POINT
inline
#endif
void MathUtil2::ChBase(const CoordonneeH& A_H,const Mat_pleine& gamma,CoordonneeH& Ap_H)
{ int dim = A_H.Dimension();
#ifdef MISE_AU_POINT
if ((gamma.Nb_ligne() != dim) || (gamma.Nb_colonne() != dim))
{ cout << "\nErreur : matrice gamma n'a pas les memes dimensions gamma.Nb_ligne(): "
<< gamma.Nb_ligne() <<" et gamma.Nb_colonne(): " << gamma.Nb_colonne()
<< " que le vecteur de dim: "
<< dim << " ! le changement de base n'est pas possible ! \n";
cout << "ChBase(const CoordonneeH& A_H,const Mat_pleine& gamma,CoordonneeH& Ap_H) \n";
Sortie(1);
};
#endif
Vecteur inter = gamma.Prod_mat_vec(A_H.Vect());
////-------- debug -------
//cout << "\n debug MathUtil2::ChBase( ...";
//cout << "\n A_H initiale: "<< A_H;
//cout << "\n gamma: "; gamma.Affiche();
//cout << "\n gamma * A_H " << inter << flush;
////-------- fin debug ---------
for (int i=1; i<= dim;i++)
Ap_H(i) = inter(i);
};
#endif