Herezh_dev/Resolin/Matrices/Mat_abstraite.cc
2023-05-03 17:23:49 +02:00

569 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 "Mat_abstraite.h"
#include "ConstMath.h"
#include <math.h>
#include "MathUtil.h"
#include <iomanip>
#include "CharUtil.h"
#include "ParaGlob.h"
// partie relative à la résolution
#include "vecdefs_GR.h" // modif GR
//#include "mvblasd.h" // MV_Vector level 1 BLAS
#include "diagpre_double_GR.h" // Diagonal preconditioner
#include "icpre_double_GR.h" // preconditioner cholesky incomplet
#include "ilupre_double_GR.h" // preconditioner ILU
#include "cgs.h" // IML++ CGS template
#include "cg.h" // IML++ CG template
#include "bicg.h" // IML++ BiCG template
#include "cheby.h" // IML++ Cheby template
#include "bicgstab.h" // IML++ BiCGSTAB template
#include "gmres.h" // IML++ GMRES template
#include "ir.h" // IML++ IR template
#include "qmr.h" // IML++ QMR template
// grandeur par défaut déclarées avant car on s'en sert dans les passages de paramètres
const double Mat_abstraite::tol_defaut = 1.E-7; // tolérance sur le résidu dans les méthodes itératives
const int Mat_abstraite::maxit_defaut = 150 ; // maximum d'itération dans les méthodes itératives
const int Mat_abstraite::restart_defaut = 32;// maximum de restart itération (nb de vecteur sauvegardé)
//calcul des valeurs propres par la méthode des puissances itérées
// inverses.
// --> Utilisable que pour des matrices carrées
// Intéressant lorsque l'on veut les plus petites
// valeurs propres, en nombre restreind car seule les premières valeurs
// sont calculées avec une précision convenable. On obtiend également
// les vecteurs propres correspondant.
// résolution de : ((*this) - lambda KG) X = 0
// en entrée : KG, VP dont la dimension est défini et fourni le nombre
// de valeurs propres que l'on veut calculer
// On considère que les conditions limites sont déjà appliquées à (*this) et KG.
// c'est à dire des 1 sur la diagonale pour (*this) et des 0 sur la diagonale pour KG
// en sortie : VP contiend les valeurs propres et vecteurs propres
Tableau <VeurPropre> * Mat_abstraite::V_Propres(Mat_abstraite& KG,Tableau <VeurPropre> & VP )
{ // la méthode est décrite dans Touzot/Dhatt seconde édition p 393-395
#ifdef MISE_AU_POINT
// vérification des entrées
// 1) (*this) et KG sont carrées et identiques en taille
if ( (Nb_ligne() != KG.Nb_ligne()) || (Nb_colonne() != KG.Nb_colonne()) ||
(Nb_ligne() != Nb_colonne()))
{ cout << "\n ***** Erreur : la taille des matrices a traiter n\'est pas correcte !\n";
cout << "\n les matrices doivent être carrés !! \n";
cout << "Mat_abstraite::V_Propres(... \n";
cout << " KG CxL " << KG.Nb_colonne() << " x " << KG.Nb_ligne();
cout << " (*this) CxL : " << (Nb_colonne()) << " x " << (Nb_ligne()) << "\n";
Sortie(1);
};
#endif
// stockage inter
Vecteur Vinter(Nb_colonne());
bool sorIter ; // gestion de la sortie de la boucle sur les valeurs propres
// on boucle sur le nombre de valeur propre demandé
for (int il=1; il<= VP.Taille(); il++)
{ sorIter = true; // tout va bien à priori
// choix d'un vecteur initial
Vecteur V(Nb_colonne(),1.);
// calcul d'un décalage éventuelle du aux vecteurs déjà calculés
Vecteur Ybarre = KG.Prod_mat_vec(V);
for (int ill=1; ill< il; ill++)
V = V - (VP(ill).Pv() * Ybarre) * VP(ill).Pv();
// calcul initial de la sollicitation
Vecteur F = KG.Prod_mat_vec(V);
// initialisation en taille (uniquement) de la sollicitation intermédiaire
Vecteur Fbarre = F;
// des scalaires intermédiaires
double d,lambda = 0.;
double lambdaPrec = ConstMath::grand; // initialisation à une valeur grande
int maxiBoucle = 500; // maxi de boucle d'itération inverse
int nbBoucle = 0; // indice courant de nombre de boucle
while (Abs(lambda - lambdaPrec) > ConstMath::petit*MaX(Abs(lambda),Abs(lambdaPrec)))
{ // a chaque itération
nbBoucle++; // incrémentation du nombre de boucle
if (nbBoucle > maxiBoucle)
{cout << " \n PB dans le calcul de la valeur propre nb = "<< il;
cout << " Mat_abstraite::V_Propres(.. \n ";
cout << " lambda = "<< lambda;
sorIter = false; // pour l'arrêt de la boucle globale
break; // sortie des itérations
};
// sauvegarde de la raideur
Mat_abstraite * KSS = NouvelElement();
V = KSS->Resol_syst(F); // résolution
// calcul d'un décalage éventuelle du aux vecteurs déjà calculés
Ybarre = KG.Prod_mat_vec(V);
for (int ill=1; ill< il; ill++)
V = V - (VP(ill).Pv() * Ybarre) * VP(ill).Pv();
Fbarre = KG.Prod_mat_vec(V); // sollicitation intermédiaire
d = V * Fbarre; // facteur d
lambdaPrec = lambda; // sauvegarde
lambda = (V * F) / d; // approximation de lambda
F = Fbarre / sqrt(d); // nouvelle sollicitation
};
if (!sorIter) break; // sortie au cas ou l'on n'a pas convergé
// calcul du vecteur propre normalisé
Vinter = V / sqrt(d);
VP(il) = VeurPropre(lambda,Vinter); // sauvegarde
};
// retour du tableau
return &VP;
};
// définie la surcharge de multiplication d'une matrice par un MV_Vector
MV_Vector<double> Mat_abstraite::operator * (const MV_Vector<double> & vec) const
{ // tout d'abord on construit un vecteur en place du MV_Vector
const MV_Vecteur vecte(vec);
const Vecteur & vecv = (Vecteur &) vecte;
// multiplication et retour
// MV_Vecteur resulta = (Prod_mat_vec(vecv)).MV_vecteur_double();
//// MV_Vector<double> resulta = (Prod_mat_vec(vecv)).MV_vecteur_double();
// autre solution
MV_Vecteur resulta(vec);
Vecteur & vecretour = (Vecteur &) resulta;
Prod_mat_vec(vecv,vecretour);
return resulta;
};
// multiplication matrice transposée fois vecteur ce qui est équivalent à
// la transposée du résultat de : multiplication vecteur transposé fois matrice
MV_Vector<double> Mat_abstraite::trans_mult(const MV_Vector<double> &vec) const
{ // tout d'abord on construit un vecteur en place du MV_Vector
const MV_Vecteur vecte(vec);
const Vecteur & vecv = (Vecteur &) vecte;
// multiplication et retour
// MV_Vecteur resulta = (Prod_vec_mat (vecv)).MV_vecteur_double();
MV_Vector<double> resulta = (Prod_vec_mat (vecv)).MV_vecteur_double();
return resulta;
};
// -------------------------- Méthodes protégées : ----------------------------
// Resolution du systeme Ax=b , méthodes générales pouvant être appelées par les classes dérivée
// en entrée : b comme second membre
// en sortie : sol de même dimension que sol
void Mat_abstraite::Resolution_syst
(const Vecteur &b, Vecteur &sol,const double &tole, const int maxi, const int rest) const
{ // récup de l'homologue en vecteur_double
// const VECTOR_double bb_double(b.MV_vecteur_double());
const VECTOR_double * bb_double_pt = b.Nouveau_MV_Vector_double_const();
const VECTOR_double& bb_double = (*bb_double_pt);
// const VECTOR_double bb_double(b.Pointeur_vect(),b.Taille(),MV_Vector_::ref_type(1));
// MV_Vector<TYPE>::MV_Vector(TYPE* d, int n, MV_Vector_::ref_type i) : // modif GR
// idem pour la solution
// VECTOR_double sol_double (sol.MV_vecteur_double());
VECTOR_double* sol_double_pt = sol.Nouveau_MV_Vector_double();
VECTOR_double& sol_double = (*sol_double_pt);
int result,maxit = maxi,restart = rest; // Maximum iterations
double tol = tole;
// choix du préconditionnement
Pre_cond_double * D = NULL; // pointeur non affecté avec le choix
switch (type_preconditionnement)
{ case DIAGONAL :
// cas diagonal
D = new DiagPreconditioner_double (*this);
break;
case ICP :
// cas de la factorisation de cholesky incomplete
D = new ICPreconditioner_double (*this);
break;
case ILU :
// cas de la factorisation LU
D = new ILUPreconditioner_double (*this);
break;
default :
cout << "\nErreur : valeur incorrecte du type de preconditionnement !\n"
<< " = " << type_preconditionnement ;
cout << "\n Mat_abstraite::Resolution_syst (... ) \n";
Sortie(1);
};
if (ParaGlob::NiveauImpression() > 2) cout << "\n Resolution ";
// choix du type de résolution
switch (type_resolution)
{ case BI_CONJUG :
// biconjugué
{result = BiCG(*this, sol_double,bb_double, *D, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "BiCG flag = " << result << endl;
break;
}
case BI_CONJUG_STAB:
// biconjugué stabilisé
{result = BiCGSTAB( *this, sol_double, bb_double, *D, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "BiCGSTAB flag = " << result << endl;
break;
}
case CONJUG_GRAD :
// conjugué gradient
{result = CG(*this, sol_double, bb_double, *D, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "CG flag = " << result << endl;
break;
}
case CONJUG_GRAD_SQUARE :
// conjugué square method
{result = CGS(*this, sol_double, bb_double, *D, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "CGS flag = " << result << endl;
break;
}
case CHEBYSHEV :
// itération de chebyshev
{if (ParaGlob::NiveauImpression() > 2)
cout << "\n attention utilisation de bornes bidon pour le rayon spectrale"
<< " dans les itération de chebyshev (résolution de système matricielle)";
double mineig = .01; // n'importe quoi
double maxeig = 3; // devra être modifié pour avoir un résultat correcte
result = CHEBY(*this, sol_double, bb_double, *D, maxit, tol, mineig, maxeig);
if (ParaGlob::NiveauImpression() > 2)
cout << "CHEBY flag = " << result << endl;
break;
}
case GENE_MINI_RESIDUAL :
// résidu minimal généralisé
{MATRIX_double Htt(restart+1, restart, 0.0); // storage for upper Hessenberg H
result = GMRES(*this, sol_double, bb_double, *D, Htt,restart, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "GMRES flag = " << result << endl;
break;
}
case ITERATION_RICHARSON :
// itération de richarson
{result = IR( *this, sol_double, bb_double, *D, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "IR flag = " << result << endl;
break;
}
case QUASI_MINI_RESIDUAL :
// résidu quasi minimal
{result = QMR( *this, sol_double, bb_double, *D, *D, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "QMR flag = " << result << endl;
break;
}
default :
cout << "\nErreur : valeur incorrecte du type de résolution !\n"
<< " = " << type_resolution ;
cout << "\n Mat_abstraite::Resolution_syst (... ) \n";
Sortie(1);
};
if (ParaGlob::NiveauImpression() > 2)
{ cout << "iterations performed: " << maxit << endl;
cout << "tolerance achieved : " << tol << endl;
};
// on recopie le résultat
sol = sol_double;
// on efface le préconditionnement
if (D != NULL) delete D;
// on efface les vecteurs de travail
delete bb_double_pt; delete sol_double_pt;
};
// idem avec en entrée un tableau de vecteur second membre et
// en sortie un tableau de vecteurs solution
void Mat_abstraite::Resolution_syst
(const Tableau <Vecteur>& b, Tableau <Vecteur> &sol,const double &tole
, const int maxi, const int rest) const
{
// choix du préconditionnement
Pre_cond_double * D = NULL; // pointeur non affecté avec le choix
switch (type_preconditionnement)
{ case DIAGONAL :
// cas diagonal
D = new DiagPreconditioner_double (*this);
break;
case ICP :
// cas de la factorisation de cholesky incomplete
D = new ICPreconditioner_double (*this);
break;
case ILU :
// cas de la factorisation LU
D = new ILUPreconditioner_double (*this);
break;
default :
cout << "\nErreur : valeur incorrecte du type de preconditionnement !\n"
<< " = " << type_preconditionnement ;
cout << "\n Mat_abstraite::Resolution_syst (... ) \n";
Sortie(1);
};
// on boucle sur le nombre de second membre
int nbsecondmembre = b.Taille();
for (int ise=1; ise<= nbsecondmembre;ise++)
{
// récup de l'homologue en vecteur_double
const VECTOR_double bb_double ((b(ise)).MV_vecteur_double());
// idem pour la solution
VECTOR_double sol_double ((sol(ise)).MV_vecteur_double());
int result,maxit = maxi,restart = rest; // Maximum iterations
double tol = tole;
if (ParaGlob::NiveauImpression() > 2)
cout << "\n resolution du second membre nb = " << ise << " ";
// choix du type de résolution
switch (type_resolution)
{ case BI_CONJUG :
// biconjugué
{result = BiCG(*this, sol_double,bb_double, *D, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "BiCG flag = " << result << endl;
break;
}
case BI_CONJUG_STAB:
// biconjugué stabilisé
{result = BiCGSTAB( *this, sol_double, bb_double, *D, maxit, tol);
cout << "BiCGSTAB flag = " << result << endl;
break;
}
case CONJUG_GRAD :
// conjugué gradient
{result = CG(*this, sol_double, bb_double, *D, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "CG flag = " << result << endl;
break;
}
case CONJUG_GRAD_SQUARE :
// conjugué square method
{result = CGS(*this, sol_double, bb_double, *D, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "CGS flag = " << result << endl;
break;
}
case CHEBYSHEV :
// itération de chebyshev
{if (ParaGlob::NiveauImpression() > 2)
cout << "\n attention utilisation de bornes bidon pour le rayon spectrale"
<< " dans les iteration de chebyshev (resolution de systeme matricielle)";
double mineig = .01; // n'importe quoi
double maxeig = 3; // devra être modifié pour avoir un résultat correcte
result = CHEBY(*this, sol_double, bb_double, *D, maxit, tol, mineig, maxeig);
if (ParaGlob::NiveauImpression() > 2)
cout << "CHEBY flag = " << result << endl;
break;
}
case GENE_MINI_RESIDUAL :
// résidu minimal généralisé
{MATRIX_double Htt(restart+1, restart, 0.0); // storage for upper Hessenberg H
result = GMRES(*this, sol_double, bb_double, *D, Htt,restart, maxit, tol);
cout << "GMRES flag = " << result << endl;
break;
}
case ITERATION_RICHARSON :
// itération de richarson
{result = IR( *this, sol_double, bb_double, *D, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "IR flag = " << result << endl;
break;
}
case QUASI_MINI_RESIDUAL :
// résidu quasi minimal
{result = QMR( *this, sol_double, bb_double, *D, *D, maxit, tol);
if (ParaGlob::NiveauImpression() > 2)
cout << "QMR flag = " << result << endl;
break;
}
default :
cout << "\nErreur : valeur incorrecte du type de résolution !\n"
<< " = " << type_resolution ;
cout << "\n Mat_abstraite::Resolution_syst (... ) \n";
Sortie(1);
};
if (ParaGlob::NiveauImpression() > 2)
{cout << "iterations performed: " << maxit << endl;
cout << "tolerance achieved : " << tol << endl;
};
// on recopie le résultat
sol(ise) = sol_double;
};
// on efface le préconditionnement
if (D != NULL) delete D;
};
// Affiche une partie de la matrice (util pour le debug)
// MiN_ et max_ sont les bornes de la sous_matrice
// pas_ indique le pas en i et j pour les indices
void Mat_abstraite::Affiche1(int min_i,int max_i,int pas_i,int min_j,int max_j,int pas_j)
const
{ cout << "\n affichage d une matrice " << '\n';
int i,j;
cout << " ";
for (j=min_j;j<=max_j;j+=pas_j) cout << "col " << setw(5) << j << " " ;
cout << '\n';
for (i=min_i;i<=max_i;i+=pas_i)
{ cout << "lig " << setw(4) << i << " * ";
for (j=min_j;j<=max_j;j+=pas_j)
cout << setw(11) << setprecision(7) << (*this)(i,j) << " ";
cout << '\n';
};
cout << endl;
};
// Affiche une partie de la matrice (util pour le debug)
// MiN_ et max_ sont les bornes de la sous_matrice
// pas_ indique le pas en i et j pour les indices
// avec en plus le nb de digit = nd
void Mat_abstraite::Affiche2
(int min_i,int max_i,int pas_i,int min_j,int max_j,int pas_j,int nd)
const
{ cout << "\n affichage d une matrice " << '\n';
if (nd < 7)
{ cout << " dimension des colonnes trop faible (<7 ! ) " << endl;
return;
};
int i,j;
SortBlanc(16);
for (j=min_j;j<=max_j;j+=pas_j)
{ cout << "col " << setw(2) << j ;
SortBlanc(nd-6);
};
cout << '\n';
for (i=min_i;i<=max_i;i+=pas_i)
{ cout << "li " << setw(3) << i << "* ";
for (j=min_j;j<=max_j;j+=pas_j)
cout << setw(nd) << setprecision(7) << (*this)(i,j);
cout << '\n';
};
cout << endl;
};
// affichage à l'écran après une demande interactive de la matrice
// entete: une chaine explicative, a afficher en entête
void Mat_abstraite::Affichage_ecran(string entete) const
{// affichage éventuelle de la matrice
cout << "\n --- " << entete << " -----";
string rep;
cout << "\n voulez-vous afficher la matrice ? (o ou n) ";
rep = lect_return_defaut(false,"n");
if (rep == "o")
{cout << "\n parametre d'affichage par defaut (toute la matrice) ? (o ou n) " ;
rep = lect_return_defaut(false,"n");
int min_i,max_i,pas_i,min_j,max_j,pas_j,nd;
if (rep =="o")
{ min_i=1; max_i=this->Nb_ligne(); min_j=1; max_j=this->Nb_ligne();
pas_i=1;pas_j=1; nd=14;
}
else
{try
{ cout << "\n a partir de quel numero de ligne ? (1) "; min_i=(int)lect_double();
cout << " jusqu'a quel numero de ligne ? ("<<this->Nb_ligne()<<") "; max_i=(int)lect_double();
cout << " par pas de ? (1) "; pas_i=(int)lect_double();
cout << "\n a partir de quel numero de colonne ? (1) "; min_j=(int)lect_double();
cout << " jusqu'a quel numero de colonne ? ("<<this->Nb_ligne()<<") "; max_j=(int)lect_double();
cout << " par pas de ? (1) "; pas_j=(int)lect_double();
cout << "\n nombre de diggit ? (14) "; nd=(int)lect_double();
}
catch (ErrSortieFinale)
// cas d'une direction voulue vers la sortie
// on relance l'interuption pour le niveau supérieur
{ ErrSortieFinale toto;
throw (toto);
}
catch (...)
{ cout << "\n Erreur sur l'entree des donnee !!, on prend les valeurs par defaut ";
min_i=1; max_i=this->Nb_ligne(); min_j=1; max_j=this->Nb_ligne();
pas_i=1;pas_j=1; nd=14;
}
};
this->Affiche2(min_i,max_i,pas_i,min_j,max_j,pas_j,nd);
cout << "\n entrez une lettre pour continuer ? ";
rep = lect_chaine();
}; //-- fin du choix visu ou pas de la matrice
};
// calcul, récupération et affichage éventuelle
// des mini, maxi, et en valeur absolue la moyenne de la diagonale de la matrice
// en retour: le min, le max et la moyenne en valeur absolue
Coordonnee3 Mat_abstraite::MinMaxMoy(bool affiche) const
{ Coordonnee3 retour;
// calcul des grandeurs
double min=ConstMath::tresgrand,max=-ConstMath::tresgrand,moy=0.;
int nbl = this->Nb_ligne();
if (affiche)
{for (int i=1;i<=nbl;i++)
{double dia = (*this)(i,i);
min = MiN(min, dia);
max = MaX(max, dia);
moy += Dabs(dia);
};
moy /= nbl;
// affichage
cout << "\n matrice masse: valeurs diagonales: min= "<<min <<", max= "<<max<<", moy des |(i,i)| "<<moy<<flush;
}
else
{for (int i=1;i<=nbl;i++)
{double dia = (*this)(i,i);
min = MiN(min, dia);
max = MaX(max, dia);
moy += Dabs(dia);
};
moy /= nbl;
};
retour(1) = min; retour(2) = max; retour(3)=moy;
return retour;
};
// limite la valeur mini de la diagonale: si un terme de la diagonale
// est inférieure à la limite passée en argument (seuil_bas), d'une manière arbitraire
// le terme à mis à une valeur égale au paramètre passé en paramètre val_a_imposer
// s'il y a eu changement retour de true
bool Mat_abstraite::Limitation_min_diag(double seuil_bas, double val_a_imposer)
{bool modif = false;
int nbl = this->Nb_ligne();
for (int i=1;i<=nbl;i++)
{double dia = (*this)(i,i);
if (dia < seuil_bas)
{ modif = true;
(*this)(i,i) = val_a_imposer;
};
};
return modif;
};