TrioCFD 1.9.8
TrioCFD documentation
Loading...
Searching...
No Matches
Solv_Gmres.cpp
1/****************************************************************************
2* Copyright (c) 2026, CEA
3* All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9*
10* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
11* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
12* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13*
14*****************************************************************************/
15
16#include <Solv_Gmres.h>
17#include <Matrice_Morse_Sym.h>
18#include <Matrice_Bloc.h>
19#include <Motcle.h>
20#include <Param.h>
21
22Implemente_instanciable_sans_constructeur(Solv_Gmres,"Solv_Gmres",solv_iteratif);
23// XD solv_gmres solveur_sys_base gmres BRACE Preconditioned GMRES.
24// XD attr diag rien diag OPT Keyword to precondition with the diagonal
25// XD attr seuil floattant seuil OPT Value of the final residue. The solver ceases iterations when the Euclidean residue
26// XD_CONT standard ||Ax-B|| is less than this value. default value 1e-12.
27// XD attr impr rien impr OPT Keyword which is used to request display of the Euclidean residue standard each time this
28// XD_CONT iterates
29// XD attr save_matrice|save_matrix entier save_matrice OPT To save the matrix in a file.
30// XD attr quiet rien quiet OPT To not displaying any outputs of the solver.
31// XD attr nb_it_max entier nb_it_max OPT Keyword to set the maximum iterations number for the solver.
32// XD attr controle_residu entier controle_residu OPT Keyword of Boolean type (by default 0). If set to 1, check the
33// XD_CONT convergence after solve
34
36{
37 seuil_ = 1.e-12;
38 nb_it_max_ = 1000000;
41}
42
43// printOn et readOn
45{
46 s<<" { seuil "<<seuil_;
47 if (precond_diag_)
48 s <<" diag ";
49 else if (is_local_gmres) s<<" sans_precond ";
50
51 if (controle_residu_) s<< " controle_residu "<<controle_residu_;
52 if (nb_it_max_!=1000000) s<<" nb_it_max "<<nb_it_max_;
53 if (limpr()==1) s<<" impr ";
54 if (limpr()==-1) s<<" quiet ";
55 if (save_matrice_) s<< " save_matrice ";
56 s<<" dim_espace_krilov "<<dim_espace_Krilov_;
57 s<<" } ";
58 return s;
59}
60
62{
63 Param param(que_suis_je());
64 set_param(param);
65 param.lire_avec_accolades_depuis(is);
66 return is;
67}
68
69void Solv_Gmres::set_param(Param& param) const
70{
71 param.ajouter_non_std("impr",(this));
72 param.ajouter("seuil",&seuil_);
73 param.ajouter_non_std("diag",(this));
74 param.ajouter_non_std("sans_precond",(this));
75 param.ajouter("nb_it_max",&nb_it_max_);
76 param.ajouter("controle_residu",&controle_residu_);
77 param.ajouter("save_matrice|save_matrix",&save_matrice_);
78 param.ajouter("dim_espace_krilov",&dim_espace_Krilov_);
79 param.ajouter_non_std("quiet",(this));
80}
81
83{
84 int retval = 1;
85
86 if (mot=="impr") fixer_limpr(1);
87 else if (mot=="quiet") fixer_limpr(-1);
88 else if (mot=="diag")
89 {
90 is_local_gmres=true;
92 }
93 else if (mot=="sans_precond")
94 {
95 is_local_gmres=true;
96 precond_diag_=false;
97 }
98 else retval = -1;
99
100 return retval;
101}
102
103
104
106 const DoubleVect& secmem,
107 DoubleVect& solution)
108{
109 if(sub_type(Matrice_Morse,la_matrice))
110 {
111 const Matrice_Morse& matrice = ref_cast(Matrice_Morse, la_matrice);
112 return Gmres(matrice,secmem,solution);
113 }
114 else
115 {
116 if(sub_type(Matrice_Bloc,la_matrice))
117 {
118 const Matrice_Bloc& matrice = ref_cast(Matrice_Bloc,la_matrice);
119 if(matrice.nb_bloc_lignes()>1)
120 {
121 Cerr<<"Solv_Gmres : WARNING : one is not able to carry out Gmres by blocks"<<finl;
122 exit();
123 return(-1);
124 }
125
127 {
128 Cerr<<"Solv_Gmres : WARNING : one is not able to carry out parallel calculation with Gmres"<<finl;
129 exit();
130 return(-1);
131 }
132
133 const Matrice_Morse& MB00 = ref_cast(Matrice_Morse,matrice.get_bloc(0,0).valeur());
134 int retour= Gmres(MB00,secmem,solution);
135 return retour;
136 }
137 else
138 {
139 Cerr<<"Solv_Gmres : WARNING : only linear systems based on Matrice_Morse_Sym or Matrice_Bloc type matrixes can be solved"<<finl;
140 exit();
141 return(-1);
142 }
143 }
144}
145
146int Solv_Gmres::gmres_local(const Matrice_Morse& A, const DoubleVect& b, DoubleVect& tab_x)
147{
148 // PL c'est pas joli et c'est de moi mais je ne comprends pas pourquoi
149 // l'utilisation de b.size_reelle() plante sur la matrice en pression depuis la 1.6.0 (non teste)
150 // Qu'est ce qui est fait en implicite pour b.size_reelle() soit >=0 ???
151 const int ns=(b.size_reelle_ok()?b.size_reelle():b.size_array());
152 int nb_ligne_tot=(int)Process::mp_sum((double) ns);
153
154 // A present dans le jdd
155 double epsGMRES=1.e-10*0;
156 //int nkr_min = 10;
157 //int nkr=std::max(nkr_min,nb_ligne_tot/2); // dimension de l'espace de Krylov
158 int nkr = dim_espace_Krilov_;
159 int nit1_min = 20;
160 int nit1=std::max(nit1_min,nb_ligne_tot);
161 int nit=std::min(nb_it_max_,nit1);
162 double rec_min = seuil_;
163 double rec_max = 0.1 ;
164 double res2_old=-1;
165 if (v.size()==0)
166 {
167 v.dimensionner(nkr); // Krilov vectors
168 h.resize(nkr + 1, nkr); // Heisenberg maatrix of coefficients
169 r.resize(nkr + 1);
170 h_loc.resize(nkr);
171 dh_loc.resize(nkr+1);
172 }
173
174 if (tab_Diag.size_array()!=ns)
175 {
176 tab_v0 = tab_x;
177 tab_v1 = tab_x;
178 tab_Diag.resize(ns);
179 }
180
181 // Initialisation
182 tab_v0 = 0.;
183 tab_v1 = 0.;
184 const bool precond_diag = precond_diag_;
185 {
186 Matrice_Morse_View matrice;
187 matrice.set(const_cast<Matrice_Morse&>(A));
188 DoubleArrView Diag = tab_Diag.view_wo();
189 Kokkos::parallel_for(start_gpu_timer(__KERNEL_NAME__), ns, KOKKOS_LAMBDA(
190 const int i)
191 {
192 Diag[i] = precond_diag ? 1. / matrice(i, i) : 1.;
193 });
194 end_gpu_timer(__KERNEL_NAME__);
195 }
196
197 A.multvect_(tab_x,tab_v0);
198 tab_v0 *= -1.;
199 tab_v0 += b;
200
201 // Reduce 2 mp_sum calls to 1 by computing local norms before and after GPU kernel
202 double res0 = local_carre_norme_vect(tab_v0);
203 {
204 CDoubleArrView Diag = tab_Diag.view_ro();
205 DoubleArrView v0 = tab_v0.view_rw();
206 Kokkos::parallel_for(start_gpu_timer(__KERNEL_NAME__), ns, KOKKOS_LAMBDA(
207 const int i)
208 {
209 v0(i) *= Diag(i);
210 });
211 end_gpu_timer(__KERNEL_NAME__);
212 }
213 double res = local_carre_norme_vect(tab_v0);
214 // Single collective operation
215 Process::mp_sum_for_each(res0, res);
216 res0 = sqrt(res0);
217 res = sqrt(res);
218
219 if (limpr()==1)
220 Cout<<"Gmres : initial residual = "<<res0<<finl;
221 // See http://stackoverflow.com/questions/3437085/check-nan-number
222 // May be could be interesting to implement isnan function somewhere
223 if (res0!=res0)
224 {
225 Cerr << "Nan detected in Solv_Gmres::gmres_local()" << finl;
226 Cerr << "Contact TRUST support." << finl;
228 }
229 rec_min = (rec_min<res*epsGMRES) ? res*epsGMRES : rec_min;
230 rec_min = (rec_min<rec_max) ? rec_min : rec_max ;
231 bool legacy = getenv("TRUST_GMRES_REDUCE_COLLECTIVES") == nullptr;
232
233 // iterations
234 for(int it=0; it<nit; it++)
235 {
236 if (res==0) return 0; // nothing to do
237 int nk = nkr;
238
239 //...Orthogonalisation of Arnoldi
240 tab_v0 /= res;
241 r = 0. ;
242 r[0] = res;
243 h = 0.;
244 for(int j=0; j<nkr; j++)
245 {
246 tab_v0.echange_espace_virtuel();
248 {
249 CDoubleArrView Diag = tab_Diag.view_ro();
250 DoubleArrView v1 = tab_v1.view_rw();
251 Kokkos::parallel_for(start_gpu_timer(__KERNEL_NAME__), ns, KOKKOS_LAMBDA(
252 const int i)
253 {
254 v1(i) *= Diag(i);
255 });
256 end_gpu_timer(__KERNEL_NAME__);
257 }
258 v[j] = tab_v0;
259 tab_v0 = tab_v1 ;
260 // Modifie par DJ
261 //---------------
262 double tem;
263 if (legacy)
264 {
265 // GMRES using classical Gram–Schmidt
266 for (int i = 0; i <= j; i++)
267 {
268 h(i, j) += mp_prodscal(tab_v0, v[i]);
269 tab_v0.ajoute(-h(i, j), v[i], VECT_REAL_ITEMS);
270 }
271 tem=mp_norme_vect(tab_v0);
272 }
273 else
274 {
275 // Communication-reduced GMRES using classical Gram–Schmidt with reorthogonalization (CGS-2), reducing global MPI collectives but at higher computational cost
276 // Compute local dot products
277 for (int i = 0; i <= j; i++)
278 h_loc[i] = local_prodscal(tab_v0, v[i]);
279 Process::mp_sum_for_each_item(h_loc, j+1); // One collective
280 // Orthoganalization
281 for (int i = 0; i <= j; i++)
282 {
283 h(i, j) = h_loc[i]; // Store in Hessenberg
284 tab_v0.ajoute(-h(i, j), v[i], VECT_REAL_ITEMS);
285 }
286 // Compute correction terms
287 for (int i = 0; i <= j; i++)
288 dh_loc[i] = local_prodscal(tab_v0, v[i]);
289 dh_loc[j + 1] = local_carre_norme_vect(tab_v0);
290 Process::mp_sum_for_each_item(dh_loc, j+2); // One collective
291 // Accumulate + correct
292 for (int i = 0; i <= j; i++)
293 {
294 h(i, j) += dh_loc[i];
295 tab_v0.ajoute(-dh_loc[i], v[i], VECT_REAL_ITEMS);
296 }
297 tem=std::sqrt(dh_loc[j + 1]); // Save one more collective
298 }
299
300 h(j+1,j) = tem;
301 if(tem<rec_min)
302 {
303 nk = j+1;
304 goto l5;
305 }
306 tab_v0 /= tem;
307 }
308 //...Triangularisation
309l5:
310 for(int i=0; i<nk; i++)
311 {
312 int im = i+1;
313 double tem = 1./sqrt(h(i,i)*h(i,i) + h(im,i)*h(im,i));
314 double ccos = h(i,i) * tem;
315 double ssin = - h(im,i) * tem;
316 for(int j=i; j<nk; j++)
317 {
318 tem = h(i,j);
319 h(i,j) = ccos * tem - ssin * h(im,j);
320 h(im,j) = ssin * tem + ccos * h(im,j);
321 }
322 r[im] = ssin * r[i];
323 r[i] *= ccos;
324 }
325
326 //...Solution of linear system
327 for(int i=nk-1; i>=0; i--)
328 {
329 r[i] /= h(i,i);
330 for(int i0=i-1; i0>=0; i0--)
331 r[i0] -= h(i0,i)* r[i];
332 }
333 for(int i=0; i<nk; i++)
334 tab_x.ajoute(r[i], v[i], VECT_REAL_ITEMS);
335
337 A.multvect_(tab_x,tab_v0);
338 tab_v0 *= -1. ;
339 tab_v0 += b;
340
341 // calcul du residu sans le precond....
342 double res2=mp_norme_vect(tab_v0);
343 if ((it>0) && (controle_residu_==1) && (sup_strict(res2,res2_old)))
344 {
345 Cout << "The Gmres iterative system is stopped after : " << it+1 <<" iterations "<<finl;
346 Cout << "since an increase of the residue is detected."<< finl;
347 return it;
348 }
349
350 res2_old = res2;
351 if (limpr()==1)
352 Cout<<" - At it = "<< it+1 <<", residu scalar = "<< res2 << finl;
353
354 // Test d'arret sur le residu
355 if(res2<rec_min)
356 {
357 // Ajoute par DJ
358 //--------------
359 if (limpr()>-1)
360 {
361 Cout << "Gmres : Number of iterations to reach convergence : " << it+1 << finl;
362 double residu_relatif = (res0>0?res2/res0:res2);
363 Cout << "Final residue: " << res2 << " ( " << residu_relatif << " )" << finl;
364 }
365 return it+1;
366 }
367 // Test d'arret sur le nombre d'iterations max
368 else if (it==nit-1)
369 {
370 if (limpr()>-1)
371 {
372 Cout << "Gmres : Stopped after "<< it+1 <<" iterations (=nb_it_max)"<< finl;
373 double residu_relatif = (res0>0?res2/res0:res2);
374 Cout << "Final residue: " << res2 << " ( " << residu_relatif << " )" << finl;
375 }
376 if (it == (nb_ligne_tot-1))
377 {
378 Cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"<< finl;
379 Cerr << "!!! Gmres stopped after a number of iterations equal to the matrix size. "<< finl;
380 Cerr << "!!! Either your matrix is ill-conditioned (try cholesky instead), or your convergence threshold is too low. "<< finl;
381 Cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"<< finl;
382 Process::exit(-1);
383 }
384 return it+1;
385 }
386
387 // Calcul du residu avec preconditionnement
388 {
389 CDoubleArrView Diag = tab_Diag.view_ro();
390 DoubleArrView v0 = tab_v0.view_rw();
391 Kokkos::parallel_for(start_gpu_timer(__KERNEL_NAME__), ns, KOKKOS_LAMBDA(
392 const int i)
393 {
394 v0(i) *= Diag(i);
395 });
396 end_gpu_timer(__KERNEL_NAME__);
397 }
398 res = mp_norme_vect(tab_v0);
399 }
400 return -1;
401}
402
403
405 const DoubleVect& secmem,
406 DoubleVect& solution)
407{
408 if (!is_local_gmres)
409 return matrice.inverse(secmem, solution, seuil_);
410 else
411 return gmres_local(matrice,secmem,solution);
412}
413
414
Class defining operators and methods for all reading operation in an input flow (file,...
Definition Entree.h:42
Classe Matrice_Base Classe de base de la hierarchie des matrices.
virtual DoubleVect & multvect_(const DoubleVect &, DoubleVect &) const
int nb_bloc_lignes() const
virtual const Matrice & get_bloc(int i, int j) const
Classe Matrice_Morse Represente une matrice M (creuse), non necessairement carree.
virtual int inverse(const DoubleVect &, DoubleVect &, double) const
Calcule la solution du systeme lineaire: A * solution = secmem.
Une chaine de caractere (Nom) en majuscules.
Definition Motcle.h:26
friend class Entree
Definition Objet_U.h:76
const Nom & que_suis_je() const
renvoie la chaine identifiant la classe.
Definition Objet_U.cpp:104
virtual Entree & readOn(Entree &)
Lecture d'un Objet_U sur un flot d'entree Methode a surcharger.
Definition Objet_U.cpp:293
virtual Sortie & printOn(Sortie &) const
Ecriture de l'objet sur un flot de sortie Methode a surcharger.
Definition Objet_U.cpp:282
Helper class to factorize the readOn method of Objet_U classes.
Definition Param.h:112
void ajouter(const char *keyword, const int *value, Param::Nature nat=Param::OPTIONAL)
Register an integer parameter.
Definition Param.cpp:364
void ajouter_non_std(const char *keyword, const Objet_U *value, Param::Nature nat=Param::OPTIONAL)
Register a keyword handled by Objet_U::lire_motcle_non_standard.
Definition Param.cpp:489
static void mp_sum_for_each(T &arg1, T &arg2)
C++14 compatible mp_sum_for_each: combine multiple mp_sum calls into one collective operation Usage: ...
Definition Process.cpp:207
static void mp_sum_for_each_item(TRUSTArray< _TYPE_ > &x, int n=-1)
Definition Process.cpp:193
static bool is_parallel()
Definition Process.cpp:110
static double mp_sum(double)
Calcule la somme de x sur tous les processeurs du groupe courant.
Definition Process.cpp:146
static void exit(int exit_code=-1)
Routine de sortie de TRUST dans une region Kokkos.
Definition Process.cpp:455
int resoudre_systeme(const Matrice_Base &, const DoubleVect &, DoubleVect &) override
DoubleVect h_loc
Definition Solv_Gmres.h:54
DoubleVect tab_v1
Definition Solv_Gmres.h:51
DoubleVect tab_v0
Definition Solv_Gmres.h:51
int Gmres(const Matrice_Morse &, const DoubleVect &, DoubleVect &)
int nb_it_max_
Definition Solv_Gmres.h:50
DoubleVect r
Definition Solv_Gmres.h:53
DoubleVect tab_Diag
Definition Solv_Gmres.h:51
int gmres_local(const Matrice_Morse &A, const DoubleVect &b, DoubleVect &tab_x1)
int controle_residu_
Definition Solv_Gmres.h:50
DoubleVect dh_loc
Definition Solv_Gmres.h:54
DoubleVects v
Definition Solv_Gmres.h:47
bool precond_diag_
Definition Solv_Gmres.h:49
void set_param(Param &param) const override
int dim_espace_Krilov_
Definition Solv_Gmres.h:50
DoubleTab h
Definition Solv_Gmres.h:52
int lire_motcle_non_standard(const Motcle &, Entree &) override
Lecture des parametres de type non simple d'un objet_U a partir d'un flot d'entree.
bool is_local_gmres
Definition Solv_Gmres.h:48
int limpr() const
void fixer_limpr(int l)
Classe de base des flux de sortie.
Definition Sortie.h:52
_SIZE_ size_array() const
_SIZE_ size_reelle() const
Definition TRUSTVect.tpp:27
_SIZE_ size_reelle_ok() const
Definition TRUSTVect.tpp:38
void ajoute(_SCALAR_TYPE_ alpha, const TRUSTVect &y, Mp_vect_options opt=VECT_ALL_ITEMS)
Definition TRUSTVect.tpp:52
virtual void echange_espace_virtuel(IsExchangeBlocking exchange_type=IsExchangeBlocking::DefaultBlocking, const std::string kernel_name="noname")