TRUST 1.9.8
HPC thermohydraulic platform
Loading...
Searching...
No Matches
Domaine_PolyMAC_CDO.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 <Linear_algebra_tools_impl.h>
17#include <MD_Vector_composite.h>
18#include <Domaine_Cl_PolyMAC_family.h>
19#include <Domaine_PolyMAC_CDO.h>
20#include <Option_PolyMAC_family.h>
21#include <Quadrangle_VEF.h>
22#include <Option_PolyMAC_family.h>
23#include <Poly_geom_base.h>
24#include <communications.h>
25#include <TRUSTTab_parts.h>
26#include <Segment_poly.h>
27#include <Matrix_tools.h>
28#include <Hexaedre_VEF.h>
29#include <Quadri_poly.h>
30#include <Array_tools.h>
31#include <TRUSTLists.h>
32#include <Tetra_poly.h>
33#include <Rectangle.h>
34#include <Tetraedre.h>
35#include <Hexa_poly.h>
36#include <TRUSTList.h>
37#include <Hexaedre.h>
38#include <Triangle.h>
39#include <EFichier.h>
40#include <Tri_poly.h>
41#include <Segment.h>
42#include <Domaine.h>
43#include <Scatter.h>
44#include <EChaine.h>
45#include <Lapack.h>
46#include <unistd.h>
47#include <numeric>
48#include <cmath>
49#include <cfloat>
50#include <tuple>
51#include <cfenv>
52
53#pragma GCC diagnostic push
54#pragma GCC diagnostic ignored "-Wunused-variable"
55#include <osqp/osqp.h>
56#pragma GCC diagnostic pop
57
58Implemente_instanciable(Domaine_PolyMAC_CDO, "Domaine_PolyMAC_CDO", Domaine_Poly_base);
59
61
63
70
72{
73 // Calcul de h_carre
74 h_carre = 1.e30;
75 h_carre_.resize(nb_faces());
76 // Calcul des surfaces
77 Elem_geom_base& elem_geom = domaine().type_elem().valeur();
78 int is_polyedre = sub_type(Poly_geom_base, elem_geom) ? 1 : 0;
79 const ArrOfInt PolyIndex = is_polyedre ? ref_cast(Poly_geom_base, domaine().type_elem().valeur()).getElemIndex() : ArrOfInt(0);
80 const DoubleVect& surfaces=face_surfaces();
81 const int nbe=nb_elem();
82 for (int num_elem=0; num_elem<nbe; num_elem++)
83 {
84 double surf_max = 0;
85 const int nb_faces_elem = is_polyedre ? PolyIndex[num_elem+1] - PolyIndex[num_elem] : domaine().nb_faces_elem();
86 for (int i=0; i<nb_faces_elem; i++)
87 {
88 double surf = surfaces(elem_faces(num_elem,i));
89 surf_max = (surf > surf_max)? surf : surf_max;
90 }
91 double vol = volumes(num_elem)/surf_max;
92 vol *= vol;
93 h_carre_(num_elem) = vol;
94 h_carre = ( vol < h_carre )? vol : h_carre;
95 }
96}
97
99{
100 const DoubleVect& fs = face_surfaces();
101
102 for (int num_face=0; num_face<nb_faces(); num_face++)
103 for (int dir=0; dir<2; dir++)
104 {
105 int elem = face_voisins_(num_face,dir);
106 if (elem!=-1)
107 {
108 volumes_entrelaces_dir_(num_face, dir) = sqrt(dot(&xp_(elem, 0), &xp_(elem, 0), &xv_(num_face, 0), &xv_(num_face, 0))) * fs[num_face];
109 volumes_entrelaces_[num_face] += volumes_entrelaces_dir_(num_face, dir);
110 }
111 }
112 volumes_entrelaces_.echange_espace_virtuel();
113 volumes_entrelaces_dir_.echange_espace_virtuel();
114}
115
117{
118 const IntTab& e_f = elem_faces(), &f_e = face_voisins();
119 const DoubleTab& nf = face_normales();
120 const DoubleVect& fs = face_surfaces(); //, &vf = volumes_entrelaces();
121 int i, j, e1, e2, f1, f2, d, D = dimension, ok;
122
123 IntTrav ntot, nequiv;
125 creer_tableau_faces(nequiv);
126 equiv_.resize(nb_faces_tot(), 2, e_f.dimension(1));
127 equiv_ = -1;
128
129 Cerr << domaine().le_nom() << " : intializing equiv... " ;
130
131 const bool is_PolyMAC_CDO = (que_suis_je() == "Domaine_PolyMAC_CDO");
132
133 for (int f = 0; f < nb_faces_tot(); f++)
134 if ((e1 = f_e(f, 0)) >= 0 && (e2 = f_e(f, 1)) >= 0)
135 for (i = 0; i < e_f.dimension(1) && (f1 = e_f(e1, i)) >= 0; i++)
136 for (j = 0, ntot(f)++; j < e_f.dimension(1) && (f2 = e_f(e2, j)) >= 0; j++)
137 {
138 if (std::fabs(fs(f1) * fs(f2)) < 1e-20) continue;
139 if (!is_PolyMAC_CDO || (is_PolyMAC_CDO && Option_PolyMAC_family::MAILLAGE_VDF))
140 if (std::fabs(std::fabs(dot(&nf(f1, 0), &nf(f2, 0)) / (fs(f1) * fs(f2))) - 1) > 1e-6)
141 continue; //normales colineaires?
142
143 // XXX Elie Saikali
144 // Options pour forcer le calcul du tableau equiv
145 // car le test ne marche pas si le maillage est hexa, conforme et non-uniforme
147 {
148 bool aligned = true;
149 const int orn_f1 = orientation(f1), orn_f2 = orientation(f2);
150 const bool same_orn = (orn_f1 == orn_f2);
151 ok = 1; // pour gnu ...
152
153 if (same_orn)
154 for (d = 0; d < D; d++)
155 if (d != orn_f1)
156 if ((xv_(f1, d) != xv_(f2, d)) )
157 {
158 aligned = false;
159 break;
160 }
161
162 if (same_orn && aligned && ( f1 != f2 ))
163 {
164 // on cherche si f1 et f2 trouve le meme elem
165 int ee1 = f_e(f1, 0), ee2 = f_e(f1, 1);
166
167 for (int ii = 0; ii < e_f.dimension(1); ii++)
168 {
169 int ff1 = ee1 >= 0 ? e_f(ee1, ii) : -1;
170 int ff2 = ee2 >= 0 ? e_f(ee2, ii) : -1;
171
172 if (f2 == ff1 || f2 == ff2)
173 {
174 ok = 1;
175 break;
176 }
177 else
178 ok = 0;
179 }
180 }
181 else
182 for (ok = 1, d = 0; d < D; d++)
183 ok &= std::fabs((xv_(f1, d) - xp_(e1, d)) - (xv_(f2, d) - xp_(e2, d))) < 1e-12; //xv - xp identiques?
184 }
185 else
186 {
187 std::array<double, 3> v1 = cross(D, D, &xv_(f1, 0), &nf(f1, 0), &xp_(e1, 0));//produit vectoriel (xs - xf)xnf
188 std::array<double, 3> v2 = cross(D, D, &xv_(f2, 0), &nf(f2, 0), &xp_(e2, 0));//produit vectoriel (xs - xf)xnf
189
190 double norm1 = (D < 3 ? v1[2]*v1[2] : 0.), norm2 = (D < 3 ? v2[2]*v2[2] : 0.);
191 for (ok = 1, d = 0; d < D; d++)
192 {
193 ok &= std::fabs((xv_(f1, d) - xp_(e1, d)) - (xv_(f2, d) - xp_(e2, d))) < (is_PolyMAC_CDO ? 1.e-12 /* XXX Elie Saikali : comme avant pour le moment */ : 1e-12); //xv - xp identiques?
194 norm1 += v1[d]*v1[d];
195 norm2 += v2[d]*v2[d];
196 }
197 ok &= sqrt(norm1) < 1e-12;
198 ok &= sqrt(norm2) < 1e-12;
199 }
200
201 if (!ok)
202 continue;
203
204 equiv_(f, 0, i) = f2;
205 equiv_(f, 1, j) = f1;
206 nequiv(f)++; //si oui, on a equivalence
207 }
208
209 Cerr << mp_somme_vect_as_double(nequiv) * 100. / mp_somme_vect_as_double(ntot) << "% equivalent faces!" << finl;
210}
211
213{
214 Cerr << "Le Domaine_PolyMAC_CDO a ete rempli avec succes" << finl;
215 // calculer_h_carre();
216
217 Journal() << "Domaine_PolyMAC_CDO::Modifier_pour_Cl" << finl;
218 int nb_cond_lim=conds_lim.size();
219 int num_cond_lim=0;
220 for (; num_cond_lim<nb_cond_lim; num_cond_lim++)
221 {
222 //for cl
223 const Cond_lim_base& cl = conds_lim[num_cond_lim].valeur();
224 if (sub_type(Periodique, cl))
225 {
226 //if Perio
227 const Periodique& la_cl_period = ref_cast(Periodique,cl);
228 int nb_faces_elem = domaine().nb_faces_elem();
229 const Front_VF& la_front_dis = ref_cast(Front_VF,cl.frontiere_dis());
230 int ndeb = 0;
231 int nfin = la_front_dis.nb_faces_tot();
232#ifndef NDEBUG
233 int num_premiere_face = la_front_dis.num_premiere_face();
234 int num_derniere_face = num_premiere_face+nfin;
235#endif
236 int nbr_faces_bord = la_front_dis.nb_faces();
237 assert((nb_faces()==0)||(ndeb<nb_faces()));
238 assert(nfin>=ndeb);
239 int elem1,elem2,k;
240 int face;
241 // Modification des tableaux face_voisins_ , face_normales_ , volumes_entrelaces_
242 // On change l'orientation de certaines normales
243 // de sorte que les normales aux faces de periodicite soient orientees
244 // de face_voisins(la_face_en_question,0) vers face_voisins(la_face_en_question,1)
245 // comme le sont les faces internes d'ailleurs
246
247 DoubleVect C1C2(dimension);
248 double vol,psc=0;
249
250 for (int ind_face=ndeb; ind_face<nfin; ind_face++)
251 {
252 //for ind_face
253 face = la_front_dis.num_face(ind_face);
254 if ( (face_voisins_(face,0) == -1) || (face_voisins_(face,1) == -1) )
255 {
256 int faassociee = la_front_dis.num_face(la_cl_period.face_associee(ind_face));
257 if (ind_face<nbr_faces_bord)
258 {
259 assert(faassociee>=num_premiere_face);
260 assert(faassociee<num_derniere_face);
261 }
262
263 elem1 = face_voisins_(face,0);
264 elem2 = face_voisins_(faassociee,0);
265 vol = (volumes_[elem1] + volumes_[elem2])/nb_faces_elem;
266 volumes_entrelaces_[face] = vol;
267 volumes_entrelaces_[faassociee] = vol;
268 face_voisins_(face,1) = elem2;
269 face_voisins_(faassociee,0) = elem1;
270 face_voisins_(faassociee,1) = elem2;
271 psc = 0;
272 for (k=0; k<dimension; k++)
273 {
274 C1C2[k] = xv_(face,k) - xp_(face_voisins_(face,0),k);
275 psc += face_normales_(face,k)*C1C2[k];
276 }
277
278 if (psc < 0)
279 for (k=0; k<dimension; k++)
280 face_normales_(face,k) *= -1;
281
282 for (k=0; k<dimension; k++)
283 face_normales_(faassociee,k) = face_normales_(face,k);
284 }
285 }
286 }
287 }
288
289 // PQ : 10/10/05 : les faces periodiques etant a double contribution
290 // l'appel a marquer_faces_double_contrib s'effectue dans cette methode
291 // afin de pouvoir beneficier de conds_lim.
293}
294
295//stabilisation des matrices m1 et m2 de PolyMAC_CDO
296inline void Domaine_PolyMAC_CDO::ajouter_stabilisation(DoubleTab& M, DoubleTab& N) const
297{
298 int i, j, k, i1, i2, j1, j2, n_f = M.dimension(0), lwork = -1, infoo = 0;
299 DoubleTab A, S, b(n_f, 1), D(1, 1), x(1, 1), work(1), U(n_f - dimension, n_f - dimension), V;
300
301 /* spectre de M */
302 kersol(M, b, 1e-12, nullptr, x, S);
303 double l_max = S(0), l_min = S(dimension - 1); //vp la plus petite sans stabilisation
304
305 /* D : noyau de N (N.D = 0), de taille n_f * (n_f - dimension) */
306 b.resize(dimension, 1), kersol(N, b, 1e-12, &D, x, S);
307 assert(D.dimension(1) == n_f - dimension); //M doit etre de rang d
308
309 /* matrice U telle que M + D.U.Dt mimimise les termes hors diagonale */
310 //une ligne par coeff M(i, j > i), une colonne par terme U(i, j >= i)
311 int n_k = D.dimension(1), n_l = n_f * (n_f - 1) / 2, n_c = n_k * (n_k + 1) / 2, un = 1;
312 A.resize(n_l, n_c), b.resize(n_l, 1);
313 for (i1 = i = 0; i1 < n_f; i1++)
314 for (i2 = i1 + 1; i2 < n_f; i2++, i++) //(i1, i2, i) -> numero de ligne
315 for (j1 = j = 0, b(i, 0) = -M(i1, i2); j1 < n_k; j1++)
316 for (j2 = j1; j2 < n_k; j2++, j++) //(j1, j2, j) -> numero de colonne
317 A(i, j) = D(i1, j1) * D(i2, j2) + (j1 != j2) * D(i1, j2) * D(i2, j1);
318 char trans = 'T';
319 //minimise la somme des carres des termes hors diag de M
320 F77NAME(dgels)(&trans, &n_c, &n_l, &un, &A(0, 0), &n_c, &b(0, 0), &n_l, &work(0), &lwork, &infoo); //"workspace query"
321 work.resize(lwork = (int)work(0));
322 F77NAME(dgels)(&trans, &n_c, &n_l, &un, &A(0, 0), &n_c, &b(0, 0), &n_l, &work(0), &lwork, &infoo); //le vrai appel
323 assert(infoo == 0);
324 //reconstruction de U
325 for (j1 = j = 0; j1 < n_k; j1++)
326 for (j2 = j1; j2 < n_k; j2++, j++) U(j1, j2) = U(j2, j1) = b(j, 0);
327
328 /* ajustement de U pour que la vp minimale depasse eps */
329 //decomposition de Schur U = Vt.S.V
330 char jobz = 'V', uplo = 'U';
331 V = U, S.resize(n_k);
332 F77NAME(dsyev)(&jobz, &uplo, &n_k, &V(0, 0), &n_k, &S(0), &work(0), &lwork, &infoo);//"workspace query"
333 work.resize(lwork = (int)work(0));
334 F77NAME(dsyev)(&jobz, &uplo, &n_k, &V(0, 0), &n_k, &S(0), &work(0), &lwork, &infoo);
335 assert(infoo == 0);
336 //pour garantir des vp plus grandes que eps : S(k) -> std::max(S(k), eps)
337 for (i = 0, U = 0; i < n_k; i++)
338 for (j = 0; j < n_k; j++)
339 for (k = 0; k < n_k; k++) U(i, j) += V(k, i) * std::min(std::max(S(k), l_min), l_max) * V(k, j);
340
341 /* ajout a M */
342 for (i1 = 0; i1 < n_f; i1++)
343 for (i2 = 0; i2 < n_f; i2++)
344 for (j1 = 0; j1 < n_k; j1++)
345 for (j2 = 0; j2 < n_k; j2++)
346 M(i1, i2) += D(i1, j1) * U(j1, j2) * D(i2, j2);
347}
348
349/* recherche d'une matrice W verifiant W.R = N avec sum_{i!=j} w_{ij}^2 minimal et un spectre acceptable */
350/* en entree,W contient le stencil admissible */
351int Domaine_PolyMAC_CDO::W_stabiliser(DoubleTab& W, DoubleTab& R, DoubleTab& N, int *ctr, double *spectre) const
352{
353 int i, j, k, l, n_f = R.dimension(0), nv = 0, d = R.dimension(1), infoo = 0, lwork = -1, sym, diag, ret, it, cv;
354
355 /* idx : numero de l'inconnue W(i, j) dans le probleme d'optimisation */
356 //si NtR est symetrique, alors on cherche a avoir W symetrique
357 Matrice33 NtR(0, 0, 0, 0, d < 2, 0, 0, 0, d < 3), iNtR;
358 for (i = 0; i < d; i++)
359 for (j = 0; j < d; j++)
360 for (k = 0; k < n_f; k++) NtR(i, j) += N(k, i) * R(k, j);
361 for (i = 0, sym = 1; i < d; i++)
362 for (j = i + 1; j < d; j++) sym &= (std::fabs(NtR(i, j) - NtR(j, i)) < 1e-8);
363 //remplissage de idx(i, j) : numero de W_{ij} dans le pb d'optimisation
364 IntTrav idx(n_f, n_f);
365 if (sym)
366 for (i = 0; i < n_f; i++)
367 for (j = i; j < n_f; j++) idx(i, j) = idx(j, i) = (W(i, j) ? nv++ : -1);
368 else for (i = 0; i < n_f; i++)
369 for (j = 0; j < n_f; j++) idx(i, j) = (W(i, j) ? nv++ : -1);
370
371 /* version non stabilisee : W0 = N.(NtR)^-1.Nt */
372 Matrice33::inverse(NtR, iNtR); //crash si NtR non inversible
373 for (i = 0, W = 0; i < n_f; i++)
374 for (j = 0; j < d; j++)
375 for (k = 0; k < d; k++)
376 for (l = 0; l < n_f; l++) W(i, l) += N(i, j) * iNtR(j, k) * N(l, k);
377 //spectre de Ws (partie symetrique de W)
378 DoubleTrav Ws(n_f, n_f), S(n_f), work(1);
379 for (i = 0; i < n_f; i++)
380 for (j = 0; j < n_f; j++) Ws(i, j) = (W(i, j) + W(j, i)) / 2;
381 char jobz = 'N', uplo = 'U';
382 F77NAME(dsyev)(&jobz, &uplo, &n_f, &Ws(0, 0), &n_f, &S(0), &work(0), &lwork, &infoo);//"workspace query"
383 work.resize(lwork = (int)work(0));
384 F77NAME(dsyev)(&jobz, &uplo, &n_f, &Ws(0, 0), &n_f, &S(0), &work(0), &lwork, &infoo);//vrai appel
385 assert(S(0) > -1e-8 && S(n_f - dimension) > 0);
386 if (spectre) spectre[0] = std::min(spectre[0], S(n_f - dimension)), spectre[2] = std::max(spectre[2], S(n_f - 1));
387 //bornes sur le spectre de la matrice finale
388 double l_min = S(n_f - dimension) / 100, l_max = S(n_f - 1) * 100;
389
390 /* probleme d'optimisation : sur les W_{ij} autorises */
391 OSQPData data;
392 OSQPSettings settings;
393 osqp_set_default_settings(&settings);
394 settings.scaled_termination = 1, settings.polish = 1, settings.eps_abs = settings.eps_rel = 1e-8, settings.max_iter = 1e3;
395
396 /* contrainte : W.R = N */
397 std::vector<std::map<int, double>> C(nv); //stockage CSC : C[j][i] = M_{ij}
398 std::vector<double> lb, ub; //bornes inf/sup
399 int il = 0; //suivi de la ligne qu'on est en train de creer
400 for (i = 0; i < n_f; i++)
401 for (j = 0; j < d; j++, il++)
402 for (k = 0, lb.push_back(N(i, j)), ub.push_back(N(i, j)); k < n_f; k++)
403 if (idx(i, k) >= 0) C[idx(i, k)][il] += R(k, j);
404
405 /* objectif: minimiser la norme l2 des termes hors diagonale */
406 std::vector<int> P_c(nv + 1), P_l(nv), A_c, A_l; //coeffs de la colonne c : indices [P_c(c), P_c(c + 1)[ dans P_l, P_v
407 std::vector<double> P_v(nv), A_v, Q(nv, 0.);
408 for (i = 0; i < nv; i++) P_l[i] = i, P_c[i + 1] = i + 1;
409 for (i = 0; i < n_f; i++)
410 for (j = 0; j < n_f; j++)
411 if (idx(i, j) >= 0) P_v[idx(i, j)] += (i == j ? 0 : 1);
412 data.n = nv, data.P = csc_matrix(nv, nv, (int)P_v.size(), P_v.data(), P_l.data(), P_c.data()), data.q = Q.data();
413
414 //iterations : on resout et on ajoute des contraintes tant que W a un spectre hors de [l_min, l_max]
415 std::fenv_t fenv;
416 std::feholdexcept(&fenv); //suspend les exceptions FP
417 std::vector<double> sol(nv);
418 for (cv = 0, it = 0; !cv && it < 100; it++)
419 {
420 /* assemblage de A : matrice des contraintes */
421 for (j = 0, A_c.resize(1), A_l.resize(0), A_v.resize(0); j < nv; j++, A_c.push_back((int)A_l.size()))
422 for (auto && l_v : C[j]) A_l.push_back(l_v.first), A_v.push_back(l_v.second);
423 data.A = csc_matrix((int)lb.size(), nv, (int)A_v.size(), A_v.data(), A_l.data(), A_c.data());
424 data.l = lb.data(), data.u = ub.data(), data.m = (int)lb.size();
425
426 /* resolution */
427 OSQPWorkspace *osqp = nullptr;
428 if (osqp_setup(&osqp, &data, &settings)) Cerr << "Domaine_PolyMAC_CDO::W_stabiliser : osqp_setup error" << finl, Process::exit();
429 if (it) osqp_warm_start_x(osqp, sol.data()); //repart de l'iteration precedente
430 osqp_solve(osqp);
431 ret = osqp->info->status_val, sol.assign(osqp->solution->x, osqp->solution->x + nv);
432 if (ret == OSQP_PRIMAL_INFEASIBLE || ret == OSQP_PRIMAL_INFEASIBLE_INACCURATE)
433 {
434 osqp_cleanup(osqp), free(data.A);
435 break;
436 }
437 for (i = 0; i < n_f; i++)
438 for (j = 0; j < n_f; j++) W(i, j) = (idx(i, j) >= 0 ? sol[idx(i, j)] : 0);
439
440 /* calcul du spectre : Ws recupere les vecteurs propres */
441 for (i = 0; i < n_f; i++)
442 for (j = 0; j < n_f; j++) Ws(i, j) = (W(i, j) + W(j, i)) / 2;
443 jobz = 'V', F77NAME(dsyev)(&jobz, &uplo, &n_f, &Ws(0, 0), &n_f, &S(0), &work(0), &lwork, &infoo);
444
445 /* pour chaque vp sortant de [ l_min, l_max ], on ajoute une contrainte pour la restreindre a [l_min * 1.1, l_max / 1.1 ] */
446 for (i = 0, cv = (osqp->info->status_val == OSQP_SOLVED); i < n_f; i++)
447 if (S(i) < l_min || S(i) > l_max)
448 {
449 for (j = 0; j < n_f; j++)
450 for (k = 0; k < n_f; k++)
451 if(idx(j, k) >= 0) C[idx(j, k)][il] += Ws(i, j) * Ws(i, k);
452 lb.push_back(l_min * (S(i) < l_min ? 1.1 : 1)), ub.push_back(l_max / (S(i) > l_max ? 1.1 : 1));
453 cv = 0, il++; //on repart avec une ligne en plus
454 }
455 osqp_cleanup(osqp), free(data.A);
456 }
457 free(data.P);
458 std::fesetenv(&fenv); //remet les exceptions FP
459
460 if (!cv)
461 {
462 Cerr << "Matrice non stabilisee : attention le maillage est trop deforme!!" << finl;
463 return 0; //ca passe ou ca casse
464 }
465 if (spectre)
466 for (i = 0; i < n_f; i++) spectre[1] = std::min(spectre[1], S(i)), spectre[3] = std::max(spectre[3], S(i));
467
468 //statistiques
469 //ctr[0] : diagonale, ctr[1] : symetrique
470 for (i = 0, diag = 1; i < n_f; i++)
471 for (j = 0; j < n_f; j++) diag &= (i == j || std::fabs(W(i, j)) < 1e-6);
472 ctr[0] += diag, ctr[1] += sym;
473 return 1;
474}
475
476void Domaine_PolyMAC_CDO::init_m2_new() const
477{
478 const IntTab& e_f = elem_faces();
479 const DoubleVect& fs = face_surfaces(), &ve = volumes();
480 int i, j, e, n_f, ctr[3] = {0, 0, 0 };
481 double spectre[4] = { DBL_MAX, DBL_MAX, 0, 0 }; //vp min (partie consistante, partie stab), vp max (partie consistante, partie stab)
482
483 if (is_init["w2"]) return;
484 Cerr << domaine().le_nom() << " : initializing w2/m2 ... ";
485
486 DoubleTab W, M;
487
488 /* pour le partage entre procs */
489 DoubleTrav m2e(0, e_f.dimension(1), e_f.dimension(1)), w2e(0, e_f.dimension(1), e_f.dimension(1));
491
492
493 /* calcul sur les elements reels */
494 //std::map<int, std::vector<int>> som_face; //som_face[s] : faces de l'element e touchant le sommet s
495 IntTrav nnz, nef;//par elements : nombre de coeffs non nuls, nombre de faces (lignes)
497 for (e = 0; e < nb_elem(); e++)
498 {
499 W2(nullptr, e, W);
500 M2(nullptr, e, M);
501
502 for (n_f = 0; n_f < e_f.dimension(1) && e_f(e, n_f) >= 0; ) n_f++;
503 for (i = 0; i < n_f; i++)
504 for (j = 0, nef(e)++; j < n_f; j++) w2e(e, i, j) = W(i, j, 0), nnz(e) += (std::fabs(W(i, j, 0)) > 1e-6);
505 for (i = 0; i < n_f; i++)
506 for (j = 0; j < n_f; j++) m2e(e, i, j) = M(i, j, 0);
507 }
508
509 /* echange et remplissage */
510 m2e.echange_espace_virtuel(), w2e.echange_espace_virtuel();
511 for (e = 0, m2d.append_line(0), m2i.append_line(0), w2i.append_line(0); e < nb_elem_tot(); e++, m2d.append_line(m2i.size() - 1))
512 {
513 for (n_f = 0; n_f < e_f.dimension(1) && e_f(e, n_f) >= 0; ) n_f++;
514 for (i = 0; i < n_f; i++, m2i.append_line(m2j.size()))
515 for (j = 0, m2j.append_line(i), m2c.append_line(m2e(e, i, i)); j < n_f; j++)
516 if (j != i && std::fabs(m2e(e, i, j)) > 1e-6) m2j.append_line(j), m2c.append_line(m2e(e, i, j));
517 for (i = 0; i < n_f; i++, w2i.append_line(w2j.size()))
518 for (j = 0, w2j.append_line(i), w2c.append_line(w2e(e, i, i)); j < n_f; j++)
519 if (j != i && std::fabs(w2e(e, i, j)) > 1e-6) w2j.append_line(j), w2c.append_line(w2e(e, i, j));
520 }
521 int f, fb;
522 for (e = 0; e < nb_elem_tot(); e++)
523 for (i = 0; i < m2d(e + 1) - m2d(e); i++)
524 for (f = e_f(e, i), j = w2i(m2d(e) + i); j < w2i(m2d(e) + i + 1); j++)
525 {
526 fb = e_f(e, w2j(j));
527 w2c(j) /= fs(f) * fs(fb) / ve(e);
528 }
529
530 for (e = 0; e < nb_elem_tot(); e++)
531 for (i = 0; i < m2d(e + 1) - m2d(e); i++)
532 for (f = e_f(e, i), j = m2i(m2d(e) + i); j < m2i(m2d(e) + i + 1); j++)
533 {
534 fb = e_f(e, m2j(j));
535 m2c(j) *= fs(f) * fs(fb) / ve(e);
536 }
537
538 CRIMP(m2d), CRIMP(m2i), CRIMP(m2j), CRIMP(m2c), CRIMP(w2i), CRIMP(w2j), CRIMP(w2c);
539 double n_tot = Process::mp_sum_as_double(nb_elem());
540 Cerr << 100. * Process::mp_sum_as_double(ctr[0]) / n_tot << "% diag " << 100. * Process::mp_sum_as_double(ctr[1]) / n_tot << "% sym "
541 << 100. * Process::mp_sum_as_double(ctr[2]) / n_tot << "% sparse lambda : " << Process::mp_min(spectre[0]) << " / "
542 << Process::mp_min(spectre[1]) << " -> " << Process::mp_max(spectre[2])
543 << " / " << Process::mp_max(spectre[3]) << " width : " << mp_somme_vect_as_double(nnz) * 1. / mp_somme_vect_as_double(nef) << finl;
544 is_init["w2"] = 1;
545}
546
547void Domaine_PolyMAC_CDO::init_m2_osqp() const
548{
549 const IntTab& f_e = face_voisins(), &e_f = elem_faces(), &f_s = face_sommets();
550 const DoubleVect& fs = face_surfaces(), &ve = volumes();
551 const DoubleTab& nf = face_normales();
552 int i, j, e, f, s, n_f, ctr[3] = {0, 0, 0 }, infoo = 0;
553 double spectre[4] = { DBL_MAX, DBL_MAX, 0, 0 }; //vp min (partie consistante, partie stab), vp max (partie consistante, partie stab)
554 char uplo = 'U';
555
556 if (is_init["w2"]) return;
557 Cerr << domaine().le_nom() << " : initializing w2/m2 ... ";
558
559 DoubleTab W, M, R, N;
560
561 /* pour le partage entre procs */
562 DoubleTrav m2e(0, e_f.dimension(1), e_f.dimension(1)), w2e(0, e_f.dimension(1), e_f.dimension(1));
564
565
566 /* calcul sur les elements reels */
567 std::map<int, std::vector<int>> som_face; //som_face[s] : faces de l'element e touchant le sommet s
568 IntTrav nnz, nef;//par elements : nombre de coeffs non nuls, nombre de faces (lignes)
570 for (e = 0; e < nb_elem(); e++)
571 {
572 for (n_f = 0; n_f < e_f.dimension(1) && e_f(e, n_f) >= 0; ) n_f++;
573 W.resize(n_f, n_f), R.resize(n_f, dimension), N.resize(n_f, dimension);
574
575 /* matrices R (lignes elements -> faces) et N (normales sortantes aux faces) */
576 for (i = 0; i < n_f; i++)
577 for (j = 0, f = e_f(e, i); j < dimension; j++)
578 N(i, j) = nf(f, j) / fs(f) * (e == f_e(f, 0) ? 1 : -1), R(i, j) = (xv_(f, j) - xp_(e, j)) * fs(f) / ve(e);
579
580 /* faces connectees a chaque sommet, puis stencil admissible */
581 for (i = 0, W = 0, som_face.clear(); i < n_f; i++)
582 for (j = 0, f = e_f(e, i); j < f_s.dimension(1) && (s = f_s(f, j)) >= 0; j++) som_face[s].push_back(i);
583 for (auto &s_fs : som_face)
584 for (auto i1 : s_fs.second)
585 for (auto i2 : s_fs.second) W(i1, i2) = 1;
586
587 /* matrice W2 stabilisee */
588 W = 1, W_stabiliser(W, R, N, ctr, spectre); /* il faut une matrice complete */
589
590 /* matrice M2 : W2^-1 */
591 M = W;
592 F77NAME(dpotrf)(&uplo, &n_f, M.addr(), &n_f, &infoo);
593 F77NAME(dpotri)(&uplo, &n_f, M.addr(), &n_f, &infoo);
594 assert(infoo == 0);
595 for (i = 0; i < n_f; i++)
596 for (j = i + 1; j < n_f; j++) M(i, j) = M(j, i);
597
598 for (i = 0; i < n_f; i++)
599 for (j = 0, nef(e)++; j < n_f; j++) w2e(e, i, j) = W(i, j), nnz(e) += (std::fabs(W(i, j)) > 1e-6);
600 for (i = 0; i < n_f; i++)
601 for (j = 0; j < n_f; j++) m2e(e, i, j) = M(i, j);
602 }
603
604 /* echange et remplissage */
605 m2e.echange_espace_virtuel(), w2e.echange_espace_virtuel();
606 for (e = 0, m2d.append_line(0), m2i.append_line(0), w2i.append_line(0); e < nb_elem_tot(); e++, m2d.append_line(m2i.size() - 1))
607 {
608 for (n_f = 0; n_f < e_f.dimension(1) && e_f(e, n_f) >= 0; ) n_f++;
609 for (i = 0; i < n_f; i++, m2i.append_line(m2j.size()))
610 for (j = 0, m2j.append_line(i), m2c.append_line(m2e(e, i, i)); j < n_f; j++)
611 if (j != i && std::fabs(m2e(e, i, j)) > 1e-6) m2j.append_line(j), m2c.append_line(m2e(e, i, j));
612 for (i = 0; i < n_f; i++, w2i.append_line(w2j.size()))
613 for (j = 0, w2j.append_line(i), w2c.append_line(w2e(e, i, i)); j < n_f; j++)
614 if (j != i && std::fabs(w2e(e, i, j)) > 1e-6) w2j.append_line(j), w2c.append_line(w2e(e, i, j));
615 }
616
617 CRIMP(m2d), CRIMP(m2i), CRIMP(m2j), CRIMP(m2c), CRIMP(w2i), CRIMP(w2j), CRIMP(w2c);
618 double n_tot = Process::mp_sum_as_double(nb_elem());
619 Cerr << 100. * Process::mp_sum_as_double(ctr[0]) / n_tot << "% diag " << 100. * Process::mp_sum_as_double(ctr[1]) / n_tot << "% sym "
620 << 100. * Process::mp_sum_as_double(ctr[2]) / n_tot << "% sparse lambda : " << Process::mp_min(spectre[0]) << " / "
621 << Process::mp_min(spectre[1]) << " -> " << Process::mp_max(spectre[2])
622 << " / " << Process::mp_max(spectre[3]) << " width : " << mp_somme_vect_as_double(nnz) * 1. / mp_somme_vect_as_double(nef) << finl;
623 is_init["w2"] = 1;
624}
625
626//matrice mimetique W_2/M_2 : valeurs tangentes aux lignes element-faces <-> valeurs normales aux faces
628{
629 if (Option_PolyMAC_family::USE_NEW_M2) init_m2_new();
630 else init_m2_osqp();
631}
632
633
634//interpolation normales aux faces -> elements d'ordre 1
636{
637 const IntTab& e_f = elem_faces(), &f_e = face_voisins();
638 const DoubleVect& ve = volumes_, &fs = face_surfaces();
639 int i, k, e, f;
640
641 if (is_init["ve"]) return;
642 Cerr << domaine().le_nom() << " : initialisation de ve... ";
643 vedeb.resize(1);
644 veci.resize(0, 3);
645 //formule (1) de Basumatary et al. (2014) https://doi.org/10.1016/j.jcp.2014.04.033 d'apres Perot
646 for (e = 0; e < nb_elem_tot(); vedeb.append_line(veji.dimension(0)), e++)
647 for (i = 0; i < e_f.dimension(1) && (f = e_f(e, i)) >= 0; i++)
648 {
649 double x[3] = { 0, };
650 for (k = 0; k < dimension; k++) x[k] = fs(f) * (xv(f, k) - xp(e, k)) * (e == f_e(f, 0) ? 1 : -1) / ve(e);
651 veji.append_line(f), veci.append_line(x[0], x[1], x[2]);
652 }
653 CRIMP(vedeb), CRIMP(veji), CRIMP(veci);
654 is_init["ve"] = 1, Cerr << "OK" << finl;
655}
656
657//rotationnel aux faces d'un champ tangent aux aretes
659{
660 const IntTab& f_s = face_sommets();
661 const DoubleTab& xs = domaine().coord_sommets(), &nf = face_normales();
662 const DoubleVect& la = longueur_aretes(), &fs = face_surfaces();
663
664 if (is_init["rf"]) return;
665 int i, s, f;
666 rfdeb.resize(1);
667 for (f = 0; f < nb_faces_tot(); rfdeb.append_line(rfji.dimension(0)), f++)
668 for (i = 0; i < f_s.dimension(1) && (s = f_s(f, i)) >= 0; i++)
669 {
670 int s2 = f_s(f, i + 1 < f_s.dimension(1) && f_s(f, i + 1) >= 0 ? i + 1 : 0),
671 a = dimension < 3 ? s : som_arete[s].at(s2);
672 std::array<double, 3> taz = {{ 0, 0, 1 }}, vec; //vecteur tangent a l'arete et produit vectoriel de celui-ci avec xa - xv
673 vec = cross(dimension, 3, dimension < 3 ? &xs(a, 0) : &xa_(a, 0), dimension < 3 ? &taz[0] : &ta_(a, 0), &xv_(f, 0));
674 int sgn = dot(&vec[0], &nf(f, 0)) > 0 ? 1 : -1;
675 rfji.append_line(a), rfci.append_line(sgn * (dimension < 3 ? 1 : la(a)) / fs(f));
676 }
677 CRIMP(rfdeb), CRIMP(rfji), CRIMP(rfci);
678 is_init["rf"] = 1;
679}
680
681//interpolation aux elements d'un champ dont on connait la composante tangente aux aretes (en 3D ), ou la composante verticale aux sommets en 2D
683{
684 if (is_init["we"]) return;
685
686 //remplissage de arete_faces_ (liste des faces touchant chaque arete en 3D, chaque sommet en 2D)
687 std::vector<std::vector<int> > a_f_vect(dimension < 3 ? nb_som_tot() : domaine().nb_aretes_tot());
688 const IntTab& f_s = face_sommets_;
689 for (int f = 0, i, s1; f < nb_faces_tot(); f++)
690 for (i = 0; i < f_s.dimension(1) && (s1 = f_s(f, i)) >= 0; i++)
691 {
692 int s2 = f_s(f, i + 1 < f_s.dimension(1) && f_s(f, i + 1) >= 0 ? i + 1 : 0);
693 a_f_vect[dimension < 3 ? s1 : som_arete[s1].at(s2)].push_back(f);
694 }
695 int a_f_max = 0;
696 for (int i = 0; i < (int) a_f_vect.size(); i++) a_f_max = std::max(a_f_max, (int) a_f_vect[i].size());
697 arete_faces_.resize((int)a_f_vect.size(), a_f_max), arete_faces_ = -1;
698 for (int i = 0, j; i < arete_faces_.dimension(0); i++)
699 for (j = 0; j < (int) a_f_vect[i].size(); j++) arete_faces_(i, j) = a_f_vect[i][j];
700
701 dimension < 3 ? init_we_2d() : init_we_3d();
702 is_init["we"] = 1;
703}
704
706{
707 const IntTab& e_f = elem_faces(), &f_s = face_sommets_;
708 const DoubleTab& xs = domaine().coord_sommets();
709 const DoubleVect& ve = volumes();
710 int i, j, e, f, s;
711
712 wedeb.resize(1);
713 std::map<int, double> wemi;
714 for (e = 0; e < nb_elem_tot(); wedeb.append_line(weji.dimension(0)), wemi.clear(), e++)
715 {
716 //une contribution par "facette" (triangle entre le sommet, le CG de la face et celui de l'element)
717 for (i = 0; i < e_f.dimension(1) && (f = e_f(e, i)) >= 0; i++)
718 for (j = 0; j < f_s.dimension(1) && (s = f_s(f, j)) >= 0; j++) //contrib. de chaque sommet de chaque face
719 wemi[s] += std::fabs(cross(2, 2, &xp_(e, 0), &xv_(f, 0), &xs(s, 0), &xs(s, 0))[2]) / (2 * ve(e));
720 for (auto &&kv : wemi) weji.append_line(kv.first), weci.append_line(kv.second);
721 }
722 CRIMP(wedeb), CRIMP(weji), CRIMP(weci);
723}
724
726{
727 const IntTab& e_f = elem_faces(), &f_s = face_sommets_;
728 const DoubleVect& la = longueur_aretes_, &ve = volumes_;
729 int i, j, k, e, f, s;
730
731 wedeb.resize(1);
732 weci.resize(0, 3);
733 std::map<int, std::array<double, 3> > wemi;
734 for (e = 0; e < nb_elem_tot(); wedeb.append_line(weji.dimension(0)), wemi.clear(), e++)
735 {
736 //une contribution par "facette" (triangle entre CG de l'arete, le CG de la face et celui de l'element)
737 for (i = 0; i < e_f.dimension(1) && (f = e_f(e, i)) >= 0; i++)
738 for (j = 0; j < f_s.dimension(1) && (s = f_s(f, j)) >= 0; j++) //contrib. de chaque sommet de chaque face
739 {
740 int s2 = f_s(f, j + 1 < f_s.dimension(1) && f_s(f, j + 1) >= 0 ? j + 1 : 0),
741 a = som_arete[s].at(s2);
742 std::array<double, 3> vec = cross(3, 3, &xp_(e, 0), &xv_(f, 0), &xa_(a, 0), &xa_(a, 0));
743 //on tourne la facette dans la bonne direction
744 int sgn = dot(&ta_(a, 0), &vec[0]) > 0 ? 1 : -1;
745 for (k = 0; k < 3; k++) wemi[a][k] += sgn * vec[k] * la(a) / (2 * ve(e));
746 }
747 for (auto &&kv : wemi) weji.append_line(kv.first), weci.append_line(kv.second[0], kv.second[1], kv.second[2]);
748 }
749 CRIMP(wedeb), CRIMP(weji), CRIMP(weci);
750}
751
752//matrice mimetique d'un champ aux aretes : (valeur tangente aux aretes) -> (flux a travers l'union des facettes touchant l'arete)
753//en 2D, m1 est toujours diagonale! Il suffit de calculer la surface de l'arete duale...
755{
756 const IntTab& e_f = elem_faces(), &f_s = face_sommets_;
757 const DoubleTab& xs = domaine().coord_sommets();
758 int i, j, e, f, s;
759
760 std::vector<std::map<std::array<int, 2>, double>> m1(domaine().nb_som_tot()); //m1[a][{ ab, e }] : contribution de (arete ab, element e)
761 for (e = 0; e < nb_elem_tot(); e++)
762 {
763 //une contribution par "facette" (triangle entre le sommet, le CG de la face et celui de l'element)
764 for (i = 0; i < e_f.dimension(1) && (f = e_f(e, i)) >= 0; i++)
765 for (j = 0; j < f_s.dimension(1) && (s = f_s(f, j)) >= 0; j++) //contrib. de chaque sommet de chaque face
766 m1[s][ {{ s, e }}] += std::fabs(cross(2, 2, &xp_(e, 0), &xv_(f, 0), &xs(s, 0), &xs(s, 0))[2]) / 2;
767 }
768
769 //remplissage
770 m1deb.resize(1);
771 m1ji.resize(0, 2);
772 for (i = 0; i < domaine().nb_som_tot(); m1deb.append_line(m1ji.dimension(0)), i++)
773 for (auto &&kv : m1[i])
774 m1ji.append_line(kv.first[0], kv.first[1]), m1ci.append_line(kv.second);
775 CRIMP(m1deb), CRIMP(m1ji), CRIMP(m1ci);
776}
777
778//en 3D, c'est moins trivial...
780{
781 const IntTab& f_s = face_sommets_, &e_a = domaine().elem_aretes(), &e_f = elem_faces_;
782 const DoubleVect& la = longueur_aretes_, &ve = volumes();
783 int a, ab, i, j, k, e, f, s, s2, n_a;
784
785 Cerr << domaine().le_nom() << " : initialisation de m1... ";
786 DoubleTab M, N;
787
788 /* tableau parallele */
789 DoubleTrav m1e(0, e_a.dimension(1), e_a.dimension(1));
791 std::map<int, int> idxa;
792 for (e = 0; e < nb_elem(); e++)
793 {
794 /* matrice non stabilisee : contribution par facette (couple face/arete) */
795 for (i = 0, idxa.clear(), n_a = 0; i < e_a.dimension(1) && (a = e_a(e, i)) >= 0; i++) idxa[a] = i, n_a++;
796 M.resize(n_a, n_a), N.resize(dimension, n_a);
797 for (i = 0, M = 0; i < e_f.dimension(1) && (f = e_f(e, i)) >= 0; i++)
798 for (j = 0; j < f_s.dimension(1) && (s = f_s(f, j)) >= 0; j++)
799 {
800 s2 = f_s(f, j + 1 < f_s.dimension(1) && f_s(f, j + 1) >= 0 ? j + 1 : 0), a = som_arete[s].at(s2);
801 std::array<double, 3> vec = cross(3, 3, &xp_(e, 0), &xv_(f, 0), &xa_(a, 0), &xa_(a, 0));
802 //on tourne la facette dans la bonne direction
803 int sgn = dot(&ta_(a, 0), &vec[0]) > 0 ? 1 : -1;
804 for (k = wedeb(e); k < wedeb(e + 1); k++) M(idxa[a], idxa[weji(k)]) += sgn * la(a) * dot(&vec[0], &weci(k, 0)) / 2 / ve(e);
805 }
806 for (i = 0; i < e_a.dimension(1) && (a = e_a(e, i)) >= 0; i++)
807 for (j = 0; j < dimension; j++) N(j, i) = ta_(a, j);
808
809 /* stabilisation et stockage */
811 for (i = 0; i < n_a; i++)
812 for (j = 0; j < n_a; j++) m1e(e, i, j) = M(i, j);
813 }
814
815 /* echange et remplissage d'un map global */
817 std::vector<std::map<std::array<int, 2>, double>> m1(domaine().nb_aretes_tot()); //m1[a][{ ab, e }] : contribution de (arete ab, element e)
818 for (e = 0; e < nb_elem_tot(); e++)
819 for (i = 0; i < e_a.dimension(1) && (a = e_a(e, i)) >= 0; i++)
820 for (j = 0; j < e_a.dimension(1) && (ab = e_a(e, j)) >= 0; j++)
821 if (std::fabs(m1e(e, i, j)) > 1e-8) m1[a][ {{ ab, e }}] += ve(e) * m1e(e, i, j);
822
823 /* remplissage final */
824 m1deb.resize(1);
825 m1ji.resize(0, 2);
826 for (a = 0; a < domaine().nb_aretes_tot(); m1deb.append_line(m1ji.dimension(0)), a++)
827 for (auto &&kv : m1[a])
828 m1ji.append_line(kv.first[0], kv.first[1]), m1ci.append_line(kv.second);
829 CRIMP(m1deb), CRIMP(m1ji), CRIMP(m1ci);
831 Cerr << "OK" << finl;
832}
833
835{
836 if (is_init["m1"]) return;
837 init_we();
838 dimension < 3 ? init_m1_2d() : init_m1_3d();
839 is_init["m1"] = 1;
840}
841
842/* initisalisation de solveurs lineaires pour inverser m1 ou m2 */
844{
845 init_m2();
846 if (is_init["m2solv"]) return;
847 /* stencil et allocation */
848 const IntTab& e_f = elem_faces(), &f_e = face_voisins();
849 Stencil stencil(0, 2);
850 int e, i, j, k, f, fb;
851 for (e = 0; e < nb_elem_tot(); e++)
852 for (i = 0, j = m2d(e); j < m2d(e + 1); i++, j++)
853 for (f = e_f(e, i), k = m2i(j); f < nb_faces() && k < m2i(j + 1); k++)
854 if (f <= (fb = e_f(e, m2j(k))))
855 stencil.append_line(f, fb);
856 tableau_trier_retirer_doublons(stencil);
858
859 /* remplissage */
860 for (e = 0; e < nb_elem_tot(); e++)
861 for (i = 0, j = m2d(e); j < m2d(e + 1); i++, j++)
862 for (f = e_f(e, i), k = m2i(j); f < nb_faces() && k < m2i(j + 1); k++)
863 if (f <= (fb = e_f(e, m2j(k))))
864 m2mat(f, fb) += (e == f_e(f, 0) ? 1 : -1) * (e == f_e(fb, 0) ? 1 : -1) * volumes(e) * m2c(k);
865 m2mat.set_est_definie(1);
866
867 char lu[] = "Petsc Cholesky { quiet }";
868 EChaine ch(lu);
869 ch >> m2solv;
870 is_init["m2solv"] = 1;
871}
872
874{
875 if (is_init["virt_ef"]) return; //deja initialisa
876 int e, f;
877 IntTrav p_e(0, 2), p_f(0, 2);
879 for (e = 0; e < nb_elem() ; e++) p_e(e, 0) = Process::me(), p_e(e, 1) = e;
880 for (f = 0; f < nb_faces(); f++) p_f(f, 0) = Process::me(), p_f(f, 1) = nb_elem_tot() + f;
882 for (e = nb_elem() ; e < nb_elem_tot() ; e++) virt_ef_map[ {{ p_e(e, 0), p_e(e, 1) }}] = e;
883 for (f = nb_faces(); f < nb_faces_tot(); f++) virt_ef_map[ {{ p_f(f, 0), p_f(f, 1) }}] = nb_elem_tot() + f;
884 is_init["virt_ef"] = 1;
885}
886
887
888/* "clamping" a 0 des coeffs petits dans M1/W1/M2/W2 */
889inline void clamp(DoubleTab& m)
890{
891 for (int i = 0; i < m.dimension(0); i++)
892 for (int j = 0; j < m.dimension(1); j++)
893 for (int n = 0; n < m.dimension(2); n++)
894 if (1e6 * std::abs(m(i, j, n)) < std::abs(m(i, i, n)) + std::abs(m(j, j, n))) m(i, j, n) = 0;
895}
896
897//matrices locales par elements (operateurs de Hodge) permettant de faire des interpolations :
898//normales aux faces -> tangentes aux faces duales : (nu x_ef.v) = m2 (|f|n_ef.v)
899void Domaine_PolyMAC_CDO::M2(const DoubleTab *nu, int e, DoubleTab& m2) const
900{
901 int i, j, k, f, n, N = nu ? nu->dimension(1) : 1, e_nu = nu && nu->dimension_tot(0) == 1 ? 0 : e, n_f, d, D = dimension;
902 const IntTab& e_f = elem_faces(), &f_e = face_voisins();
903 const DoubleTab& xe = xp(), &xf = xv(), &nf = face_normales();
904 const DoubleVect& ve = volumes();
905 for (n_f = 0; n_f < e_f.dimension(1) && e_f(e, n_f) >= 0; ) n_f++; //nombre de faces de e
906 double prefac, fac, beta = n_f == D + 1 ? 1. / D : D == 2 ? 1. / sqrt(2) : 1. / sqrt(3); //stabilisation : DGA sur simplexes, SUSHI sinon
907 m2.resize(n_f, n_f, N), m2 = 0;
908 DoubleTrav v_e(n_f, D), v_ef(n_f, n_f, D); //interpolations du vecteur complet : non stabilisee en e, stabilisee en (e, f)
909 for (i = 0; i < n_f; i++)
910 for (f = e_f(e, i), d = 0; d < D; d++) v_e(i, d) = (xf(f, d) - xe(e, d)) / ve(e);
911 for (i = 0; i < n_f; i++)
912 for (f = e_f(e, i), prefac = D * beta / std::abs(dot(&xf(f, 0), &nf(f, 0), &xe(e, 0))), j = 0; j < n_f; j++)
913 for (fac = prefac * ((j == i) - (e == f_e(f, 0) ? 1 : -1) * dot(&nf(f, 0), &v_e(j, 0))), d = 0; d < D; d++)
914 v_ef(i, j, d) = v_e(j, d) + fac * (xf(f, d) - xe(e, d));
915 //matrice!
916 for (m2 = 0, i = 0; i < n_f; i++)
917 for (j = 0; j < n_f; j++)
918 if (j < i)
919 for (n = 0; n < N; n++) m2(i, j, n) = m2(j, i, n); //sous la diagonale -> avec l'autre cote
920 else for (k = 0; k < n_f; k++)
921 for (f = e_f(e, k), fac = std::abs(dot(&xf(f, 0), &nf(f, 0), &xe(e, 0))) / D, n = 0; n < N; n++)
922 m2(i, j, n) += fac * nu_dot(nu, e_nu, n, &v_ef(k, i, 0), &v_ef(k, j, 0));
923 clamp(m2);
924}
925
926//tangentes aux faces duales -> normales aux faces : nu|f|n_ef.v = w2.(x_ef.v)
927void Domaine_PolyMAC_CDO::W2(const DoubleTab *nu, int e, DoubleTab& w2) const
928{
929 int i, j, k, f, n, N = nu ? nu->dimension(1) : 1, e_nu = nu && nu->dimension_tot(0) == 1 ? 0 : e, n_f, d, D = dimension;
930 const IntTab& e_f = elem_faces(), &f_e = face_voisins();
931 const DoubleTab& xe = xp(), &xf = xv(), &nf = face_normales();
932 const DoubleVect& ve = volumes();
933 for (n_f = 0; n_f < e_f.dimension(1) && e_f(e, n_f) >= 0; ) n_f++; //nombre de faces de e
934 double prefac, fac, beta = n_f == D + 1 ? 1. / D : D == 2 ? 1. / sqrt(2) : 1. / sqrt(3); //stabilisation : DGA sur simplexes, SUSHI sinon
935 w2.resize(n_f, n_f, N), w2 = 0;
936 DoubleTrav v_e(n_f, D), v_ef(n_f, n_f, D); //interpolations du vecteur complet : non stabilisee en e, stabilisee en (e, f)
937 for (i = 0; i < n_f; i++)
938 for (f = e_f(e, i), d = 0; d < D; d++) v_e(i, d) = (e == f_e(f, 0) ? 1 : -1) * nf(f, d) / ve(e);
939 for (i = 0; i < n_f; i++)
940 for (f = e_f(e, i), prefac = D * beta * (e == f_e(f, 0) ? 1 : -1) / std::abs(dot(&xf(f, 0), &nf(f, 0), &xe(e, 0))), j = 0; j < n_f; j++)
941 for (fac = prefac * ((j == i) - dot(&xf(f, 0), &v_e(j, 0), &xe(e, 0))), d = 0; d < D; d++)
942 v_ef(i, j, d) = v_e(j, d) + fac * nf(f, d);
943 //matrice!
944 for (i = 0; i < n_f; i++)
945 for (j = 0; j < n_f; j++)
946 if (j < i)
947 for (n = 0; n < N; n++) w2(i, j, n) = w2(j, i, n); //sous-diagonale -> on copie l'autre cote
948 else for (k = 0; k < n_f; k++)
949 for (f = e_f(e, k), fac = std::abs(dot(&xf(f, 0), &nf(f, 0), &xe(e, 0))) / D, n = 0; n < N; n++)
950 w2(i, j, n) += fac * nu_dot(nu, e_nu, n, &v_ef(k, i, 0), &v_ef(k, j, 0));
951 clamp(w2);
952}
953
classe Cond_lim_base Classe de base pour la hierarchie des classes qui representent les differentes c...
virtual Frontiere_dis_base & frontiere_dis()
Renvoie la frontiere discretisee a laquelle les conditions aux limites s'appliquent.
classe Conds_lim Cette classe represente un vecteur de conditions aux limites.
Definition Conds_lim.h:32
int_t nb_aretes_tot() const
renvoie le nombre d'aretes total (reelles+virtuelles).
Definition Domaine.h:145
virtual void creer_tableau_elements(Array_base &, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT) const
creation d'un tableau parallele de valeurs aux elements.
Definition Domaine.cpp:851
int nb_faces_elem(int=0) const
Renvoie le nombre de face de type i des elements geometriques constituants le domaine.
Definition Domaine.h:484
const DoubleTab_t & coord_sommets() const
Definition Domaine.h:112
int_t elem_aretes(int_t i, int j) const
renvoie le numero de la jeme arete du ieme element.
Definition Domaine.h:154
int_t nb_som_tot() const
Renvoie le nombre total de sommets du domaine i.e. le nombre de sommets reels et virtuels sur le proc...
Definition Domaine.h:123
void W2(const DoubleTab *nu, int e, DoubleTab &w2) const
int W_stabiliser(DoubleTab &W, DoubleTab &R, DoubleTab &N, int *ctr, double *spectre) const
double dot(const double *a, const double *b, const double *ma=nullptr, const double *mb=nullptr) const
void ajouter_stabilisation(DoubleTab &M, DoubleTab &N) const
void calculer_volumes_entrelaces() override
std::map< std::string, int > is_init
Matrice_Morse_Sym m2mat
void M2(const DoubleTab *nu, int e, DoubleTab &m2) const
void init_equiv() const override
std::map< std::array< int, 2 >, int > virt_ef_map
void modifier_pour_Cl(const Conds_lim &) override
void calculer_h_carre() override
class Domaine_Poly_base
const Elem_poly_base & type_elem() const
const DoubleVect & longueur_aretes() const
std::vector< std::map< int, int > > som_arete
void discretiser() override
double nu_dot(const DoubleTab *nu, int e, int n, const double *a, const double *b, const double *ma=nullptr, const double *mb=nullptr) const
virtual const DoubleVect & face_surfaces() const
Definition Domaine_VF.h:51
int nb_faces() const
renvoie le nombre global de faces.
Definition Domaine_VF.h:471
DoubleTab xp_
Definition Domaine_VF.h:218
IntTab & face_sommets() override
renvoie le tableau de connectivite faces/sommets.
Definition Domaine_VF.h:591
DoubleVect volumes_entrelaces_
Definition Domaine_VF.h:210
DoubleTab xa_
Definition Domaine_VF.h:225
int nb_faces_tot() const
renvoie le nombre total de faces.
Definition Domaine_VF.h:481
void creer_tableau_faces(Array_base &, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT) const
DoubleTab xv_
Definition Domaine_VF.h:219
IntTab face_sommets_
Definition Domaine_VF.h:223
DoubleTab & xv()
Definition Domaine_VF.h:93
IntTab & elem_faces()
renvoie le tableau de connectivite element/faces
Definition Domaine_VF.h:551
DoubleVect volumes_
Definition Domaine_VF.h:208
virtual const IntVect & orientation() const
Definition Domaine_VF.h:646
IntTab face_voisins_
Definition Domaine_VF.h:216
IntTab elem_faces_
Definition Domaine_VF.h:222
DoubleVect & volumes()
Definition Domaine_VF.h:119
std::array< double, 3 > cross(int dima, int dimb, const double *a, const double *b, const double *ma=nullptr, const double *mb=nullptr) const
Definition Domaine_VF.h:711
DoubleTab face_normales_
Definition Domaine_VF.h:212
DoubleTab volumes_entrelaces_dir_
Definition Domaine_VF.h:211
DoubleTab & xp()
Definition Domaine_VF.h:95
virtual DoubleTab & face_normales()
Definition Domaine_VF.h:48
IntTab & face_voisins() override
renvoie le tableaux des volumes des connectivites face elements cf au dessus.
Definition Domaine_VF.h:426
void marquer_faces_double_contrib(const Conds_lim &)
const Nom & le_nom() const override
Donne le nom de l'Objet_U Methode a surcharger : renvoie "neant" dans cette implementation.
int nb_elem_tot() const
const Domaine & domaine() const
int nb_som_tot() const
Une entree dont la source est une chaine de caracteres.
Definition EChaine.h:31
Class defining operators and methods for all reading operation in an input flow (file,...
Definition Entree.h:42
class Front_VF
Definition Front_VF.h:36
int nb_faces() const
Definition Front_VF.h:53
int num_premiere_face() const
Definition Front_VF.h:63
int nb_faces_tot() const
Definition Front_VF.h:58
int num_face(const int) const
Definition Front_VF.h:68
une matrice 3x3.
Definition Matrice33.h:28
static double inverse(const Matrice33 &m, Matrice33 &resu, int exit_on_error=1)
calcul de l'inverse.
static void allocate_symmetric_morse_matrix(const int order, const Stencil &stencil, Matrice_Morse_Sym &matrix)
static int dimension
Definition Objet_U.h:99
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
classe Periodique Cette classe represente une condition aux limites periodique.
Definition Periodique.h:31
int face_associee(int i) const
Definition Periodique.h:35
static double mp_min(double)
Definition Process.cpp:386
static double mp_max(double)
Definition Process.cpp:376
static Sortie & Journal(int message_level=0)
Renvoie un objet statique de type Sortie qui sert de journal d'evenements.
Definition Process.cpp:588
static double mp_sum_as_double(int v)
Definition Process.h:99
static void barrier()
Synchronise tous les processeurs du groupe courant (attend que tous les processeurs soient arrives a ...
Definition Process.cpp:136
static int me()
renvoie mon rang dans le groupe de communication courant.
Definition Process.cpp:125
static void exit(int exit_code=-1)
Routine de sortie de TRUST dans une region Kokkos.
Definition Process.cpp:455
Classe de base des flux de sortie.
Definition Sortie.h:52
_TYPE_ * addr()
void resize(_SIZE_ n, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
Definition TRUSTTab.tpp:469
_SIZE_ dimension_tot(int) const override
Definition TRUSTTab.tpp:160
void append_line(_TYPE_)
Definition TRUSTTab.tpp:213
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133
virtual void echange_espace_virtuel(IsExchangeBlocking exchange_type=IsExchangeBlocking::DefaultBlocking, const std::string kernel_name="noname")