TrioCFD 1.9.9_beta
TrioCFD documentation
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 // Compute h_carre
74 h_carre = 1.e30;
75 h_carre_.resize(nb_faces());
76 // Compute the face 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() << " : initializing 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; //collinear normals?
142
143 // XXX Elie Saikali
144 // Options to force computation of the equiv array
145 // because the test fails for hex, conforming, non-uniform meshes
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; // for 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 // check if f1 and f2 share the same element
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 identical?
184 }
185 else
186 {
187 std::array<double, 3> v1 = cross(D, D, &xv_(f1, 0), &nf(f1, 0), &xp_(e1, 0));//cross product (xs - xf)xnf
188 std::array<double, 3> v2 = cross(D, D, &xv_(f2, 0), &nf(f2, 0), &xp_(e2, 0));//cross product (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 : same as before for now */ : 1e-12); //xv - xp identical?
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)++; //if so, equivalence is found
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 has been filled successfully" << 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 of face_voisins_, face_normales_, volumes_entrelaces_ arrays
242 // Change the orientation of certain normals
243 // so that the normals at periodic faces are oriented
244 // from face_voisins(la_face_en_question,0) toward face_voisins(la_face_en_question,1)
245 // just like internal faces
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 : periodic faces have double contribution,
290 // so the call to marquer_faces_double_contrib is done in this method
291 // in order to benefit from conds_lim.
293}
294
295//stabilization of the m1 and m2 matrices of 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 /* spectrum of M */
302 kersol(M, b, 1e-12, nullptr, x, S);
303 double l_max = S(0), l_min = S(dimension - 1); //smallest eigenvalue without stabilization
304
305 /* D : kernel of N (N.D = 0), of size 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 must have rank d
308
309 /* matrix U such that M + D.U.Dt minimizes the off-diagonal terms */
310 //one row per coefficient M(i, j > i), one column per term 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) -> row index
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) -> column index
317 A(i, j) = D(i1, j1) * D(i2, j2) + (j1 != j2) * D(i1, j2) * D(i2, j1);
318 char trans = 'T';
319 //minimize the sum of squares of the off-diagonal terms of 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); //actual call
323 assert(infoo == 0);
324 //reconstruct 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 /* adjust U so that the minimum eigenvalue exceeds eps */
329 //Schur decomposition 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 //to guarantee eigenvalues larger than 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 /* add to 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/* find a matrix W satisfying W.R = N with sum_{i!=j} w_{ij}^2 minimal and an acceptable spectrum */
350/* on input, W contains the admissible stencil */
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 : index of the unknown W(i, j) in the optimization problem */
356 //if NtR is symmetric, then we seek a symmetric W
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 //fill idx(i, j): index of W_{ij} in the optimization problem
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 /* non-stabilized version: W0 = N.(NtR)^-1.Nt */
372 Matrice33::inverse(NtR, iNtR); //crash if NtR is not invertible
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 //spectrum of Ws (symmetric part of 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);//actual call
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 //bounds on the spectrum of the final matrix
388 double l_min = S(n_f - dimension) / 100, l_max = S(n_f - 1) * 100;
389
390 /* optimization problem: over the allowed W_{ij} */
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 /* constraint: W.R = N */
397 std::vector<std::map<int, double>> C(nv); //CSC storage: C[j][i] = M_{ij}
398 std::vector<double> lb, ub; //lower/upper bounds
399 int il = 0; //track the current row being built
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 /* objective: minimize the l2 norm of the off-diagonal terms */
406 std::vector<int> P_c(nv + 1), P_l(nv), A_c, A_l; //coefficients of column c: indices [P_c(c), P_c(c + 1)[ in 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: solve and add constraints as long as W has a spectrum outside [l_min, l_max]
415 std::fenv_t fenv;
416 std::feholdexcept(&fenv); //suspend floating-point exceptions
417 std::vector<double> sol(nv);
418 for (cv = 0, it = 0; !cv && it < 100; it++)
419 {
420 /* assemble A: constraint matrix */
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 /* solve */
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()); //warm-start from the previous iteration
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 /* compute the spectrum: Ws retrieves the eigenvectors */
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 /* for each eigenvalue outside [ l_min, l_max ], add a constraint to restrict it to [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++; //restart with one additional row
454 }
455 osqp_cleanup(osqp), free(data.A);
456 }
457 free(data.P);
458 std::fesetenv(&fenv); //restore floating-point exceptions
459
460 if (!cv)
461 {
462 Cerr << "Non-stabilized matrix: warning, the mesh is too deformed!!" << finl;
463 return 0; //pass or fail
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 //statistics
469 //ctr[0]: diagonal, ctr[1]: symmetric
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 }; //min eigenvalue (consistent, stabilized), max eigenvalue (consistent, stabilized)
482
483 if (is_init["w2"]) return;
484 Cerr << domaine().le_nom() << " : initializing w2/m2 ... ";
485
486 DoubleTab W, M;
487
488 /* for sharing between processes */
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 /* computation on real elements */
494 //std::map<int, std::vector<int>> som_face; //som_face[s]: faces of element e touching vertex s
495 IntTrav nnz, nef;//per element: number of nonzero coefficients, number of faces (rows)
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 /* exchange and fill */
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 }; //min eigenvalue (consistent, stabilized), max eigenvalue (consistent, stabilized)
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 /* for sharing between processes */
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 /* computation on real elements */
567 std::map<int, std::vector<int>> som_face; //som_face[s]: faces of element e touching vertex s
568 IntTrav nnz, nef;//per element: number of nonzero coefficients, number of faces (rows)
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 (rows: elements -> faces) and N (outward normals at 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 connected to each vertex, then admissible stencil */
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 /* stabilized W2 matrix */
588 W = 1, W_stabiliser(W, R, N, ctr, spectre); /* a complete matrix is required */
589
590 /* M2 matrix: 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 /* exchange and fill */
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//mimetic matrix W_2/M_2: tangential values along element-face lines <-> normal values at faces
628{
629 if (Option_PolyMAC_family::USE_NEW_M2) init_m2_new();
630 else init_m2_osqp();
631}
632
633
634//first-order interpolation of face normals to elements
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 //formula (1) from Basumatary et al. (2014) https://doi.org/10.1016/j.jcp.2014.04.033 following 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//curl at faces of a field tangent to edges
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; //tangent vector to the edge and its cross product with 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 at elements of a field whose tangential component at edges (in 3D) or vertical component at vertices (in 2D) is known
683{
684 if (is_init["we"]) return;
685
686 //fill arete_faces_ (list of faces touching each edge in 3D, each vertex in 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 //one contribution per "facet" (triangle between the vertex, the face centroid, and the element centroid)
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. from each vertex of each 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 //one contribution per "facet" (triangle between the edge centroid, the face centroid, and the element centroid)
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. from each vertex of each 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 //orient the facet in the correct 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//mimetic matrix of an edge field: (tangential value at edges) -> (flux through the union of facets touching the edge)
753//in 2D, m1 is always diagonal! It suffices to compute the area of the dual edge...
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 from (edge ab, element e)
761 for (e = 0; e < nb_elem_tot(); e++)
762 {
763 //one contribution per "facet" (triangle between the vertex, the face centroid, and the element centroid)
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. from each vertex of each 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 //fill
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//in 3D, this is less 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() << " : initializing m1... ";
786 DoubleTab M, N;
787
788 /* parallel array */
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 /* non-stabilized matrix: contribution per facet (face/edge pair) */
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 //orient the facet in the correct 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 /* stabilization and storage */
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 /* exchange and fill a global map */
817 std::vector<std::map<std::array<int, 2>, double>> m1(domaine().nb_aretes_tot()); //m1[a][{ ab, e }]: contribution from (edge 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 /* final fill */
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/* initialization of linear solvers to invert m1 or m2 */
844{
845 init_m2();
846 if (is_init["m2solv"]) return;
847 /* stencil and 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 /* fill */
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; //already initialized
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" to 0 of small coefficients in 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//local matrices per element (Hodge operators) for performing interpolations:
898//face normals -> dual face tangentials: (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++; //number of faces of e
906 double prefac, fac, beta = n_f == D + 1 ? 1. / D : D == 2 ? 1. / sqrt(2) : 1. / sqrt(3); //stabilization: DGA on simplices, SUSHI otherwise
907 m2.resize(n_f, n_f, N), m2 = 0;
908 DoubleTrav v_e(n_f, D), v_ef(n_f, n_f, D); //full vector interpolations: non-stabilized at e, stabilized at (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 //matrix!
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); //below diagonal -> copy from the other side
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//dual face tangentials -> face normals: 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++; //number of faces of e
934 double prefac, fac, beta = n_f == D + 1 ? 1. / D : D == 2 ? 1. / sqrt(2) : 1. / sqrt(3); //stabilization: DGA on simplices, SUSHI otherwise
935 w2.resize(n_f, n_f, N), w2 = 0;
936 DoubleTrav v_e(n_f, D), v_ef(n_f, n_f, D); //full vector interpolations: non-stabilized at e, stabilized at (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 //matrix!
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); //below diagonal -> copy from the other side
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
class Cond_lim_base Base class for the hierarchy of classes that represent the different boundary con...
virtual Frontiere_dis_base & frontiere_dis()
Returns the discretized boundary to which the boundary conditions apply.
class Conds_lim This class represents a vector of boundary conditions.
Definition Conds_lim.h:32
int_t nb_aretes_tot() const
returns the total number of edges (real+virtual).
Definition Domaine.h:145
virtual void creer_tableau_elements(Array_base &, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT) const
Creates a parallel array of values at elements.
Definition Domaine.cpp:850
int nb_faces_elem(int=0) const
Returns the number of faces of type i of the geometric elements that make up the domain.
Definition Domaine.h:484
const DoubleTab_t & coord_sommets() const
Definition Domaine.h:112
int_t elem_aretes(int_t i, int j) const
returns the number of the j-th edge of the i-th element.
Definition Domaine.h:154
int_t nb_som_tot() const
Returns the total number of vertices of the domain i.e. the number of real and virtual vertices on th...
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
Returns the total number of faces.
Definition Domaine_VF.h:471
DoubleTab xp_
Definition Domaine_VF.h:218
IntTab & face_sommets() override
Returns the face/vertex connectivity array.
Definition Domaine_VF.h:590
DoubleVect volumes_entrelaces_
Definition Domaine_VF.h:210
DoubleTab xa_
Definition Domaine_VF.h:225
int nb_faces_tot() const
Returns the total number of 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()
Returns the element/face connectivity array.
Definition Domaine_VF.h:550
DoubleVect volumes_
Definition Domaine_VF.h:208
virtual const IntVect & orientation() const
Definition Domaine_VF.h:645
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:710
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
Returns the face-element connectivity array; see above.
Definition Domaine_VF.h:426
void marquer_faces_double_contrib(const Conds_lim &)
const Nom & le_nom() const override
Returns the name of the Objet_U. Virtual method to override: returns "neant" in this implementation.
int nb_elem_tot() const
const Domaine & domaine() const
int nb_som_tot() const
An input stream whose source is a character string.
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
A 3x3 matrix.
Definition Matrice33.h:28
static double inverse(const Matrice33 &m, Matrice33 &resu, int exit_on_error=1)
Computes the inverse.
static void allocate_symmetric_morse_matrix(const int order, const Stencil &stencil, Matrice_Morse_Sym &matrix)
static int dimension
Definition Objet_U.h:94
const Nom & que_suis_je() const
Returns the string identifying the class.
Definition Objet_U.cpp:104
virtual Entree & readOn(Entree &)
Reads an Objet_U from an input stream. Virtual method to override.
Definition Objet_U.cpp:289
virtual Sortie & printOn(Sortie &) const
Writes the object to an output stream. Virtual method to override.
Definition Objet_U.cpp:278
class Periodique This class represents a periodic boundary condition.
Definition Periodique.h:31
int face_associee(int i) const
Definition Periodique.h:35
static double mp_min(double)
Definition Process.cpp:391
static double mp_max(double)
Definition Process.cpp:379
static Sortie & Journal(int message_level=0)
Returns a static Sortie object used as an event journal.
Definition Process.cpp:592
static double mp_sum_as_double(int v)
Definition Process.h:97
static void barrier()
Synchronizes all processors in the current group (waits until all processors have reached the barrier...
Definition Process.cpp:133
static int me()
Returns the rank of the local processor in the current communication group. See Comm_Group::rank() an...
Definition Process.cpp:122
static void exit(int exit_code=-1)
Exit routine for TRUST within a Kokkos region.
Definition Process.cpp:466
Base class for output streams.
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")