1 /*============================================================================
2  * Gradient reconstruction.
3  *============================================================================*/
4 
5 /*
6   This file is part of Code_Saturne, a general-purpose CFD tool.
7 
8   Copyright (C) 1998-2021 EDF S.A.
9 
10   This program is free software; you can redistribute it and/or modify it under
11   the terms of the GNU General Public License as published by the Free Software
12   Foundation; either version 2 of the License, or (at your option) any later
13   version.
14 
15   This program is distributed in the hope that it will be useful, but WITHOUT
16   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17   FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18   details.
19 
20   You should have received a copy of the GNU General Public License along with
21   this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
22   Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24 
25 /*----------------------------------------------------------------------------*/
26 
27 #include "cs_defs.h"
28 
29 /*----------------------------------------------------------------------------
30  * Standard C library headers
31  *----------------------------------------------------------------------------*/
32 
33 #include <assert.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdarg.h>
37 #include <string.h>
38 #include <math.h>
39 #include <float.h>
40 
41 #if defined(HAVE_MPI)
42 #include <mpi.h>
43 #endif
44 
45 /*----------------------------------------------------------------------------
46  *  Local headers
47  *----------------------------------------------------------------------------*/
48 
49 #include "bft_error.h"
50 #include "bft_mem.h"
51 #include "bft_printf.h"
52 
53 #include "cs_bad_cells_regularisation.h"
54 #include "cs_blas.h"
55 #include "cs_cell_to_vertex.h"
56 #include "cs_ext_neighborhood.h"
57 #include "cs_field.h"
58 #include "cs_field_pointer.h"
59 #include "cs_halo.h"
60 #include "cs_halo_perio.h"
61 #include "cs_internal_coupling.h"
62 #include "cs_log.h"
63 #include "cs_math.h"
64 #include "cs_mesh.h"
65 #include "cs_mesh_adjacencies.h"
66 #include "cs_mesh_quantities.h"
67 #include "cs_parall.h"
68 #include "cs_porous_model.h"
69 #include "cs_prototypes.h"
70 #include "cs_timer.h"
71 #include "cs_timer_stats.h"
72 
73 /*----------------------------------------------------------------------------
74  *  Header for the current file
75  *----------------------------------------------------------------------------*/
76 
77 #include "cs_gradient.h"
78 #include "cs_gradient_priv.h"
79 
80 /*----------------------------------------------------------------------------*/
81 
82 BEGIN_C_DECLS
83 
84 /*=============================================================================
85  * Additional Doxygen documentation
86  *============================================================================*/
87 
88 /*!
89  * \file cs_gradient.c
90  * \brief Gradient reconstruction.
91  *
92  * Please refer to the
93  * <a href="../../theory.pdf#gradreco"><b>gradient reconstruction</b></a>
94  * section of the theory guide for more informations.
95  */
96 
97 /*! \cond DOXYGEN_SHOULD_SKIP_THIS */
98 
99 /*=============================================================================
100  * Local macros
101  *============================================================================*/
102 
103 /* Cache line multiple, in cs_real_t units */
104 
105 #define CS_CL  (CS_CL_SIZE/8)
106 
107 /*=============================================================================
108  * Local type definitions
109  *============================================================================*/
110 
111 /* Structure associated to gradient quantities management */
112 
113 typedef struct {
114 
115   cs_real_33_t  *cocg_it;          /* Interleaved cocg matrix
116                                       for iterative gradients */
117 
118   cs_cocg_6_t   *cocgb_s_lsq;      /* coupling of gradient components for
119                                       least-square reconstruction at boundary */
120   cs_cocg_6_t   *cocg_lsq;         /* Interleaved cocg matrix
121                                       for least square gradients */
122 
123   cs_cocg_6_t   *cocgb_s_lsq_ext;  /* coupling of gradient components for
124                                       least-square reconstruction at boundary */
125   cs_cocg_6_t   *cocg_lsq_ext;     /* Interleaved cocg matrix for least
126                                       squares gradients with ext. neighbors */
127 
128 } cs_gradient_quantities_t;
129 
130 /* Basic per gradient computation options and logging */
131 /*----------------------------------------------------*/
132 
133 typedef struct _cs_gradient_info_t {
134 
135   char                *name;               /* System name */
136   cs_gradient_type_t   type;               /* Gradient type */
137 
138   unsigned             n_calls;            /* Number of times system solved */
139 
140   int                  n_iter_min;         /* Minimum number of iterations */
141   int                  n_iter_max;         /* Minimum number of iterations */
142   unsigned long        n_iter_tot;         /* Total number of iterations */
143 
144   cs_timer_counter_t   t_tot;              /* Total time used */
145 
146 } cs_gradient_info_t;
147 
148 /*============================================================================
149  *  Global variables
150  *============================================================================*/
151 
152 static int cs_glob_gradient_n_systems = 0;      /* Current number of systems */
153 static int cs_glob_gradient_n_max_systems = 0;  /* Max. number of systems for
154                                                    cs_glob_gradient_systems. */
155 
156 /* System info array */
157 static cs_gradient_info_t **cs_glob_gradient_systems = NULL;
158 
159 /* Short names for gradient computation types */
160 
161 const char *cs_gradient_type_name[]
162   = {N_("Green-Gauss, iterative handling of non-orthogonalities"),
163      N_("Least-squares"),
164      N_("Green-Gauss, least-squares gradient face values"),
165      N_("Green-Gauss, vertex-based face interpolation")};
166 
167 /* Timer statistics */
168 
169 static cs_timer_counter_t   _gradient_t_tot;     /* Total time in gradients */
170 static int _gradient_stat_id = -1;
171 
172 /* Gradient quantities */
173 
174 static int                        _n_gradient_quantities = 0;
175 static cs_gradient_quantities_t  *_gradient_quantities = NULL;
176 
177 /*============================================================================
178  * Prototypes for functions intended for use only by Fortran wrappers.
179  * (descriptions follow, with function bodies).
180  *============================================================================*/
181 
182 void
183 cs_f_gradient_s(int               f_id,
184                 int               imrgra,
185                 int               inc,
186                 int               iccocg,
187                 int               n_r_sweeps,
188                 int               iwarnp,
189                 int               imligp,
190                 cs_real_t         epsrgp,
191                 cs_real_t         climgp,
192                 const cs_real_t   coefap[],
193                 const cs_real_t   coefbp[],
194                 cs_real_t         pvar[],
195                 cs_real_3_t       grad[]);
196 
197 void
198 cs_f_gradient_potential(int               f_id,
199                         int               imrgra,
200                         int               inc,
201                         int               iccocg,
202                         int               n_r_sweeps,
203                         int               iphydp,
204                         int               iwarnp,
205                         int               imligp,
206                         cs_real_t         epsrgp,
207                         cs_real_t         climgp,
208                         cs_real_3_t       f_ext[],
209                         const cs_real_t   coefap[],
210                         const cs_real_t   coefbp[],
211                         cs_real_t         pvar[],
212                         cs_real_3_t       grad[]);
213 
214 void
215 cs_f_gradient_weighted_s(int               f_id,
216                          int               imrgra,
217                          int               inc,
218                          int               iccocg,
219                          int               n_r_sweeps,
220                          int               iphydp,
221                          int               iwarnp,
222                          int               imligp,
223                          cs_real_t         epsrgp,
224                          cs_real_t         climgp,
225                          cs_real_3_t       f_ext[],
226                          const cs_real_t   coefap[],
227                          const cs_real_t   coefbp[],
228                          cs_real_t         pvar[],
229                          cs_real_t         c_weight[],
230                          cs_real_3_t       grad[]);
231 
232 /*============================================================================
233  * Private function definitions
234  *============================================================================*/
235 
236 /*----------------------------------------------------------------------------*/
237 /*!
238  * \brief  Inverse a 3x3 symmetric matrix (with symmetric storage)
239  *         in place, using Cramer's rule
240  *
241  * \param[in, out]  a   matrix to inverse
242  */
243 /*----------------------------------------------------------------------------*/
244 
245 static inline void
_math_6_inv_cramer_sym_in_place(cs_cocg_t a[6])246 _math_6_inv_cramer_sym_in_place(cs_cocg_t  a[6])
247 {
248   cs_real_t a00 = a[1]*a[2] - a[4]*a[4];
249   cs_real_t a01 = a[4]*a[5] - a[3]*a[2];
250   cs_real_t a02 = a[3]*a[4] - a[1]*a[5];
251   cs_real_t a11 = a[0]*a[2] - a[5]*a[5];
252   cs_real_t a12 = a[3]*a[5] - a[0]*a[4];
253   cs_real_t a22 = a[0]*a[1] - a[3]*a[3];
254 
255   double det_inv = 1. / (a[0]*a00 + a[3]*a01 + a[5]*a02);
256 
257   a[0] = a00 * det_inv;
258   a[1] = a11 * det_inv;
259   a[2] = a22 * det_inv;
260   a[3] = a01 * det_inv;
261   a[4] = a12 * det_inv;
262   a[5] = a02 * det_inv;
263 }
264 
265 /*----------------------------------------------------------------------------*/
266 /*!
267  * \brief  Return a gradient quantities structure, adding one if needed
268  *
269  * \param[in]  id  id of structure to return
270 
271  * \return  pointer to gradient quantities structure
272  */
273 /*----------------------------------------------------------------------------*/
274 
275 static cs_gradient_quantities_t  *
_gradient_quantities_get(int id)276 _gradient_quantities_get(int  id)
277 {
278   assert(id >= 0);
279 
280   if (id >= _n_gradient_quantities) {
281 
282     BFT_REALLOC(_gradient_quantities, id+1, cs_gradient_quantities_t);
283 
284     for (int i = _n_gradient_quantities; i < id+1; i++) {
285       cs_gradient_quantities_t  *gq = _gradient_quantities + i;
286 
287       gq->cocg_it = NULL;
288       gq->cocgb_s_lsq = NULL;
289       gq->cocg_lsq = NULL;
290       gq->cocgb_s_lsq_ext = NULL;
291       gq->cocg_lsq_ext = NULL;
292     }
293 
294     _n_gradient_quantities = id+1;
295 
296   }
297 
298   return _gradient_quantities + id;
299 }
300 
301 /*----------------------------------------------------------------------------*/
302 /*!
303  * \brief  Destroy mesh quantities structures.
304  */
305 /*----------------------------------------------------------------------------*/
306 
307 static void
_gradient_quantities_destroy(void)308 _gradient_quantities_destroy(void)
309 {
310   for (int i = 0; i < _n_gradient_quantities; i++) {
311 
312     cs_gradient_quantities_t  *gq = _gradient_quantities + i;
313 
314     BFT_FREE(gq->cocg_it);
315     BFT_FREE(gq->cocgb_s_lsq);
316     BFT_FREE(gq->cocg_lsq);
317     BFT_FREE(gq->cocgb_s_lsq_ext);
318     BFT_FREE(gq->cocg_lsq_ext);
319 
320   }
321 
322   BFT_FREE(_gradient_quantities);
323   _n_gradient_quantities = 0;
324 }
325 
326 /*----------------------------------------------------------------------------
327  * Factorize dense p*p symmetric matrices.
328  * Only the lower triangular part is stored and the factorization is performed
329  * in place (original coefficients are replaced).
330  * Crout Factorization is performed (A = L D t(L)).
331  *
332  * parameters:
333  *   d_size   <--  matrix size (p)
334  *   ad       <--> symmetric matrix to be factorized
335  *----------------------------------------------------------------------------*/
336 
337 inline static void
_fact_crout_pp(const int d_size,cs_real_t * ad)338 _fact_crout_pp(const int   d_size,
339                cs_real_t  *ad)
340 {
341   cs_real_t aux[d_size];
342   for (int kk = 0; kk < d_size - 1; kk++) {
343     int kk_d_size = kk*(kk + 1)/2;
344     for (int ii = kk + 1; ii < d_size; ii++) {
345       int ii_d_size = ii*(ii + 1)/2;
346       aux[ii] = ad[ii_d_size + kk];
347       ad[ii_d_size + kk] =   ad[ii_d_size + kk]
348                            / ad[kk_d_size + kk];
349       for (int jj = kk + 1; jj < ii + 1; jj++) {
350         ad[ii_d_size + jj] = ad[ii_d_size + jj] - ad[ii_d_size + kk]*aux[jj];
351       }
352     }
353   }
354 }
355 
356 /*----------------------------------------------------------------------------
357  * Solve forward and backward linear systems of the form L D t(L) x = b.
358  * Matrix L D t(L) should be given by a Crout factorization.
359  *
360  * parameters:
361  *   mat      <--  symmetric factorized (Crout) matrix
362  *   d_size   <--  matrix size (p)
363  *   x         --> linear system vector solution
364  *   b        <--  linear system vector right hand side
365  *----------------------------------------------------------------------------*/
366 
367 inline static void
_fw_and_bw_ldtl_pp(const cs_real_t mat[],const int d_size,cs_real_t x[],const cs_real_t b[])368 _fw_and_bw_ldtl_pp(const cs_real_t mat[],
369                    const int       d_size,
370                          cs_real_t x[],
371                    const cs_real_t b[])
372 {
373   cs_real_t  aux[d_size];
374 
375   /* forward (stricly lower + identity) */
376   for (int ii = 0; ii < d_size; ii++) {
377     int ii_d_size = ii*(ii + 1)/2;
378     aux[ii] = b[ii];
379     for (int jj = 0; jj < ii; jj++) {
380       aux[ii] -= aux[jj]*mat[ii_d_size + jj];
381     }
382   }
383 
384   /* diagonal */
385   for (int ii = 0; ii < d_size; ii++) {
386     int ii_d_size = ii*(ii + 1)/2;
387     aux[ii] /= mat[ii_d_size + ii];
388   }
389 
390   /* backward (transposed of strictly lower + identity) */
391   for (int ii = d_size - 1; ii >= 0; ii--) {
392     x[ii] = aux[ii];
393     for (int jj = d_size - 1; jj > ii; jj--) {
394       int jj_d_size = jj*(jj + 1)/2;
395       x[ii] -= x[jj]*mat[jj_d_size + ii];
396     }
397   }
398 }
399 
400 /*----------------------------------------------------------------------------
401  * Return pointer to new gradient computation info structure.
402  *
403  * parameters:
404  *   name <-- system name
405  *   type <-- resolution method
406  *
407  * returns:
408  *   pointer to newly created linear system info structure
409  *----------------------------------------------------------------------------*/
410 
411 static cs_gradient_info_t *
_gradient_info_create(const char * name,cs_gradient_type_t type)412 _gradient_info_create(const char          *name,
413                       cs_gradient_type_t   type)
414 {
415   cs_gradient_info_t *new_info = NULL;
416 
417   BFT_MALLOC(new_info, 1, cs_gradient_info_t);
418   BFT_MALLOC(new_info->name, strlen(name) + 1, char);
419 
420   strcpy(new_info->name, name);
421   new_info->type = type;
422 
423   new_info->n_calls = 0;
424   new_info->n_iter_min = 0;
425   new_info->n_iter_max = 0;
426   new_info->n_iter_tot = 0;
427 
428   CS_TIMER_COUNTER_INIT(new_info->t_tot);
429 
430   return new_info;
431 }
432 
433 /*----------------------------------------------------------------------------
434  * Destroy gradient computation info structure.
435  *
436  * parameters:
437  *   this_info <-> pointer to linear system info structure pointer
438  *----------------------------------------------------------------------------*/
439 
440 static void
_gradient_info_destroy(cs_gradient_info_t ** this_info)441 _gradient_info_destroy(cs_gradient_info_t  **this_info)
442 {
443   if (*this_info != NULL) {
444     BFT_FREE((*this_info)->name);
445     BFT_FREE(*this_info);
446   }
447 }
448 
449 /*----------------------------------------------------------------------------
450  * Update the number of sweeps for gradient information
451  *
452  * parameters:
453  *   this_info <-> pointer to linear system info structure
454  *   n_iter    <-- number of iterations
455  *----------------------------------------------------------------------------*/
456 
457 static void
_gradient_info_update_iter(cs_gradient_info_t * this_info,int n_iter)458 _gradient_info_update_iter(cs_gradient_info_t  *this_info,
459                            int                  n_iter)
460 {
461   if (n_iter > this_info->n_iter_max) {
462     this_info->n_iter_max = n_iter;
463     /* for first pass: */
464     if (this_info->n_calls == 0)
465       this_info->n_iter_min = n_iter;
466   }
467   else if (n_iter < this_info->n_iter_min)
468     this_info->n_iter_min = n_iter;
469 
470   this_info->n_iter_tot += n_iter;
471 }
472 
473 /*----------------------------------------------------------------------------
474  * Output information regarding gradient computation.
475  *
476  * parameters:
477  *   this_info <-> pointer to linear system info structure
478  *----------------------------------------------------------------------------*/
479 
480 static void
_gradient_info_dump(cs_gradient_info_t * this_info)481 _gradient_info_dump(cs_gradient_info_t *this_info)
482 {
483   int n_calls = this_info->n_calls;
484 
485   cs_log_printf(CS_LOG_PERFORMANCE,
486                 _("\n"
487                   "Summary of gradient computations for \"%s\":\n\n"
488                   "  Reconstruction type:   %s\n"
489                   "  Number of calls:       %d\n"),
490                 this_info->name, cs_gradient_type_name[this_info->type],
491                 n_calls);
492   if (this_info->n_iter_tot > 0)
493     cs_log_printf(CS_LOG_PERFORMANCE,
494                   _("  Number of iterations:  %d mean, %d min., %d max.\n"),
495                   (int)(this_info->n_iter_tot / (unsigned long)n_calls),
496                   this_info->n_iter_min,
497                   this_info->n_iter_max);
498   cs_log_printf(CS_LOG_PERFORMANCE,
499                 _("  Total elapsed time:    %.3f\n"),
500                 this_info->t_tot.nsec*1e-9);
501 }
502 
503 /*----------------------------------------------------------------------------
504  * Return pointer to gradient computation info.
505  *
506  * If this system did not previously exist, it is added to the list of
507  * "known" systems.
508  *
509  * parameters:
510  *   name --> system name
511  *   type --> resolution method
512  *----------------------------------------------------------------------------*/
513 
514 static cs_gradient_info_t *
_find_or_add_system(const char * name,cs_gradient_type_t type)515 _find_or_add_system(const char          *name,
516                     cs_gradient_type_t   type)
517 {
518   int ii, start_id, end_id, mid_id;
519   int cmp_ret = 1;
520 
521   /* Use binary search to find system */
522 
523   start_id = 0;
524   end_id = cs_glob_gradient_n_systems - 1;
525   mid_id = start_id + ((end_id -start_id) / 2);
526 
527   while (start_id <= end_id) {
528     cmp_ret = strcmp((cs_glob_gradient_systems[mid_id])->name, name);
529     if (cmp_ret == 0)
530       cmp_ret = (cs_glob_gradient_systems[mid_id])->type - type;
531     if (cmp_ret < 0)
532       start_id = mid_id + 1;
533     else if (cmp_ret > 0)
534       end_id = mid_id - 1;
535     else
536       break;
537     mid_id = start_id + ((end_id -start_id) / 2);
538   }
539 
540   /* If found, return */
541 
542   if (cmp_ret == 0)
543     return cs_glob_gradient_systems[mid_id];
544 
545   /* Reallocate global array if necessary */
546 
547   if (cs_glob_gradient_n_systems >= cs_glob_gradient_n_max_systems) {
548 
549     if (cs_glob_gradient_n_max_systems == 0)
550       cs_glob_gradient_n_max_systems = 10;
551     else
552       cs_glob_gradient_n_max_systems *= 2;
553 
554     BFT_REALLOC(cs_glob_gradient_systems,
555                 cs_glob_gradient_n_max_systems,
556                 cs_gradient_info_t *);
557 
558   }
559 
560   /* Insert in sorted list */
561 
562   for (ii = cs_glob_gradient_n_systems; ii > mid_id; ii--)
563     cs_glob_gradient_systems[ii] = cs_glob_gradient_systems[ii - 1];
564 
565   cs_glob_gradient_systems[mid_id] = _gradient_info_create(name,
566                                                            type);
567   cs_glob_gradient_n_systems += 1;
568 
569   return cs_glob_gradient_systems[mid_id];
570 }
571 
572 /*----------------------------------------------------------------------------
573  * Compute L2 norm.
574  *
575  * parameters:
576  *   n_elts <-- Local number of elements
577  *   x      <-- array of 3-vectors
578  *----------------------------------------------------------------------------*/
579 
580 static double
_l2_norm_1(cs_lnum_t n_elts,cs_real_t * restrict x)581 _l2_norm_1(cs_lnum_t            n_elts,
582            cs_real_t  *restrict x)
583 {
584   double s = cs_dot(n_elts, x, x);
585 
586 #if defined(HAVE_MPI)
587 
588   if (cs_glob_n_ranks > 1) {
589     double _s;
590     MPI_Allreduce(&s, &_s, 1, MPI_DOUBLE, MPI_SUM, cs_glob_mpi_comm);
591     s = _s;
592   }
593 
594 #endif /* defined(HAVE_MPI) */
595 
596   return (sqrt(s));
597 }
598 
599 /*----------------------------------------------------------------------------
600  * Update R.H.S. for lsq gradient taking into account the weight coefficients.
601  *
602  * parameters:
603  *   wi     <-- Weight coefficient of cell i
604  *   wj     <-- Weight coefficient of cell j
605  *   p_diff <-- R.H.S.
606  *   d      <-- R.H.S.
607  *   a      <-- geometric weight J'F/I'J'
608  *   resi   --> Updated R.H.S. for cell i
609  *   resj   --> Updated R.H.S. for cell j
610  *----------------------------------------------------------------------------*/
611 
612 static inline void
_compute_ani_weighting(const cs_real_t wi[],const cs_real_t wj[],const cs_real_t p_diff,const cs_real_t d[],const cs_real_t a,cs_real_t resi[],cs_real_t resj[])613 _compute_ani_weighting(const cs_real_t  wi[],
614                        const cs_real_t  wj[],
615                        const cs_real_t  p_diff,
616                        const cs_real_t  d[],
617                        const cs_real_t  a,
618                        cs_real_t        resi[],
619                        cs_real_t        resj[])
620 {
621   int ii;
622   cs_real_t ki_d[3] = {0., 0., 0.};
623   cs_real_t kj_d[3] = {0., 0., 0.};
624 
625   cs_real_6_t sum;
626   cs_real_6_t inv_wi;
627   cs_real_6_t inv_wj;
628   cs_real_t _d[3];
629 
630   for (ii = 0; ii < 6; ii++)
631     sum[ii] = a*wi[ii] + (1. - a)*wj[ii];
632 
633   cs_math_sym_33_inv_cramer(wi, inv_wi);
634 
635   cs_math_sym_33_inv_cramer(wj, inv_wj);
636 
637   cs_math_sym_33_3_product(inv_wj, d,  _d);
638   cs_math_sym_33_3_product(sum, _d, ki_d);
639   cs_math_sym_33_3_product(inv_wi, d, _d);
640   cs_math_sym_33_3_product(sum, _d, kj_d);
641 
642   /* 1 / ||Ki. K_f^-1. IJ||^2 */
643   cs_real_t normi = 1. / cs_math_3_dot_product(ki_d, ki_d);
644   /* 1 / ||Kj. K_f^-1. IJ||^2 */
645   cs_real_t normj = 1. / cs_math_3_dot_product(kj_d, kj_d);
646 
647   for (ii = 0; ii < 3; ii++) {
648     resi[ii] += p_diff * ki_d[ii] * normi;
649     resj[ii] += p_diff * kj_d[ii] * normj;
650   }
651 }
652 
653 /*----------------------------------------------------------------------------
654  * Compute the inverse of the face viscosity tensor and anisotropic vector
655  * taking into account the weight coefficients to update cocg for lsq gradient.
656  *
657  * parameters:
658  *   wi     <-- Weight coefficient of cell i
659  *   wj     <-- Weight coefficient of cell j
660  *   d      <-- IJ direction
661  *   a      <-- geometric weight J'F/I'J'
662  *   ki_d   --> Updated vector for cell i
663  *   kj_d   --> Updated vector for cell j
664  *----------------------------------------------------------------------------*/
665 
666 static inline void
_compute_ani_weighting_cocg(const cs_real_t wi[],const cs_real_t wj[],const cs_real_t d[],const cs_real_t a,cs_real_t ki_d[],cs_real_t kj_d[])667 _compute_ani_weighting_cocg(const cs_real_t  wi[],
668                             const cs_real_t  wj[],
669                             const cs_real_t  d[],
670                             const cs_real_t  a,
671                             cs_real_t        ki_d[],
672                             cs_real_t        kj_d[])
673 {
674   int ii;
675   cs_real_6_t sum;
676   cs_real_6_t inv_wi;
677   cs_real_6_t inv_wj;
678   cs_real_t _d[3];
679 
680   for (ii = 0; ii < 6; ii++)
681     sum[ii] = a*wi[ii] + (1. - a)*wj[ii];
682 
683   cs_math_sym_33_inv_cramer(wi,
684                             inv_wi);
685   cs_math_sym_33_inv_cramer(wj,
686                             inv_wj);
687 
688   /* Note: K_i.K_f^-1 = SUM.K_j^-1
689    *       K_j.K_f^-1 = SUM.K_i^-1
690    * So: K_i d = SUM.K_j^-1.IJ */
691 
692   cs_math_sym_33_3_product(inv_wj,
693                            d,
694                            _d);
695   cs_math_sym_33_3_product(sum,
696                            _d,
697                            ki_d);
698   cs_math_sym_33_3_product(inv_wi,
699                            d,
700                            _d);
701   cs_math_sym_33_3_product(sum,
702                            _d,
703                            kj_d);
704 }
705 
706 /*----------------------------------------------------------------------------
707  * Synchronize halos for scalar gradients.
708  *
709  * parameters:
710  *   m              <-- pointer to associated mesh structure
711  *   halo_type      <-- halo type (extended or not)
712  *   grad           <-> gradient of pvar (halo prepared for periodicity
713  *                      of rotation)
714  *----------------------------------------------------------------------------*/
715 
716 static void
_sync_scalar_gradient_halo(const cs_mesh_t * m,cs_halo_type_t halo_type,cs_real_3_t grad[])717 _sync_scalar_gradient_halo(const cs_mesh_t  *m,
718                            cs_halo_type_t    halo_type,
719                            cs_real_3_t       grad[])
720 {
721   if (m->halo != NULL) {
722     cs_halo_sync_var_strided
723       (m->halo, halo_type, (cs_real_t *)grad, 3);
724     if (m->have_rotation_perio)
725       cs_halo_perio_sync_var_vect
726         (m->halo, halo_type, (cs_real_t *)grad, 3);
727   }
728 }
729 
730 /*----------------------------------------------------------------------------
731  * Clip the gradient of a scalar if necessary. This function deals with
732  * the standard or extended neighborhood.
733  *
734  * parameters:
735  *   halo_type      <-- halo type (extended or not)
736  *   clip_mode      <-- type of clipping for the computation of the gradient
737  *   iwarnp         <-- output level
738  *   climgp         <-- clipping coefficient for the computation of the gradient
739  *   var            <-- variable
740  *   grad           --> components of the pressure gradient
741  *----------------------------------------------------------------------------*/
742 
743 static void
_scalar_gradient_clipping(cs_halo_type_t halo_type,cs_gradient_limit_t clip_mode,int verbosity,cs_real_t climgp,const char * var_name,const cs_real_t var[],cs_real_3_t * restrict grad)744 _scalar_gradient_clipping(cs_halo_type_t         halo_type,
745                           cs_gradient_limit_t    clip_mode,
746                           int                    verbosity,
747                           cs_real_t              climgp,
748                           const char            *var_name,
749                           const cs_real_t        var[],
750                           cs_real_3_t  *restrict grad)
751 {
752   cs_real_t  global_min_factor, global_max_factor;
753 
754   cs_real_t  min_factor = 1, max_factor = 0;
755   cs_gnum_t  n_clip = 0, n_g_clip = 0;
756   cs_real_t  *buf = NULL, *restrict clip_factor = NULL;
757   cs_real_t  *restrict denom = NULL, *restrict denum = NULL;
758 
759   const cs_mesh_t  *mesh = cs_glob_mesh;
760   const int n_i_groups = mesh->i_face_numbering->n_groups;
761   const int n_i_threads = mesh->i_face_numbering->n_threads;
762   const cs_lnum_t *restrict i_group_index = mesh->i_face_numbering->group_index;
763   const cs_lnum_t  n_cells = mesh->n_cells;
764   const cs_lnum_t  n_cells_ext = mesh->n_cells_with_ghosts;
765   const cs_lnum_t  *cell_cells_idx = mesh->cell_cells_idx;
766   const cs_lnum_t  *cell_cells_lst = mesh->cell_cells_lst;
767   const cs_real_3_t  *restrict cell_cen
768     = (const cs_real_3_t *restrict)cs_glob_mesh_quantities->cell_cen;
769   const cs_lnum_2_t *restrict i_face_cells
770     = (const cs_lnum_2_t *restrict)mesh->i_face_cells;
771 
772   const cs_halo_t *halo = mesh->halo;
773 
774   if (clip_mode <= CS_GRADIENT_LIMIT_NONE)
775     return;
776 
777   /* Synchronize variable */
778 
779   if (halo != NULL) {
780 
781     /* Exchange for the gradients. Not useful for working array */
782 
783     if (clip_mode == CS_GRADIENT_LIMIT_FACE) {
784 
785       cs_halo_sync_var_strided(halo,
786                                halo_type,
787                                (cs_real_t *restrict)grad,
788                                3);
789       cs_halo_perio_sync_var_vect(halo,
790                                   halo_type,
791                                   (cs_real_t *restrict)grad,
792                                   3);
793     }
794 
795   } /* End if halo */
796 
797   /* Allocate and initialize working buffers */
798 
799   if (clip_mode == CS_GRADIENT_LIMIT_FACE)
800     BFT_MALLOC(buf, 3*n_cells_ext, cs_real_t);
801   else
802     BFT_MALLOC(buf, 2*n_cells_ext, cs_real_t);
803 
804   denum = buf;
805   denom = buf + n_cells_ext;
806 
807   if (clip_mode == CS_GRADIENT_LIMIT_FACE)
808     clip_factor = buf + 2*n_cells_ext;
809 
810 # pragma omp parallel for
811   for (cs_lnum_t ii = 0; ii < n_cells_ext; ii++) {
812     denum[ii] = 0;
813     denom[ii] = 0;
814   }
815 
816   /* First computations:
817       denum holds the maximum variation of the gradient
818       denom holds the maximum variation of the variable */
819 
820   if (clip_mode == CS_GRADIENT_LIMIT_CELL) {
821 
822     for (int g_id = 0; g_id < n_i_groups; g_id++) {
823 #     pragma omp parallel for
824       for (int t_id = 0; t_id < n_i_threads; t_id++) {
825         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
826              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
827              f_id++) {
828 
829           cs_lnum_t ii = i_face_cells[f_id][0];
830           cs_lnum_t jj = i_face_cells[f_id][1];
831 
832           cs_real_t dist[3];
833           for (cs_lnum_t ll = 0; ll < 3; ll++)
834             dist[ll] = cell_cen[ii][ll] - cell_cen[jj][ll];
835 
836           cs_real_t dist1 = CS_ABS(  dist[0]*grad[ii][0]
837                                    + dist[1]*grad[ii][1]
838                                    + dist[2]*grad[ii][2]);
839           cs_real_t dist2 = CS_ABS(  dist[0]*grad[jj][0]
840                                    + dist[1]*grad[jj][1]
841                                    + dist[2]*grad[jj][2]);
842 
843           cs_real_t dvar = CS_ABS(var[ii] - var[jj]);
844 
845           denum[ii] = CS_MAX(denum[ii], dist1);
846           denum[jj] = CS_MAX(denum[jj], dist2);
847           denom[ii] = CS_MAX(denom[ii], dvar);
848           denom[jj] = CS_MAX(denom[jj], dvar);
849 
850         } /* End of loop on faces */
851 
852       } /* End of loop on threads */
853 
854     } /* End of loop on thread groups */
855 
856     /* Complement for extended neighborhood */
857 
858     if (cell_cells_idx != NULL && halo_type == CS_HALO_EXTENDED) {
859 
860 #     pragma omp parallel for
861       for (cs_lnum_t ii = 0; ii < n_cells; ii++) {
862         for (cs_lnum_t cidx = cell_cells_idx[ii];
863              cidx < cell_cells_idx[ii+1];
864              cidx++) {
865 
866           cs_lnum_t jj = cell_cells_lst[cidx];
867 
868           cs_real_t dist[3];
869           for (cs_lnum_t ll = 0; ll < 3; ll++)
870             dist[ll] = cell_cen[ii][ll] - cell_cen[jj][ll];
871 
872           cs_real_t dist1 = CS_ABS(  dist[0]*grad[ii][0]
873                                    + dist[1]*grad[ii][1]
874                                    + dist[2]*grad[ii][2]);
875           cs_real_t dvar = CS_ABS(var[ii] - var[jj]);
876 
877           denum[ii] = CS_MAX(denum[ii], dist1);
878           denom[ii] = CS_MAX(denom[ii], dvar);
879 
880         }
881       }
882 
883     } /* End for extended halo */
884 
885   }
886   else if (clip_mode == CS_GRADIENT_LIMIT_FACE) {
887 
888     for (int g_id = 0; g_id < n_i_groups; g_id++) {
889 #     pragma omp parallel for
890       for (int t_id = 0; t_id < n_i_threads; t_id++) {
891         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
892              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
893              f_id++) {
894 
895           cs_lnum_t ii = i_face_cells[f_id][0];
896           cs_lnum_t jj = i_face_cells[f_id][1];
897 
898           cs_real_t dist[3];
899           for (cs_lnum_t ll = 0; ll < 3; ll++)
900             dist[ll] = cell_cen[ii][ll] - cell_cen[jj][ll];
901 
902           cs_real_t dpdxf = 0.5 * (grad[ii][0] + grad[jj][0]);
903           cs_real_t dpdyf = 0.5 * (grad[ii][1] + grad[jj][1]);
904           cs_real_t dpdzf = 0.5 * (grad[ii][2] + grad[jj][2]);
905 
906           cs_real_t dist1 = CS_ABS(  dist[0]*dpdxf
907                                    + dist[1]*dpdyf
908                                    + dist[2]*dpdzf);
909           cs_real_t dvar = CS_ABS(var[ii] - var[jj]);
910 
911           denum[ii] = CS_MAX(denum[ii], dist1);
912           denum[jj] = CS_MAX(denum[jj], dist1);
913           denom[ii] = CS_MAX(denom[ii], dvar);
914           denom[jj] = CS_MAX(denom[jj], dvar);
915 
916         } /* End of loop on faces */
917 
918       } /* End of loop on threads */
919 
920     } /* End of loop on thread groups */
921 
922     /* Complement for extended neighborhood */
923 
924     if (cell_cells_idx != NULL && halo_type == CS_HALO_EXTENDED) {
925 
926 #     pragma omp parallel for
927       for (cs_lnum_t ii = 0; ii < n_cells; ii++) {
928         for (cs_lnum_t cidx = cell_cells_idx[ii];
929              cidx < cell_cells_idx[ii+1];
930              cidx++) {
931 
932           cs_lnum_t jj = cell_cells_lst[cidx];
933 
934           cs_real_t dist[3];
935           for (cs_lnum_t ll = 0; ll < 3; ll++)
936             dist[ll] = cell_cen[ii][ll] - cell_cen[jj][ll];
937 
938           cs_real_t dpdxf = 0.5 * (grad[ii][0] + grad[jj][0]);
939           cs_real_t dpdyf = 0.5 * (grad[ii][1] + grad[jj][1]);
940           cs_real_t dpdzf = 0.5 * (grad[ii][2] + grad[jj][2]);
941 
942           cs_real_t dist1 = CS_ABS(  dist[0]*dpdxf
943                                    + dist[1]*dpdyf
944                                    + dist[2]*dpdzf);
945           cs_real_t dvar = CS_ABS(var[ii] - var[jj]);
946 
947           denum[ii] = CS_MAX(denum[ii], dist1);
948           denom[ii] = CS_MAX(denom[ii], dvar);
949 
950         }
951       }
952 
953     } /* End for extended neighborhood */
954 
955   } /* End if clip_mode == CS_GRADIENT_LIMIT_FACE */
956 
957   /* Clipping of the gradient if denum/denom > climgp */
958 
959   if (clip_mode == CS_GRADIENT_LIMIT_CELL) {
960 
961 #   pragma omp parallel
962     {
963       cs_gnum_t  t_n_clip = 0;
964       cs_real_t  t_min_factor = min_factor;
965       cs_real_t  t_max_factor = max_factor;
966 
967 #     pragma omp for
968       for (cs_lnum_t ii = 0; ii < n_cells; ii++) {
969 
970         if (denum[ii] > climgp * denom[ii]) {
971 
972           cs_real_t factor1 = climgp * denom[ii]/denum[ii];
973           grad[ii][0] *= factor1;
974           grad[ii][1] *= factor1;
975           grad[ii][2] *= factor1;
976 
977           t_min_factor = CS_MIN(factor1, t_min_factor);
978           t_max_factor = CS_MAX(factor1, t_max_factor);
979           t_n_clip++;
980 
981         } /* If clipping */
982 
983       } /* End of loop on cells */
984 
985 #     pragma omp critical
986       {
987         min_factor = CS_MIN(min_factor, t_min_factor);
988         max_factor = CS_MAX(max_factor, t_max_factor);
989         n_clip += t_n_clip;
990       }
991     } /* End of omp parallel construct */
992 
993   }
994   else if (clip_mode == CS_GRADIENT_LIMIT_FACE) {
995 
996 #   pragma omp parallel for
997     for (cs_lnum_t ii = 0; ii < n_cells_ext; ii++)
998       clip_factor[ii] = (cs_real_t)DBL_MAX;
999 
1000     /* Synchronize variable */
1001 
1002     if (halo != NULL) {
1003       cs_halo_sync_var(halo, halo_type, denom);
1004       cs_halo_sync_var(halo, halo_type, denum);
1005     }
1006 
1007     for (int g_id = 0; g_id < n_i_groups; g_id++) {
1008 
1009 #     pragma omp parallel for
1010       for (int t_id = 0; t_id < n_i_threads; t_id++) {
1011 
1012         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
1013              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
1014              f_id++) {
1015 
1016           cs_lnum_t ii = i_face_cells[f_id][0];
1017           cs_lnum_t jj = i_face_cells[f_id][1];
1018 
1019           cs_real_t factor1 = 1.0;
1020           if (denum[ii] > climgp * denom[ii])
1021             factor1 = climgp * denom[ii]/denum[ii];
1022 
1023           cs_real_t factor2 = 1.0;
1024           if (denum[jj] > climgp * denom[jj])
1025             factor2 = climgp * denom[jj]/denum[jj];
1026 
1027           cs_real_t l_min_factor = CS_MIN(factor1, factor2);
1028 
1029           clip_factor[ii] = CS_MIN(clip_factor[ii], l_min_factor);
1030           clip_factor[jj] = CS_MIN(clip_factor[jj], l_min_factor);
1031 
1032         } /* End of loop on faces */
1033 
1034       } /* End of loop on threads */
1035 
1036     } /* End of loop on thread groups */
1037 
1038     /* Complement for extended neighborhood */
1039 
1040     if (cell_cells_idx != NULL && halo_type == CS_HALO_EXTENDED) {
1041 
1042 #     pragma omp parallel for
1043       for (cs_lnum_t ii = 0; ii < n_cells; ii++) {
1044 
1045         cs_real_t factor1 = 1.0;
1046 
1047         for (cs_lnum_t cidx = cell_cells_idx[ii];
1048              cidx < cell_cells_idx[ii+1];
1049              cidx++) {
1050 
1051           cs_lnum_t jj = cell_cells_lst[cidx];
1052 
1053           cs_real_t factor2 = 1.0;
1054 
1055           if (denum[jj] > climgp * denom[jj])
1056             factor2 = climgp * denom[jj]/denum[jj];
1057 
1058           factor1 = CS_MIN(factor1, factor2);
1059 
1060         }
1061 
1062         clip_factor[ii] = CS_MIN(clip_factor[ii], factor1);
1063 
1064       } /* End of loop on cells */
1065 
1066     } /* End for extended neighborhood */
1067 
1068 #   pragma omp parallel
1069     {
1070       cs_gnum_t t_n_clip = 0;
1071       cs_real_t t_min_factor = min_factor, t_max_factor = max_factor;
1072 
1073 #     pragma omp for
1074       for (cs_lnum_t ii = 0; ii < n_cells; ii++) {
1075 
1076         for (cs_lnum_t ll = 0; ll < 3; ll++)
1077           grad[ii][ll] *= clip_factor[ii];
1078 
1079         if (clip_factor[ii] < 0.99) {
1080           t_max_factor = CS_MAX(t_max_factor, clip_factor[ii]);
1081           t_min_factor = CS_MIN(t_min_factor, clip_factor[ii]);
1082           t_n_clip++;
1083         }
1084 
1085       } /* End of loop on cells */
1086 
1087 #     pragma omp critical
1088       {
1089         min_factor = CS_MIN(min_factor, t_min_factor);
1090         max_factor = CS_MAX(max_factor, t_max_factor);
1091         n_clip += t_n_clip;
1092       }
1093     } /* End of omp parallel construct */
1094 
1095   } /* End if clip_mode == CS_GRADIENT_LIMIT_FACE */
1096 
1097   /* Update min/max and n_clip in case of parallelism */
1098 
1099 #if defined(HAVE_MPI)
1100 
1101   if (mesh->n_domains > 1) {
1102 
1103     assert(sizeof(cs_real_t) == sizeof(double));
1104 
1105     /* Global Max */
1106 
1107     MPI_Allreduce(&max_factor, &global_max_factor, 1, CS_MPI_REAL,
1108                   MPI_MAX, cs_glob_mpi_comm);
1109 
1110     max_factor = global_max_factor;
1111 
1112     /* Global min */
1113 
1114     MPI_Allreduce(&min_factor, &global_min_factor, 1, CS_MPI_REAL,
1115                   MPI_MIN, cs_glob_mpi_comm);
1116 
1117     min_factor = global_min_factor;
1118 
1119     /* Sum number of clippings */
1120 
1121     MPI_Allreduce(&n_clip, &n_g_clip, 1, CS_MPI_GNUM,
1122                   MPI_SUM, cs_glob_mpi_comm);
1123 
1124     n_clip = n_g_clip;
1125 
1126   } /* If n_domains > 1 */
1127 
1128 #endif /* defined(HAVE_MPI) */
1129 
1130   /* Output warning if necessary */
1131 
1132   if (verbosity > 1)
1133     bft_printf(_(" Variable: %s; Gradient limitation in %llu cells\n"
1134                  "   minimum factor = %14.5e; maximum factor = %14.5e\n"),
1135                var_name, (unsigned long long)n_clip, min_factor, max_factor);
1136 
1137   /* Synchronize grad */
1138 
1139   if (halo != NULL) {
1140 
1141     cs_halo_sync_var_strided(halo,
1142                              halo_type,
1143                              (cs_real_t *restrict)grad,
1144                              3);
1145 
1146     cs_halo_perio_sync_var_vect(halo,
1147                                 halo_type,
1148                                 (cs_real_t *restrict)grad,
1149                                 3);
1150 
1151   }
1152 
1153   BFT_FREE(buf);
1154 }
1155 
1156 /*----------------------------------------------------------------------------
1157  * Initialize gradient and right-hand side for scalar gradient reconstruction.
1158  *
1159  * A non-reconstructed gradient is computed at this stage.
1160  *
1161  * Optionally, a volume force generating a hydrostatic pressure component
1162  * may be accounted for.
1163  *
1164  * parameters:
1165  *   m              <-- pointer to associated mesh structure
1166  *   fvq            <-- pointer to associated finite volume quantities
1167  *   cpl            <-- structure associated with internal coupling, or NULL
1168  *   hyd_p_flag     <-- flag for hydrostatic pressure
1169  *   inc            <-- if 0, solve on increment; 1 otherwise
1170  *   f_ext          <-- exterior force generating pressure
1171  *   coefap         <-- B.C. coefficients for boundary face normals
1172  *   coefbp         <-- B.C. coefficients for boundary face normals
1173  *   pvar           <-- variable
1174  *   c_weight       <-- weighted gradient coefficient variable
1175  *   grad           <-> gradient of pvar (halo prepared for periodicity
1176  *                      of rotation)
1177  *----------------------------------------------------------------------------*/
1178 
1179 static void
_initialize_scalar_gradient(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,int hyd_p_flag,cs_real_t inc,const cs_real_3_t f_ext[],const cs_real_t coefap[],const cs_real_t coefbp[],const cs_real_t pvar[],const cs_real_t c_weight[],cs_real_3_t * restrict grad)1180 _initialize_scalar_gradient(const cs_mesh_t                *m,
1181                             const cs_mesh_quantities_t     *fvq,
1182                             const cs_internal_coupling_t   *cpl,
1183                             int                             hyd_p_flag,
1184                             cs_real_t                       inc,
1185                             const cs_real_3_t               f_ext[],
1186                             const cs_real_t                 coefap[],
1187                             const cs_real_t                 coefbp[],
1188                             const cs_real_t                 pvar[],
1189                             const cs_real_t                 c_weight[],
1190                             cs_real_3_t           *restrict grad)
1191 {
1192   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
1193   const cs_lnum_t n_cells = m->n_cells;
1194   const int n_i_groups = m->i_face_numbering->n_groups;
1195   const int n_i_threads = m->i_face_numbering->n_threads;
1196   const int n_b_groups = m->b_face_numbering->n_groups;
1197   const int n_b_threads = m->b_face_numbering->n_threads;
1198   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
1199   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
1200 
1201   const cs_lnum_2_t *restrict i_face_cells
1202     = (const cs_lnum_2_t *restrict)m->i_face_cells;
1203   const cs_lnum_t *restrict b_face_cells
1204     = (const cs_lnum_t *restrict)m->b_face_cells;
1205 
1206   const int *restrict c_disable_flag = fvq->c_disable_flag;
1207   cs_lnum_t has_dc = fvq->has_disable_flag; /* Has cells disabled? */
1208 
1209   const cs_real_t *restrict weight = fvq->weight;
1210   const cs_real_t *restrict cell_f_vol = fvq->cell_f_vol;
1211   if (cs_glob_porous_model == 1 || cs_glob_porous_model == 2)
1212     cell_f_vol = fvq->cell_vol;
1213   const cs_real_3_t *restrict cell_cen
1214     = (const cs_real_3_t *restrict)fvq->cell_cen;
1215   const cs_real_3_t *restrict i_f_face_normal
1216     = (const cs_real_3_t *restrict)fvq->i_f_face_normal;
1217   const cs_real_3_t *restrict b_f_face_normal
1218     = (const cs_real_3_t *restrict)fvq->b_f_face_normal;
1219   const cs_real_3_t *restrict i_face_cog
1220     = (const cs_real_3_t *restrict)fvq->i_face_cog;
1221   const cs_real_3_t *restrict b_face_cog
1222     = (const cs_real_3_t *restrict)fvq->b_face_cog;
1223 
1224   bool  *coupled_faces = (cpl == NULL) ?
1225     NULL : (bool *)cpl->coupled_faces;
1226 
1227   /*Additional terms due to porosity */
1228   cs_field_t *f_i_poro_duq_0 = cs_field_by_name_try("i_poro_duq_0");
1229 
1230   cs_real_t *i_poro_duq_0;
1231   cs_real_t *i_poro_duq_1;
1232   cs_real_t *b_poro_duq;
1233   cs_real_t _f_ext = 0.;
1234 
1235   cs_lnum_t is_porous = 0;
1236   if (f_i_poro_duq_0 != NULL) {
1237     is_porous = 1;
1238     i_poro_duq_0 = f_i_poro_duq_0->val;
1239     i_poro_duq_1 = cs_field_by_name("i_poro_duq_1")->val;
1240     b_poro_duq = cs_field_by_name("b_poro_duq")->val;
1241   } else {
1242     i_poro_duq_0 = &_f_ext;
1243     i_poro_duq_1 = &_f_ext;
1244     b_poro_duq = &_f_ext;
1245   }
1246 
1247   /* Initialize gradient */
1248   /*---------------------*/
1249 
1250 # pragma omp parallel for
1251   for (cs_lnum_t cell_id = 0; cell_id < n_cells_ext; cell_id++) {
1252     for (cs_lnum_t j = 0; j < 3; j++)
1253       grad[cell_id][j] = 0.0;
1254   }
1255 
1256   /* Case with hydrostatic pressure */
1257   /*--------------------------------*/
1258 
1259   if (hyd_p_flag == 1) {
1260 
1261     /* Contribution from interior faces */
1262 
1263     for (int g_id = 0; g_id < n_i_groups; g_id++) {
1264 
1265 #     pragma omp parallel for
1266       for (int t_id = 0; t_id < n_i_threads; t_id++) {
1267 
1268         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
1269              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
1270              f_id++) {
1271 
1272           cs_lnum_t ii = i_face_cells[f_id][0];
1273           cs_lnum_t jj = i_face_cells[f_id][1];
1274 
1275           cs_real_t ktpond = (c_weight == NULL) ?
1276              weight[f_id] :              /* no cell weighting */
1277              weight[f_id] * c_weight[ii] /* cell weighting active */
1278                / (      weight[f_id] * c_weight[ii]
1279                  + (1.0-weight[f_id])* c_weight[jj]);
1280 
1281           cs_real_2_t poro = {
1282             i_poro_duq_0[is_porous*f_id],
1283             i_poro_duq_1[is_porous*f_id]
1284           };
1285 
1286           /*
1287              Remark: \f$ \varia_\face = \alpha_\ij \varia_\celli
1288                                       + (1-\alpha_\ij) \varia_\cellj\f$
1289                      but for the cell \f$ \celli \f$ we remove
1290                      \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
1291                      and for the cell \f$ \cellj \f$ we remove
1292                      \f$ \varia_\cellj \sum_\face \vect{S}_\face = \vect{0} \f$
1293           */
1294 
1295           /* Reconstruction part */
1296           cs_real_t pfaci
1297             =  ktpond
1298                  * (  (i_face_cog[f_id][0] - cell_cen[ii][0])*f_ext[ii][0]
1299                     + (i_face_cog[f_id][1] - cell_cen[ii][1])*f_ext[ii][1]
1300                     + (i_face_cog[f_id][2] - cell_cen[ii][2])*f_ext[ii][2]
1301                     + poro[0])
1302             +  (1.0 - ktpond)
1303                  * (  (i_face_cog[f_id][0] - cell_cen[jj][0])*f_ext[jj][0]
1304                     + (i_face_cog[f_id][1] - cell_cen[jj][1])*f_ext[jj][1]
1305                     + (i_face_cog[f_id][2] - cell_cen[jj][2])*f_ext[jj][2]
1306                     + poro[1]);
1307 
1308           cs_real_t pfacj = pfaci;
1309 
1310           pfaci += (1.0-ktpond) * (pvar[jj] - pvar[ii]);
1311           pfacj -=      ktpond  * (pvar[jj] - pvar[ii]);
1312 
1313           for (cs_lnum_t j = 0; j < 3; j++) {
1314             grad[ii][j] += pfaci * i_f_face_normal[f_id][j];
1315             grad[jj][j] -= pfacj * i_f_face_normal[f_id][j];
1316           }
1317 
1318         } /* loop on faces */
1319 
1320       } /* loop on threads */
1321 
1322     } /* loop on thread groups */
1323 
1324     /* Contribution from boundary faces */
1325 
1326     for (int g_id = 0; g_id < n_b_groups; g_id++) {
1327 
1328 #     pragma omp parallel for
1329       for (int t_id = 0; t_id < n_b_threads; t_id++) {
1330 
1331         for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
1332              f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
1333              f_id++) {
1334 
1335           cs_lnum_t ii = b_face_cells[f_id];
1336 
1337           cs_real_t poro = b_poro_duq[is_porous*f_id];
1338 
1339           /*
1340              Remark: for the cell \f$ \celli \f$ we remove
1341                      \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
1342            */
1343 
1344           /* Reconstruction part */
1345           cs_real_t pfac
1346             = coefap[f_id] * inc
1347             + coefbp[f_id]
1348               * ( (b_face_cog[f_id][0] - cell_cen[ii][0])*f_ext[ii][0]
1349                 + (b_face_cog[f_id][1] - cell_cen[ii][1])*f_ext[ii][1]
1350                 + (b_face_cog[f_id][2] - cell_cen[ii][2])*f_ext[ii][2]
1351                 + poro);
1352 
1353           pfac += (coefbp[f_id] - 1.0) * pvar[ii];
1354 
1355           for (cs_lnum_t j = 0; j < 3; j++)
1356             grad[ii][j] += pfac * b_f_face_normal[f_id][j];
1357 
1358         } /* loop on faces */
1359 
1360       } /* loop on threads */
1361 
1362     } /* loop on thread groups */
1363 
1364   } /* End of test on hydrostatic pressure */
1365 
1366   /* Standard case, without hydrostatic pressure */
1367   /*---------------------------------------------*/
1368 
1369   else {
1370 
1371     /* Contribution from interior faces */
1372 
1373     for (int g_id = 0; g_id < n_i_groups; g_id++) {
1374 
1375 #     pragma omp parallel for
1376       for (int t_id = 0; t_id < n_i_threads; t_id++) {
1377 
1378         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
1379              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
1380              f_id++) {
1381 
1382           cs_lnum_t ii = i_face_cells[f_id][0];
1383           cs_lnum_t jj = i_face_cells[f_id][1];
1384 
1385           cs_real_t ktpond = (c_weight == NULL) ?
1386              weight[f_id] :              /* no cell weighting */
1387              weight[f_id] * c_weight[ii] /* cell weighting active */
1388                / (      weight[f_id] * c_weight[ii]
1389                  + (1.0-weight[f_id])* c_weight[jj]);
1390 
1391           /*
1392              Remark: \f$ \varia_\face = \alpha_\ij \varia_\celli
1393                                       + (1-\alpha_\ij) \varia_\cellj\f$
1394                      but for the cell \f$ \celli \f$ we remove
1395                      \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
1396                      and for the cell \f$ \cellj \f$ we remove
1397                      \f$ \varia_\cellj \sum_\face \vect{S}_\face = \vect{0} \f$
1398           */
1399           cs_real_t pfaci = (1.0-ktpond) * (pvar[jj] - pvar[ii]);
1400           cs_real_t pfacj =     -ktpond  * (pvar[jj] - pvar[ii]);
1401 
1402           for (cs_lnum_t j = 0; j < 3; j++) {
1403             grad[ii][j] += pfaci * i_f_face_normal[f_id][j];
1404             grad[jj][j] -= pfacj * i_f_face_normal[f_id][j];
1405           }
1406 
1407         } /* loop on faces */
1408 
1409       } /* loop on threads */
1410 
1411     } /* loop on thread groups */
1412 
1413     /* Contribution from coupled faces */
1414     if (cpl != NULL)
1415       cs_internal_coupling_initialize_scalar_gradient
1416         (cpl, c_weight, pvar, grad);
1417 
1418     /* Contribution from boundary faces */
1419 
1420     for (int g_id = 0; g_id < n_b_groups; g_id++) {
1421 
1422 #     pragma omp parallel for
1423       for (int t_id = 0; t_id < n_b_threads; t_id++) {
1424 
1425         for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
1426              f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
1427              f_id++) {
1428 
1429           if (cpl == NULL || !coupled_faces[f_id]) {
1430 
1431             cs_lnum_t ii = b_face_cells[f_id];
1432 
1433             /*
1434                Remark: for the cell \f$ \celli \f$ we remove
1435                        \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
1436              */
1437 
1438             cs_real_t pfac =   inc*coefap[f_id]
1439                              + (coefbp[f_id]-1.0)*pvar[ii];
1440 
1441             for (cs_lnum_t j = 0; j < 3; j++)
1442               grad[ii][j] += pfac * b_f_face_normal[f_id][j];
1443 
1444           } /* face without internal coupling */
1445 
1446         } /* loop on faces */
1447 
1448       } /* loop on threads */
1449 
1450     } /* loop on thread groups */
1451 
1452   }
1453 
1454 # pragma omp parallel for
1455   for (cs_lnum_t cell_id = 0; cell_id < n_cells; cell_id++) {
1456     cs_real_t dvol;
1457     /* Is the cell disabled (for solid or porous)? Not the case if coupled */
1458     if (has_dc * c_disable_flag[has_dc * cell_id] == 0)
1459       dvol = 1. / cell_f_vol[cell_id];
1460     else
1461       dvol = 0.;
1462 
1463     for (cs_lnum_t j = 0; j < 3; j++)
1464       grad[cell_id][j] *= dvol;
1465   }
1466 
1467   /* Synchronize halos */
1468 
1469   _sync_scalar_gradient_halo(m, CS_HALO_EXTENDED, grad);
1470 }
1471 
1472 /*----------------------------------------------------------------------------
1473  * Compute 3x3 matrix cocg for the iterative algorithm
1474  *
1475  * parameters:
1476  *   m    <--  mesh
1477  *   fvq  <--  mesh quantities
1478  *   ce   <--  coupling entity
1479  *   gq   <->  gradient quantities
1480  *
1481  * returns:
1482  *   pointer to cocg matrix (handled in main or coupled mesh quantities)
1483  *----------------------------------------------------------------------------*/
1484 
1485 static cs_real_33_t *
_compute_cell_cocg_it(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * ce,cs_gradient_quantities_t * gq)1486 _compute_cell_cocg_it(const cs_mesh_t               *m,
1487                       const cs_mesh_quantities_t    *fvq,
1488                       const cs_internal_coupling_t  *ce,
1489                       cs_gradient_quantities_t      *gq)
1490 {
1491   /* Local variables */
1492 
1493   const int n_cells = m->n_cells;
1494   const int n_cells_with_ghosts = m->n_cells_with_ghosts;
1495   const int n_i_faces = m->n_i_faces;
1496 
1497   const cs_lnum_2_t *restrict i_face_cells
1498     = (const cs_lnum_2_t *restrict)m->i_face_cells;
1499 
1500   const cs_real_t *restrict cell_vol = fvq->cell_vol;
1501   const cs_real_3_t *restrict i_face_normal
1502     = (const cs_real_3_t *restrict)fvq->i_face_normal;
1503   const cs_real_3_t *restrict dofij
1504     = (const cs_real_3_t *restrict)fvq->dofij;
1505 
1506   cs_real_33_t *restrict cocg = gq->cocg_it;
1507 
1508   cs_lnum_t  cell_id, f_id, i, j;
1509   cs_real_t  pfac, vecfac;
1510   cs_real_t  dvol1, dvol2;
1511 
1512   if (cocg == NULL) {
1513     BFT_MALLOC(cocg, n_cells_with_ghosts, cs_real_33_t);
1514     gq->cocg_it = cocg;
1515   }
1516 
1517   /* compute the dimensionless matrix COCG for each cell*/
1518 
1519   for (cell_id = 0; cell_id < n_cells_with_ghosts; cell_id++) {
1520     cocg[cell_id][0][0]= 1.0;
1521     cocg[cell_id][0][1]= 0.0;
1522     cocg[cell_id][0][2]= 0.0;
1523     cocg[cell_id][1][0]= 0.0;
1524     cocg[cell_id][1][1]= 1.0;
1525     cocg[cell_id][1][2]= 0.0;
1526     cocg[cell_id][2][0]= 0.0;
1527     cocg[cell_id][2][1]= 0.0;
1528     cocg[cell_id][2][2]= 1.0;
1529   }
1530 
1531   /* Interior face treatment */
1532 
1533   for (f_id = 0; f_id < n_i_faces; f_id++) {
1534     cs_lnum_t cell_id1 = i_face_cells[f_id][0];
1535     cs_lnum_t cell_id2 = i_face_cells[f_id][1];
1536 
1537     dvol1 = 1./cell_vol[cell_id1];
1538     dvol2 = 1./cell_vol[cell_id2];
1539 
1540     for (i = 0; i < 3; i++) {
1541 
1542       pfac = -0.5*dofij[f_id][i];
1543 
1544       for (j = 0; j < 3; j++) {
1545         vecfac = pfac*i_face_normal[f_id][j];
1546         cocg[cell_id1][i][j] += vecfac * dvol1;
1547         cocg[cell_id2][i][j] -= vecfac * dvol2;
1548       }
1549     }
1550   }
1551 
1552   /* Contribution for internal coupling */
1553   if (ce != NULL) {
1554     cs_internal_coupling_it_cocg_contribution(ce, cocg);
1555   }
1556 
1557   /* 3x3 Matrix inversion */
1558 
1559 # pragma omp parallel for
1560   for (cell_id = 0; cell_id < n_cells; cell_id++)
1561     cs_math_33_inv_cramer_in_place(cocg[cell_id]);
1562 
1563   return cocg;
1564 }
1565 
1566 /*----------------------------------------------------------------------------
1567  * Compute cell gradient using iterative reconstruction for non-orthogonal
1568  * meshes (nswrgp > 1).
1569  *
1570  * Optionally, a volume force generating a hydrostatic pressure component
1571  * may be accounted for.
1572  *
1573  * parameters:
1574  *   m               <-- pointer to associated mesh structure
1575  *   fvq             <-- pointer to associated finite volume quantities
1576  *   cpl             <-- structure associated with internal coupling, or NULL
1577  *   var_name        <-- variable name
1578  *   gradient_info   <-- pointer to performance logging structure, or NULL
1579  *   nswrgp          <-- number of sweeps for gradient reconstruction
1580  *   hyd_p_flag      <-- flag for hydrostatic pressure
1581  *   verbosity       <-- verbosity level
1582  *   inc             <-- if 0, solve on increment; 1 otherwise
1583  *   epsrgp          <-- relative precision for gradient reconstruction
1584  *   f_ext           <-- exterior force generating pressure
1585  *   bc_coeff_a          <-- B.C. coefficients for boundary face normals
1586  *   coefbp          <-- B.C. coefficients for boundary face normals
1587  *   pvar            <-- variable
1588  *   c_weight        <-- weighted gradient coefficient variable
1589  *   grad            <-> gradient of pvar (halo prepared for periodicity
1590  *                       of rotation)
1591  *----------------------------------------------------------------------------*/
1592 
1593 static void
_iterative_scalar_gradient(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,const char * var_name,cs_gradient_info_t * gradient_info,int nswrgp,int hyd_p_flag,int verbosity,cs_real_t inc,cs_real_t epsrgp,const cs_real_3_t f_ext[],const cs_real_t coefap[],const cs_real_t coefbp[],const cs_real_t pvar[],const cs_real_t c_weight[],cs_real_3_t * restrict grad)1594 _iterative_scalar_gradient(const cs_mesh_t                *m,
1595                            const cs_mesh_quantities_t     *fvq,
1596                            const cs_internal_coupling_t   *cpl,
1597                            const char                     *var_name,
1598                            cs_gradient_info_t             *gradient_info,
1599                            int                             nswrgp,
1600                            int                             hyd_p_flag,
1601                            int                             verbosity,
1602                            cs_real_t                       inc,
1603                            cs_real_t                       epsrgp,
1604                            const cs_real_3_t               f_ext[],
1605                            const cs_real_t                 coefap[],
1606                            const cs_real_t                 coefbp[],
1607                            const cs_real_t                 pvar[],
1608                            const cs_real_t                 c_weight[],
1609                            cs_real_3_t           *restrict grad)
1610 {
1611   const cs_lnum_t n_cells = m->n_cells;
1612   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
1613   const int n_i_groups = m->i_face_numbering->n_groups;
1614   const int n_i_threads = m->i_face_numbering->n_threads;
1615   const int n_b_groups = m->b_face_numbering->n_groups;
1616   const int n_b_threads = m->b_face_numbering->n_threads;
1617   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
1618   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
1619 
1620   const cs_lnum_2_t *restrict i_face_cells
1621     = (const cs_lnum_2_t *restrict)m->i_face_cells;
1622   const cs_lnum_t *restrict b_face_cells
1623     = (const cs_lnum_t *restrict)m->b_face_cells;
1624 
1625   const int *restrict c_disable_flag = fvq->c_disable_flag;
1626   cs_lnum_t has_dc = fvq->has_disable_flag; /* Has cells disabled? */
1627 
1628   const cs_real_t *restrict weight = fvq->weight;
1629   const cs_real_t *restrict cell_f_vol = fvq->cell_f_vol;
1630   if (cs_glob_porous_model == 1 || cs_glob_porous_model == 2)
1631     cell_f_vol = fvq->cell_vol;
1632   const cs_real_3_t *restrict cell_cen
1633     = (const cs_real_3_t *restrict)fvq->cell_cen;
1634   const cs_real_3_t *restrict i_f_face_normal
1635     = (const cs_real_3_t *restrict)fvq->i_f_face_normal;
1636   const cs_real_3_t *restrict b_f_face_normal
1637     = (const cs_real_3_t *restrict)fvq->b_f_face_normal;
1638   const cs_real_3_t *restrict i_face_cog
1639     = (const cs_real_3_t *restrict)fvq->i_face_cog;
1640   const cs_real_3_t *restrict b_face_cog
1641     = (const cs_real_3_t *restrict)fvq->b_face_cog;
1642   const cs_real_3_t *restrict diipb
1643     = (const cs_real_3_t *restrict)fvq->diipb;
1644   const cs_real_3_t *restrict dofij
1645     = (const cs_real_3_t *restrict)fvq->dofij;
1646 
1647   cs_real_3_t *rhs;
1648 
1649   int n_sweeps = 0;
1650   cs_real_t l2_residual = 0.;
1651 
1652   if (nswrgp < 1) {
1653     if (gradient_info != NULL)
1654       _gradient_info_update_iter(gradient_info, 0);
1655     return;
1656   }
1657 
1658   int gq_id = (cpl == NULL) ? 0 : cpl->id+1;
1659   cs_gradient_quantities_t  *gq = _gradient_quantities_get(gq_id);
1660 
1661   cs_real_33_t *restrict cocg = gq->cocg_it;
1662   if (cocg == NULL)
1663     cocg = _compute_cell_cocg_it(m, fvq, cpl, gq);
1664 
1665   bool  *coupled_faces = (cpl == NULL) ?
1666     NULL : (bool *)cpl->coupled_faces;
1667 
1668   /*Additional terms due to porosity */
1669   cs_field_t *f_i_poro_duq_0 = cs_field_by_name_try("i_poro_duq_0");
1670 
1671   cs_real_t *i_poro_duq_0;
1672   cs_real_t *i_poro_duq_1;
1673   cs_real_t *b_poro_duq;
1674   cs_real_t _f_ext = 0.;
1675 
1676   int is_porous = 0;
1677   if (f_i_poro_duq_0 != NULL) {
1678     is_porous = 1;
1679     i_poro_duq_0 = f_i_poro_duq_0->val;
1680     i_poro_duq_1 = cs_field_by_name("i_poro_duq_1")->val;
1681     b_poro_duq = cs_field_by_name("b_poro_duq")->val;
1682   } else {
1683     i_poro_duq_0 = &_f_ext;
1684     i_poro_duq_1 = &_f_ext;
1685     b_poro_duq = &_f_ext;
1686   }
1687 
1688   /* Reconstruct gradients for non-orthogonal meshes */
1689   /*-------------------------------------------------*/
1690 
1691   /* Semi-implicit resolution on the whole mesh  */
1692 
1693   /* Compute normalization residual */
1694 
1695   cs_real_t  rnorm = _l2_norm_1(3*n_cells, (cs_real_t *)grad);
1696 
1697   if (rnorm <= cs_math_epzero) {
1698     if (gradient_info != NULL)
1699       _gradient_info_update_iter(gradient_info, 0);
1700     return;
1701   }
1702 
1703   BFT_MALLOC(rhs, n_cells_ext, cs_real_3_t);
1704 
1705   /* Vector OijFij is computed in CLDijP */
1706 
1707   /* Start iterations */
1708   /*------------------*/
1709 
1710   for (n_sweeps = 1; n_sweeps < nswrgp; n_sweeps++) {
1711 
1712     /* Compute right hand side */
1713 
1714 #   pragma omp parallel for
1715     for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
1716       rhs[c_id][0] = -grad[c_id][0] * cell_f_vol[c_id];
1717       rhs[c_id][1] = -grad[c_id][1] * cell_f_vol[c_id];
1718       rhs[c_id][2] = -grad[c_id][2] * cell_f_vol[c_id];
1719     }
1720 
1721     /* Case with hydrostatic pressure */
1722     /*--------------------------------*/
1723 
1724     if (hyd_p_flag == 1) {
1725 
1726       /* Contribution from interior faces */
1727 
1728       for (int g_id = 0; g_id < n_i_groups; g_id++) {
1729 
1730 #       pragma omp parallel for
1731         for (int t_id = 0; t_id < n_i_threads; t_id++) {
1732 
1733           for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
1734                f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
1735                f_id++) {
1736 
1737             cs_lnum_t c_id1 = i_face_cells[f_id][0];
1738             cs_lnum_t c_id2 = i_face_cells[f_id][1];
1739 
1740             cs_real_t ktpond = (c_weight == NULL) ?
1741               weight[f_id] :                     // no cell weighting
1742               weight[f_id]  * c_weight[c_id1] // cell weighting active
1743                 / (      weight[f_id]  * c_weight[c_id1]
1744                   + (1.0-weight[f_id]) * c_weight[c_id2]);
1745 
1746             cs_real_2_t poro = {
1747               i_poro_duq_0[is_porous*f_id],
1748               i_poro_duq_1[is_porous*f_id]
1749             };
1750 
1751             // TODO add porous contribution
1752             cs_real_t  fexd[3];
1753             fexd[0] = 0.5 * (f_ext[c_id1][0] + f_ext[c_id2][0]);
1754             fexd[1] = 0.5 * (f_ext[c_id1][1] + f_ext[c_id2][1]);
1755             fexd[2] = 0.5 * (f_ext[c_id1][2] + f_ext[c_id2][2]);
1756 
1757             /*
1758                Remark: \f$ \varia_\face = \alpha_\ij \varia_\celli
1759                                         + (1-\alpha_\ij) \varia_\cellj\f$
1760                        but for the cell \f$ \celli \f$ we remove
1761                        \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
1762                        and for the cell \f$ \cellj \f$ we remove
1763                        \f$ \varia_\cellj \sum_\face \vect{S}_\face = \vect{0} \f$
1764             */
1765 
1766             /* Reconstruction part */
1767             cs_real_t pfaci =
1768                      (i_face_cog[f_id][0]-cell_cen[c_id1][0])
1769                     *(ktpond*f_ext[c_id1][0]-weight[f_id]*fexd[0])
1770                    + (i_face_cog[f_id][1]-cell_cen[c_id1][1])
1771                     *(ktpond*f_ext[c_id1][1]-weight[f_id]*fexd[1])
1772                    + (i_face_cog[f_id][2]-cell_cen[c_id1][2])
1773                     *(ktpond*f_ext[c_id1][2]-weight[f_id]*fexd[2])
1774                    + ktpond*poro[0]
1775                +     (i_face_cog[f_id][0]-cell_cen[c_id2][0])
1776                     *((1.0 - ktpond)*f_ext[c_id2][0]-(1.-weight[f_id])*fexd[0])
1777                    + (i_face_cog[f_id][1]-cell_cen[c_id2][1])
1778                     *((1.0 - ktpond)*f_ext[c_id2][1]-(1.-weight[f_id])*fexd[1])
1779                    + (i_face_cog[f_id][2]-cell_cen[c_id2][2])
1780                     *((1.0 - ktpond)*f_ext[c_id2][2]-(1.-weight[f_id])*fexd[2])
1781                    + (1.0 - ktpond)*poro[1]
1782                + ( dofij[f_id][0] * (grad[c_id1][0]+grad[c_id2][0])
1783                  + dofij[f_id][1] * (grad[c_id1][1]+grad[c_id2][1])
1784                  + dofij[f_id][2] * (grad[c_id1][2]+grad[c_id2][2]))*0.5;
1785 
1786             cs_real_t pfacj = pfaci;
1787 
1788             pfaci += (1.0-ktpond) * (pvar[c_id2] - pvar[c_id1]);
1789             pfacj -= ktpond * (pvar[c_id2] - pvar[c_id1]);
1790 
1791             for (cs_lnum_t j = 0; j < 3; j++) {
1792               rhs[c_id1][j] += pfaci * i_f_face_normal[f_id][j];
1793               rhs[c_id2][j] -= pfacj * i_f_face_normal[f_id][j];
1794             }
1795 
1796           } /* loop on faces */
1797 
1798         } /* loop on threads */
1799 
1800       } /* loop on thread groups */
1801 
1802       /* Contribution from boundary faces */
1803 
1804       for (int g_id = 0; g_id < n_b_groups; g_id++) {
1805 
1806 #       pragma omp parallel for
1807         for (int t_id = 0; t_id < n_b_threads; t_id++) {
1808 
1809           for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
1810                f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
1811                f_id++) {
1812 
1813             cs_lnum_t c_id = b_face_cells[f_id];
1814 
1815             cs_real_t poro = b_poro_duq[is_porous*f_id];
1816 
1817             /*
1818               Remark: for the cell \f$ \celli \f$ we remove
1819               \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
1820             */
1821 
1822             /* Reconstruction part */
1823             cs_real_t pfac
1824               =   coefap[f_id] * inc
1825                 + coefbp[f_id]
1826                    * (  diipb[f_id][0] * (grad[c_id][0] - f_ext[c_id][0])
1827                       + diipb[f_id][1] * (grad[c_id][1] - f_ext[c_id][1])
1828                       + diipb[f_id][2] * (grad[c_id][2] - f_ext[c_id][2])
1829                       + (b_face_cog[f_id][0]-cell_cen[c_id][0]) * f_ext[c_id][0]
1830                       + (b_face_cog[f_id][1]-cell_cen[c_id][1]) * f_ext[c_id][1]
1831                       + (b_face_cog[f_id][2]-cell_cen[c_id][2]) * f_ext[c_id][2]
1832                       + poro);
1833 
1834             pfac += (coefbp[f_id] -1.0) * pvar[c_id];
1835 
1836             rhs[c_id][0] += pfac * b_f_face_normal[f_id][0];
1837             rhs[c_id][1] += pfac * b_f_face_normal[f_id][1];
1838             rhs[c_id][2] += pfac * b_f_face_normal[f_id][2];
1839 
1840           } /* loop on faces */
1841 
1842         } /* loop on threads */
1843 
1844       } /* loop on thread groups */
1845 
1846     } /* End of test on hydrostatic pressure */
1847 
1848     /* Standard case, without hydrostatic pressure */
1849     /*---------------------------------------------*/
1850 
1851     else {
1852 
1853       /* Contribution from interior faces */
1854 
1855       for (int g_id = 0; g_id < n_i_groups; g_id++) {
1856 
1857 #       pragma omp parallel for
1858         for (int t_id = 0; t_id < n_i_threads; t_id++) {
1859 
1860           for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
1861                f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
1862                f_id++) {
1863 
1864             cs_lnum_t c_id1 = i_face_cells[f_id][0];
1865             cs_lnum_t c_id2 = i_face_cells[f_id][1];
1866 
1867             /*
1868                Remark: \f$ \varia_\face = \alpha_\ij \varia_\celli
1869                                         + (1-\alpha_\ij) \varia_\cellj\f$
1870                        but for the cell \f$ \celli \f$ we remove
1871                        \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
1872                        and for the cell \f$ \cellj \f$ we remove
1873                        \f$ \varia_\cellj \sum_\face \vect{S}_\face = \vect{0} \f$
1874             */
1875 
1876             /* Reconstruction part */
1877             cs_real_t pfaci
1878               = 0.5 * (  dofij[f_id][0] * (grad[c_id1][0]+grad[c_id2][0])
1879                        + dofij[f_id][1] * (grad[c_id1][1]+grad[c_id2][1])
1880                        + dofij[f_id][2] * (grad[c_id1][2]+grad[c_id2][2]));
1881             cs_real_t pfacj = pfaci;
1882 
1883             cs_real_t ktpond = (c_weight == NULL) ?
1884               weight[f_id] :                     // no cell weighting
1885               weight[f_id]  * c_weight[c_id1] // cell weighting active
1886                 / (      weight[f_id]  * c_weight[c_id1]
1887                   + (1.0-weight[f_id]) * c_weight[c_id2]);
1888 
1889             pfaci += (1.0-ktpond) * (pvar[c_id2] - pvar[c_id1]);
1890             pfacj -=      ktpond  * (pvar[c_id2] - pvar[c_id1]);
1891 
1892             for (cs_lnum_t j = 0; j < 3; j++) {
1893               rhs[c_id1][j] += pfaci * i_f_face_normal[f_id][j];
1894               rhs[c_id2][j] -= pfacj * i_f_face_normal[f_id][j];
1895             }
1896 
1897           } /* loop on faces */
1898 
1899         } /* loop on threads */
1900 
1901       } /* loop on thread groups */
1902 
1903       /* Contribution from coupled faces */
1904       if (cpl != NULL)
1905         cs_internal_coupling_iterative_scalar_gradient
1906           (cpl,
1907            c_weight,
1908            grad,
1909            pvar,
1910            rhs);
1911 
1912       /* Contribution from boundary faces */
1913 
1914       for (int g_id = 0; g_id < n_b_groups; g_id++) {
1915 
1916 #       pragma omp parallel for
1917         for (int t_id = 0; t_id < n_b_threads; t_id++) {
1918 
1919           for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
1920                f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
1921                f_id++) {
1922 
1923             if (cpl == NULL || !coupled_faces[f_id]) {
1924 
1925               cs_lnum_t c_id = b_face_cells[f_id];
1926 
1927               /*
1928                 Remark: for the cell \f$ \celli \f$ we remove
1929                 \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
1930               */
1931 
1932               /* Reconstruction part */
1933               cs_real_t pfac
1934                 =      coefap[f_id] * inc
1935                      + coefbp[f_id]
1936                        * (  diipb[f_id][0] * grad[c_id][0]
1937                           + diipb[f_id][1] * grad[c_id][1]
1938                           + diipb[f_id][2] * grad[c_id][2]);
1939 
1940               pfac += (coefbp[f_id] -1.0) * pvar[c_id];
1941 
1942               rhs[c_id][0] += pfac * b_f_face_normal[f_id][0];
1943               rhs[c_id][1] += pfac * b_f_face_normal[f_id][1];
1944               rhs[c_id][2] += pfac * b_f_face_normal[f_id][2];
1945 
1946             } /* face without internal coupling */
1947 
1948           } /* loop on faces */
1949 
1950         } /* loop on threads */
1951 
1952       } /* loop on thread groups */
1953 
1954     }
1955 
1956     /* Increment gradient */
1957     /*--------------------*/
1958 
1959 #   pragma omp parallel for
1960     for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
1961       cs_real_t dvol;
1962       /* Is the cell disabled (for solid or porous)? Not the case if coupled */
1963       if (has_dc * c_disable_flag[has_dc * c_id] == 0)
1964         dvol = 1. / cell_f_vol[c_id];
1965       else
1966         dvol = 0.;
1967 
1968       rhs[c_id][0] *= dvol;
1969       rhs[c_id][1] *= dvol;
1970       rhs[c_id][2] *= dvol;
1971 
1972       grad[c_id][0] +=   rhs[c_id][0] * cocg[c_id][0][0]
1973                        + rhs[c_id][1] * cocg[c_id][1][0]
1974                        + rhs[c_id][2] * cocg[c_id][2][0];
1975       grad[c_id][1] +=   rhs[c_id][0] * cocg[c_id][0][1]
1976                        + rhs[c_id][1] * cocg[c_id][1][1]
1977                        + rhs[c_id][2] * cocg[c_id][2][1];
1978       grad[c_id][2] +=   rhs[c_id][0] * cocg[c_id][0][2]
1979                        + rhs[c_id][1] * cocg[c_id][1][2]
1980                        + rhs[c_id][2] * cocg[c_id][2][2];
1981     }
1982 
1983     /* Synchronize halos */
1984 
1985     _sync_scalar_gradient_halo(m, CS_HALO_STANDARD, grad);
1986 
1987     /* Convergence test */
1988 
1989     l2_residual = _l2_norm_1(3*n_cells, (cs_real_t *)rhs);
1990 
1991     if (l2_residual < epsrgp*rnorm) {
1992       if (verbosity >= 2)
1993         bft_printf(_(" %s; variable: %s; converged in %d sweeps\n"
1994                      " %*s  normed residual: %11.4e; norm: %11.4e\n"),
1995                    __func__, var_name, n_sweeps,
1996                    (int)(strlen(__func__)), " ", l2_residual/rnorm, rnorm);
1997       break;
1998     }
1999 
2000   } /* Loop on sweeps */
2001 
2002   if (l2_residual >= epsrgp*rnorm && verbosity > -1) {
2003     bft_printf(_(" Warning:\n"
2004                  " --------\n"
2005                  "   %s; variable: %s; sweeps: %d\n"
2006                  "   %*s  normed residual: %11.4e; norm: %11.4e\n"),
2007                __func__, var_name, n_sweeps,
2008                (int)(strlen(__func__)), " ", l2_residual/rnorm, rnorm);
2009   }
2010 
2011   if (gradient_info != NULL)
2012     _gradient_info_update_iter(gradient_info, n_sweeps);
2013 
2014   BFT_FREE(rhs);
2015 }
2016 
2017 /*----------------------------------------------------------------------------
2018  * Compute 3x3 matrix cocg for the scalar gradient least squares algorithm
2019  *
2020  * parameters:
2021  *   m             <--  mesh
2022  *   extended      <--  true if extended neighborhood used
2023  *   fvq           <--  mesh quantities
2024  *   ce            <--  coupling entity
2025  *   gq            <->  gradient quantities
2026  *----------------------------------------------------------------------------*/
2027 
2028 static void
_compute_cell_cocg_lsq(const cs_mesh_t * m,bool extended,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * ce,cs_gradient_quantities_t * gq)2029 _compute_cell_cocg_lsq(const cs_mesh_t               *m,
2030                        bool                           extended,
2031                        const cs_mesh_quantities_t    *fvq,
2032                        const cs_internal_coupling_t  *ce,
2033                        cs_gradient_quantities_t      *gq)
2034 
2035 {
2036   const int n_cells = m->n_cells;
2037   const int n_cells_ext = m->n_cells_with_ghosts;
2038   const int n_i_groups = m->i_face_numbering->n_groups;
2039   const int n_i_threads = m->i_face_numbering->n_threads;
2040   const int n_b_groups = m->b_face_numbering->n_groups;
2041   const int n_b_threads = m->b_face_numbering->n_threads;
2042   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
2043   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
2044 
2045   const cs_lnum_2_t *restrict i_face_cells
2046     = (const cs_lnum_2_t *restrict)m->i_face_cells;
2047   const cs_lnum_t *restrict b_face_cells
2048     = (const cs_lnum_t *restrict)m->b_face_cells;
2049   const cs_lnum_t *restrict cell_cells_idx
2050     = (const cs_lnum_t *restrict)m->cell_cells_idx;
2051   const cs_lnum_t *restrict cell_cells_lst
2052     = (const cs_lnum_t *restrict)m->cell_cells_lst;
2053 
2054   const cs_real_3_t *restrict cell_cen
2055     = (const cs_real_3_t *restrict)fvq->cell_cen;
2056   const cs_real_3_t *restrict b_face_normal
2057     = (const cs_real_3_t *restrict)fvq->b_face_normal;
2058 
2059   cs_cocg_6_t  *restrict cocgb = NULL, *restrict cocg = NULL;
2060 
2061   /* Map cocg/cocgb to correct structure, reallocate if needed */
2062 
2063   if (extended) {
2064     cocg = gq->cocg_lsq_ext;
2065     cocgb = gq->cocgb_s_lsq_ext;
2066   }
2067   else {
2068     cocg = gq->cocg_lsq;
2069     cocgb =gq->cocgb_s_lsq;
2070   }
2071 
2072   if (cocg == NULL) {
2073 
2074     assert(cocgb == NULL);
2075 
2076     BFT_MALLOC(cocg, n_cells_ext, cs_cocg_6_t);
2077     BFT_MALLOC(cocgb, m->n_b_cells, cs_cocg_6_t);
2078 
2079     if (extended) {
2080       gq->cocg_lsq_ext = cocg;
2081       gq->cocgb_s_lsq_ext = cocgb;
2082     }
2083     else {
2084       gq->cocg_lsq = cocg;
2085       gq->cocgb_s_lsq = cocgb;
2086     }
2087 
2088   }
2089 
2090   const bool *coupled_faces = NULL;
2091   if (ce != NULL)
2092     coupled_faces = ce->coupled_faces;
2093 
2094   /* Initialization */
2095 
2096 # pragma omp parallel
2097   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
2098     for (cs_lnum_t ll = 0; ll < 6; ll++) {
2099       cocg[c_id][ll] = 0.0;
2100     }
2101   }
2102 
2103   /* Contribution from interior faces */
2104 
2105   for (int g_id = 0; g_id < n_i_groups; g_id++) {
2106 
2107 #   pragma omp parallel for
2108     for (int t_id = 0; t_id < n_i_threads; t_id++) {
2109 
2110       for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
2111            f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
2112            f_id++) {
2113 
2114         cs_real_t dc[3];
2115         cs_lnum_t ii = i_face_cells[f_id][0];
2116         cs_lnum_t jj = i_face_cells[f_id][1];
2117 
2118         for (cs_lnum_t ll = 0; ll < 3; ll++)
2119           dc[ll] = cell_cen[jj][ll] - cell_cen[ii][ll];
2120         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
2121 
2122         cocg[ii][0] += dc[0]*dc[0]*ddc;
2123         cocg[ii][1] += dc[1]*dc[1]*ddc;
2124         cocg[ii][2] += dc[2]*dc[2]*ddc;
2125         cocg[ii][3] += dc[0]*dc[1]*ddc;
2126         cocg[ii][4] += dc[1]*dc[2]*ddc;
2127         cocg[ii][5] += dc[0]*dc[2]*ddc;
2128 
2129         cocg[jj][0] += dc[0]*dc[0]*ddc;
2130         cocg[jj][1] += dc[1]*dc[1]*ddc;
2131         cocg[jj][2] += dc[2]*dc[2]*ddc;
2132         cocg[jj][3] += dc[0]*dc[1]*ddc;
2133         cocg[jj][4] += dc[1]*dc[2]*ddc;
2134         cocg[jj][5] += dc[0]*dc[2]*ddc;
2135 
2136       } /* loop on faces */
2137 
2138     } /* loop on threads */
2139 
2140   } /* loop on thread groups */
2141 
2142   /* Contribution for internal coupling */
2143   if (ce != NULL) {
2144     cs_internal_coupling_lsq_cocg_contribution(ce, cocg);
2145   }
2146 
2147   /* Contribution from extended neighborhood */
2148 
2149   if (extended) {
2150 
2151 #   pragma omp parallel for
2152     for (cs_lnum_t ii = 0; ii < n_cells; ii++) {
2153       for (cs_lnum_t cidx = cell_cells_idx[ii];
2154            cidx < cell_cells_idx[ii+1];
2155            cidx++) {
2156 
2157         cs_real_t dc[3];
2158         cs_lnum_t jj = cell_cells_lst[cidx];
2159 
2160         for (cs_lnum_t ll = 0; ll < 3; ll++)
2161           dc[ll] = cell_cen[jj][ll] - cell_cen[ii][ll];
2162         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
2163 
2164         cocg[ii][0] += dc[0]*dc[0]*ddc;
2165         cocg[ii][1] += dc[1]*dc[1]*ddc;
2166         cocg[ii][2] += dc[2]*dc[2]*ddc;
2167         cocg[ii][3] += dc[0]*dc[1]*ddc;
2168         cocg[ii][4] += dc[1]*dc[2]*ddc;
2169         cocg[ii][5] += dc[0]*dc[2]*ddc;
2170 
2171       }
2172     }
2173 
2174   } /* End for extended neighborhood */
2175 
2176   /* Save partial cocg at interior faces of boundary cells */
2177 
2178 # pragma omp parallel for
2179   for (cs_lnum_t ii = 0; ii < m->n_b_cells; ii++) {
2180     cs_lnum_t c_id = m->b_cells[ii];
2181     for (cs_lnum_t ll = 0; ll < 6; ll++) {
2182       cocgb[ii][ll] = cocg[c_id][ll];
2183     }
2184   }
2185 
2186   /* Contribution from boundary faces, assuming symmetry everywhere
2187      so as to avoid obtaining a non-invertible matrix in 2D cases. */
2188 
2189   for (int g_id = 0; g_id < n_b_groups; g_id++) {
2190 
2191 #   pragma omp parallel for
2192     for (int t_id = 0; t_id < n_b_threads; t_id++) {
2193 
2194       for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
2195            f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
2196            f_id++) {
2197 
2198         if (ce == NULL || !coupled_faces[f_id]) {
2199 
2200           cs_lnum_t ii = b_face_cells[f_id];
2201 
2202           cs_real_3_t normal;
2203           /* Normal is vector 0 if the b_face_normal norm is too small */
2204           cs_math_3_normalise(b_face_normal[f_id], normal);
2205 
2206           cocg[ii][0] += normal[0] * normal[0];
2207           cocg[ii][1] += normal[1] * normal[1];
2208           cocg[ii][2] += normal[2] * normal[2];
2209           cocg[ii][3] += normal[0] * normal[1];
2210           cocg[ii][4] += normal[1] * normal[2];
2211           cocg[ii][5] += normal[0] * normal[2];
2212 
2213         } /* face without internal coupling */
2214 
2215       } /* loop on faces */
2216 
2217     } /* loop on threads */
2218 
2219   } /* loop on thread groups */
2220 
2221   /* Invert for all cells. */
2222   /*-----------------------*/
2223 
2224   /* The cocg term for interior cells only changes if the mesh does */
2225 
2226 # pragma omp parallel for
2227   for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
2228     _math_6_inv_cramer_sym_in_place(cocg[c_id]);
2229   }
2230 }
2231 
2232 /*----------------------------------------------------------------------------
2233  * Return current symmetric 3x3 matrix cocg for least squares algorithm
2234  *
2235  * parameters:
2236  *   m          <--  mesh
2237  *   halo_type  <--  halo type
2238  *   accel      <--  use accelerator device (if true, cocg and cocgb
2239  *                   pointers returned are device pointers)
2240  *   fvq        <--  mesh quantities
2241  *   ce         <--  coupling entity
2242  *   cocg       -->  coupling coeffiences (covariance matrices)
2243  *   cocgb      -->  partial boundary coupling coeffients, or NULL
2244  *----------------------------------------------------------------------------*/
2245 
2246 static void
_get_cell_cocg_lsq(const cs_mesh_t * m,cs_halo_type_t halo_type,bool accel,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * ce,cs_cocg_6_t * restrict * cocg,cs_cocg_6_t * restrict * cocgb)2247 _get_cell_cocg_lsq(const cs_mesh_t               *m,
2248                    cs_halo_type_t                 halo_type,
2249                    bool                           accel,
2250                    const cs_mesh_quantities_t    *fvq,
2251                    const cs_internal_coupling_t  *ce,
2252                    cs_cocg_6_t                   *restrict *cocg,
2253                    cs_cocg_6_t                   *restrict *cocgb)
2254 {
2255   int gq_id = (ce == NULL) ? 0 : ce->id+1;
2256   cs_gradient_quantities_t  *gq = _gradient_quantities_get(gq_id);
2257 
2258   cs_cocg_6_t *_cocg = NULL, *_cocgb = NULL;
2259 
2260   bool extended = (   halo_type == CS_HALO_EXTENDED
2261                    && m->cell_cells_idx) ? true : false;
2262 
2263   if (extended) {
2264     _cocg = gq->cocg_lsq_ext;
2265     _cocgb = gq->cocgb_s_lsq_ext;
2266   }
2267   else {
2268     _cocg = gq->cocg_lsq;
2269     _cocgb = gq->cocgb_s_lsq;
2270   }
2271 
2272   /* Compute if not present yet.
2273    *
2274    * TODO: when using accelerators, this imples a first computation will be
2275    *       run on the host. This will usually be amortized, but could be
2276    *       further improved. */
2277 
2278   if (_cocg == NULL)
2279     _compute_cell_cocg_lsq(m, extended, fvq, ce, gq);
2280 
2281   /* If used on accelerator, ensure arrays are available there */
2282 
2283   if (accel) {
2284 
2285     cs_alloc_mode_t alloc_mode = CS_ALLOC_HOST_DEVICE_SHARED;
2286 
2287     void *_cocg_p = _cocg, *_cocgb_p = _cocgb;
2288 
2289     cs_set_alloc_mode(&_cocg_p, alloc_mode);
2290     cs_set_alloc_mode(&_cocgb_p, alloc_mode);
2291 
2292     if (extended) {
2293       cs_set_alloc_mode(&(gq->cocg_lsq_ext), alloc_mode);
2294       cs_set_alloc_mode(&(gq->cocgb_s_lsq_ext), alloc_mode);
2295     }
2296     else {
2297       cs_set_alloc_mode(&(gq->cocg_lsq), alloc_mode);
2298       cs_set_alloc_mode(&(gq->cocgb_s_lsq), alloc_mode);
2299     }
2300 
2301   }
2302 
2303   /* Set pointers */
2304 
2305   if (extended)
2306     *cocg = gq->cocg_lsq_ext;
2307   else
2308     *cocg = gq->cocg_lsq;
2309 
2310   if (cocgb != NULL) {
2311     if (extended)
2312       *cocgb = gq->cocgb_s_lsq_ext;
2313     else
2314       *cocgb = gq->cocgb_s_lsq;
2315   }
2316 
2317   /* If used on acclelerator, copy/prefetch values and switch to
2318      device pointers */
2319 
2320   if (accel) {
2321     cs_sync_h2d(*cocg);
2322     *cocg = cs_get_device_ptr(*cocg);
2323 
2324     if (cocgb != NULL) {
2325       cs_sync_h2d(*cocgb);
2326       *cocgb = cs_get_device_ptr(*cocgb);
2327     }
2328   }
2329 }
2330 
2331 /*----------------------------------------------------------------------------
2332  * Compute cell gradient using least-squares reconstruction for non-orthogonal
2333  * meshes (nswrgp > 1).
2334  *
2335  * Optionally, a volume force generating a hydrostatic pressure component
2336  * may be accounted for.
2337  *
2338  * cocg is computed to account for variable B.C.'s (flux).
2339  *
2340  * parameters:
2341  *   m              <-- pointer to associated mesh structure
2342  *   fvq            <-- pointer to associated finite volume quantities
2343  *   cpl            <-- structure associated with internal coupling, or NULL
2344  *   halo_type      <-- halo type (extended or not)
2345  *   recompute_cocg <-- flag to recompute cocg
2346  *   hyd_p_flag     <-- flag for hydrostatic pressure
2347  *   inc            <-- if 0, solve on increment; 1 otherwise
2348  *   fext           <-- exterior force generating pressure
2349  *   coefap         <-- B.C. coefficients for boundary face normals
2350  *   coefbp         <-- B.C. coefficients for boundary face normals
2351  *   pvar           <-- variable
2352  *   c_weight       <-- weighted gradient coefficient variable,
2353  *                      or NULL
2354  *   grad           --> gradient of pvar (halo prepared for periodicity
2355  *                      of rotation)
2356  *----------------------------------------------------------------------------*/
2357 
2358 static void
_lsq_scalar_gradient(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,cs_halo_type_t halo_type,bool recompute_cocg,int hyd_p_flag,cs_real_t inc,const cs_real_3_t f_ext[],const cs_real_t coefap[],const cs_real_t coefbp[],const cs_real_t pvar[],const cs_real_t * restrict c_weight,cs_real_3_t * restrict grad)2359 _lsq_scalar_gradient(const cs_mesh_t                *m,
2360                      const cs_mesh_quantities_t     *fvq,
2361                      const cs_internal_coupling_t   *cpl,
2362                      cs_halo_type_t                  halo_type,
2363                      bool                            recompute_cocg,
2364                      int                             hyd_p_flag,
2365                      cs_real_t                       inc,
2366                      const cs_real_3_t               f_ext[],
2367                      const cs_real_t                 coefap[],
2368                      const cs_real_t                 coefbp[],
2369                      const cs_real_t                 pvar[],
2370                      const cs_real_t       *restrict c_weight,
2371                      cs_real_3_t           *restrict grad)
2372 {
2373   const cs_lnum_t n_cells = m->n_cells;
2374   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
2375   const int n_i_groups = m->i_face_numbering->n_groups;
2376   const int n_i_threads = m->i_face_numbering->n_threads;
2377   const int n_b_groups = m->b_face_numbering->n_groups;
2378   const int n_b_threads = m->b_face_numbering->n_threads;
2379   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
2380   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
2381 
2382   const cs_lnum_2_t *restrict i_face_cells
2383     = (const cs_lnum_2_t *restrict)m->i_face_cells;
2384   const cs_lnum_t *restrict b_face_cells
2385     = (const cs_lnum_t *restrict)m->b_face_cells;
2386   const cs_lnum_t *restrict cell_cells_idx
2387     = (const cs_lnum_t *restrict)m->cell_cells_idx;
2388   const cs_lnum_t *restrict cell_cells_lst
2389     = (const cs_lnum_t *restrict)m->cell_cells_lst;
2390 
2391   const cs_real_3_t *restrict cell_cen
2392     = (const cs_real_3_t *restrict)fvq->cell_cen;
2393   const cs_real_3_t *restrict b_face_normal
2394     = (const cs_real_3_t *restrict)fvq->b_face_normal;
2395   const cs_real_t *restrict b_face_surf
2396     = (const cs_real_t *restrict)fvq->b_face_surf;
2397   const cs_real_t *restrict b_dist
2398     = (const cs_real_t *restrict)fvq->b_dist;
2399   const cs_real_3_t *restrict i_face_cog
2400     = (const cs_real_3_t *restrict)fvq->i_face_cog;
2401   const cs_real_3_t *restrict b_face_cog
2402     = (const cs_real_3_t *restrict)fvq->b_face_cog;
2403   const cs_real_3_t *restrict diipb
2404     = (const cs_real_3_t *restrict)fvq->diipb;
2405   const cs_real_t *restrict weight = fvq->weight;
2406 
2407   cs_cocg_6_t  *restrict cocgb = NULL;
2408   cs_cocg_6_t  *restrict cocg = NULL;
2409 
2410   /* Additional terms due to porosity */
2411   cs_field_t *f_i_poro_duq_0 = cs_field_by_name_try("i_poro_duq_0");
2412 
2413   cs_real_t *i_poro_duq_0;
2414   cs_real_t *i_poro_duq_1;
2415   cs_real_t *b_poro_duq;
2416   cs_real_t _f_ext = 0.;
2417 
2418   int is_porous = 0;
2419   if (f_i_poro_duq_0 != NULL) {
2420     is_porous = 1;
2421     i_poro_duq_0 = f_i_poro_duq_0->val;
2422     i_poro_duq_1 = cs_field_by_name("i_poro_duq_1")->val;
2423     b_poro_duq = cs_field_by_name("b_poro_duq")->val;
2424   } else {
2425     i_poro_duq_0 = &_f_ext;
2426     i_poro_duq_1 = &_f_ext;
2427     b_poro_duq = &_f_ext;
2428   }
2429 
2430 #if defined(HAVE_CUDA)
2431   bool accel = (   cs_get_device_id() > -1
2432                 && cpl == NULL
2433                 && hyd_p_flag == 0
2434                 && is_porous == false) ? true : false;
2435 #else
2436   bool accel = false;
2437 #endif
2438 
2439   _get_cell_cocg_lsq(m,
2440                      halo_type,
2441                      accel,
2442                      fvq,
2443                      cpl,
2444                      &cocg,
2445                      &cocgb);
2446 
2447 #if defined(HAVE_CUDA)
2448 
2449   if (accel) {
2450 
2451     cs_gradient_scalar_lsq_cuda(m,
2452                                 fvq,
2453                                 halo_type,
2454                                 recompute_cocg,
2455                                 hyd_p_flag,
2456                                 inc,
2457                                 f_ext,
2458                                 coefap,
2459                                 coefbp,
2460                                 pvar,
2461                                 c_weight,
2462                                 cocg,
2463                                 cocgb,
2464                                 grad);
2465 
2466     return;
2467 
2468   }
2469 
2470 #endif
2471 
2472   bool  *coupled_faces = (cpl == NULL) ?
2473     NULL : (bool *)cpl->coupled_faces;
2474 
2475   /* Reconstruct gradients using least squares for non-orthogonal meshes */
2476   /*---------------------------------------------------------------------*/
2477 
2478   /* Compute cocg and save contribution at boundaries */
2479 
2480   if (recompute_cocg) {
2481 
2482     /* Recompute cocg at boundaries, using saved cocgb */
2483 
2484 #   pragma omp parallel for
2485     for (cs_lnum_t ii = 0; ii < m->n_b_cells; ii++) {
2486       cs_lnum_t c_id = m->b_cells[ii];
2487       for (cs_lnum_t ll = 0; ll < 6; ll++)
2488         cocg[c_id][ll] = cocgb[ii][ll];
2489     }
2490 
2491     for (int g_id = 0; g_id < n_b_groups; g_id++) {
2492 
2493 #     pragma omp parallel for
2494       for (int t_id = 0; t_id < n_b_threads; t_id++) {
2495 
2496         for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
2497              f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
2498              f_id++) {
2499 
2500           if (cpl == NULL || !coupled_faces[f_id]) {
2501 
2502             cs_lnum_t ii = b_face_cells[f_id];
2503 
2504             cs_real_t umcbdd = (1. - coefbp[f_id]) / b_dist[f_id];
2505             cs_real_t udbfs = 1. / b_face_surf[f_id];
2506 
2507             cs_real_t dddij[3];
2508             for (cs_lnum_t ll = 0; ll < 3; ll++)
2509               dddij[ll] =   udbfs * b_face_normal[f_id][ll]
2510                           + umcbdd * diipb[f_id][ll];
2511 
2512             cocg[ii][0] += dddij[0]*dddij[0];
2513             cocg[ii][1] += dddij[1]*dddij[1];
2514             cocg[ii][2] += dddij[2]*dddij[2];
2515             cocg[ii][3] += dddij[0]*dddij[1];
2516             cocg[ii][4] += dddij[1]*dddij[2];
2517             cocg[ii][5] += dddij[0]*dddij[2];
2518 
2519           }  /* face without internal coupling */
2520 
2521         } /* loop on faces */
2522 
2523       } /* loop on threads */
2524 
2525     } /* loop on thread groups */
2526 
2527 #   pragma omp parallel for
2528     for (cs_lnum_t ii = 0; ii < m->n_b_cells; ii++) {
2529       cs_lnum_t c_id = m->b_cells[ii];
2530       _math_6_inv_cramer_sym_in_place(cocg[c_id]);
2531     }
2532 
2533   } /* End of recompute_cocg */
2534 
2535   /* Compute Right-Hand Side */
2536   /*-------------------------*/
2537 
2538   cs_real_4_t  *restrict rhsv;
2539   BFT_MALLOC(rhsv, n_cells_ext, cs_real_4_t);
2540 
2541 # pragma omp parallel for
2542   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
2543     rhsv[c_id][0] = 0.0;
2544     rhsv[c_id][1] = 0.0;
2545     rhsv[c_id][2] = 0.0;
2546     rhsv[c_id][3] = pvar[c_id];
2547   }
2548 
2549   /* Standard case, without hydrostatic pressure */
2550   /*---------------------------------------------*/
2551 
2552   if (hyd_p_flag == 0) {
2553 
2554     /* Contribution from interior faces */
2555 
2556     for (int g_id = 0; g_id < n_i_groups; g_id++) {
2557 
2558 #     pragma omp parallel for
2559       for (int t_id = 0; t_id < n_i_threads; t_id++) {
2560 
2561         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
2562              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
2563              f_id++) {
2564 
2565           cs_lnum_t ii = i_face_cells[f_id][0];
2566           cs_lnum_t jj = i_face_cells[f_id][1];
2567 
2568           cs_real_t pond = weight[f_id];
2569 
2570           cs_real_t pfac, dc[3], fctb[4];
2571 
2572           for (cs_lnum_t ll = 0; ll < 3; ll++)
2573             dc[ll] = cell_cen[jj][ll] - cell_cen[ii][ll];
2574 
2575           if (c_weight != NULL) {
2576             /* (P_j - P_i) / ||d||^2 */
2577             pfac =   (rhsv[jj][3] - rhsv[ii][3])
2578                    / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
2579 
2580             for (cs_lnum_t ll = 0; ll < 3; ll++)
2581               fctb[ll] = dc[ll] * pfac;
2582 
2583             cs_real_t denom = 1. / (  pond       *c_weight[ii]
2584                                     + (1. - pond)*c_weight[jj]);
2585 
2586             for (cs_lnum_t ll = 0; ll < 3; ll++)
2587               rhsv[ii][ll] +=  c_weight[jj] * denom * fctb[ll];
2588 
2589             for (cs_lnum_t ll = 0; ll < 3; ll++)
2590               rhsv[jj][ll] +=  c_weight[ii] * denom * fctb[ll];
2591           }
2592           else {
2593             /* (P_j - P_i) / ||d||^2 */
2594             pfac =   (rhsv[jj][3] - rhsv[ii][3])
2595                    / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
2596 
2597             for (cs_lnum_t ll = 0; ll < 3; ll++)
2598               fctb[ll] = dc[ll] * pfac;
2599 
2600             for (cs_lnum_t ll = 0; ll < 3; ll++)
2601               rhsv[ii][ll] += fctb[ll];
2602 
2603             for (cs_lnum_t ll = 0; ll < 3; ll++)
2604               rhsv[jj][ll] += fctb[ll];
2605           }
2606 
2607         } /* loop on faces */
2608 
2609       } /* loop on threads */
2610 
2611     } /* loop on thread groups */
2612 
2613     /* Contribution from extended neighborhood */
2614 
2615     if (halo_type == CS_HALO_EXTENDED && cell_cells_idx != NULL) {
2616 
2617 #     pragma omp parallel for
2618       for (cs_lnum_t ii = 0; ii < n_cells; ii++) {
2619         for (cs_lnum_t cidx = cell_cells_idx[ii];
2620              cidx < cell_cells_idx[ii+1];
2621              cidx++) {
2622 
2623           cs_lnum_t jj = cell_cells_lst[cidx];
2624 
2625           cs_real_t pfac, dc[3], fctb[4];
2626 
2627           for (cs_lnum_t ll = 0; ll < 3; ll++)
2628             dc[ll] = cell_cen[jj][ll] - cell_cen[ii][ll];
2629 
2630           pfac =   (rhsv[jj][3] - rhsv[ii][3])
2631                  / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
2632 
2633           for (cs_lnum_t ll = 0; ll < 3; ll++)
2634             fctb[ll] = dc[ll] * pfac;
2635 
2636           for (cs_lnum_t ll = 0; ll < 3; ll++)
2637             rhsv[ii][ll] += fctb[ll];
2638 
2639         }
2640       }
2641 
2642     } /* End for extended neighborhood */
2643 
2644     /* Contribution from coupled faces */
2645 
2646     if (cpl != NULL)
2647       cs_internal_coupling_lsq_scalar_gradient
2648         (cpl, c_weight, 1, rhsv);
2649 
2650     /* Contribution from boundary faces */
2651 
2652     for (int g_id = 0; g_id < n_b_groups; g_id++) {
2653 
2654 #     pragma omp parallel for
2655       for (int t_id = 0; t_id < n_b_threads; t_id++) {
2656 
2657         for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
2658              f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
2659              f_id++) {
2660 
2661           if (cpl == NULL || !coupled_faces[f_id]) {
2662 
2663             cs_lnum_t ii = b_face_cells[f_id];
2664 
2665             cs_real_t unddij = 1. / b_dist[f_id];
2666             cs_real_t udbfs = 1. / b_face_surf[f_id];
2667             cs_real_t umcbdd = (1. - coefbp[f_id]) * unddij;
2668 
2669             cs_real_t dsij[3];
2670             for (cs_lnum_t ll = 0; ll < 3; ll++)
2671               dsij[ll] =   udbfs * b_face_normal[f_id][ll]
2672                          + umcbdd*diipb[f_id][ll];
2673 
2674             cs_real_t pfac =   (coefap[f_id]*inc + (coefbp[f_id] -1.)
2675                              * rhsv[ii][3]) * unddij;
2676 
2677             for (cs_lnum_t ll = 0; ll < 3; ll++)
2678               rhsv[ii][ll] += dsij[ll] * pfac;
2679 
2680           } /* face without internal coupling */
2681 
2682         } /* loop on faces */
2683 
2684       } /* loop on threads */
2685 
2686     } /* loop on thread groups */
2687 
2688   }
2689 
2690   /* Case with hydrostatic pressure */
2691   /*--------------------------------*/
2692 
2693   else {  /* if hyd_p_flag == 1 */
2694 
2695     /* Contribution from interior faces */
2696 
2697     for (int g_id = 0; g_id < n_i_groups; g_id++) {
2698 
2699 #     pragma omp parallel for
2700       for (int t_id = 0; t_id < n_i_threads; t_id++) {
2701 
2702         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
2703              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
2704              f_id++) {
2705 
2706           cs_lnum_t ii = i_face_cells[f_id][0];
2707           cs_lnum_t jj = i_face_cells[f_id][1];
2708 
2709           cs_real_2_t poro = {
2710             i_poro_duq_0[is_porous*f_id],
2711             i_poro_duq_1[is_porous*f_id]
2712           };
2713 
2714           cs_real_t pond = weight[f_id];
2715 
2716           cs_real_t pfac, dc[3], fctb[4];
2717 
2718           for (cs_lnum_t ll = 0; ll < 3; ll++)
2719             dc[ll] = cell_cen[jj][ll] - cell_cen[ii][ll];
2720 
2721           pfac =   (  rhsv[jj][3] - rhsv[ii][3]
2722                     + (cell_cen[ii][0] - i_face_cog[f_id][0]) * f_ext[ii][0]
2723                     + (cell_cen[ii][1] - i_face_cog[f_id][1]) * f_ext[ii][1]
2724                     + (cell_cen[ii][2] - i_face_cog[f_id][2]) * f_ext[ii][2]
2725                     + poro[0]
2726                     - (cell_cen[jj][0] - i_face_cog[f_id][0]) * f_ext[jj][0]
2727                     - (cell_cen[jj][1] - i_face_cog[f_id][1]) * f_ext[jj][1]
2728                     - (cell_cen[jj][2] - i_face_cog[f_id][2]) * f_ext[jj][2]
2729                     - poro[1])
2730                   / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
2731 
2732           for (cs_lnum_t ll = 0; ll < 3; ll++)
2733             fctb[ll] = dc[ll] * pfac;
2734 
2735           if (c_weight != NULL) {
2736               cs_real_t denom = 1. / (  pond       *c_weight[ii]
2737                                       + (1. - pond)*c_weight[jj]);
2738 
2739               for (cs_lnum_t ll = 0; ll < 3; ll++)
2740                 rhsv[ii][ll] += c_weight[jj] * denom * fctb[ll];
2741 
2742               for (cs_lnum_t ll = 0; ll < 3; ll++)
2743                 rhsv[jj][ll] += c_weight[ii] * denom * fctb[ll];
2744           }
2745           else { // no cell weighting
2746             for (cs_lnum_t ll = 0; ll < 3; ll++)
2747               rhsv[ii][ll] += fctb[ll];
2748 
2749             for (cs_lnum_t ll = 0; ll < 3; ll++)
2750               rhsv[jj][ll] += fctb[ll];
2751           }
2752         } /* loop on faces */
2753 
2754       } /* loop on threads */
2755 
2756     } /* loop on thread groups */
2757 
2758     /* Contribution from extended neighborhood;
2759        We assume that the middle of the segment joining cell centers
2760        may replace the center of gravity of a fictitious face. */
2761 
2762     if (halo_type == CS_HALO_EXTENDED && cell_cells_idx != NULL) {
2763 
2764 #     pragma omp parallel for
2765       for (cs_lnum_t ii = 0; ii < n_cells; ii++) {
2766         for (cs_lnum_t cidx = cell_cells_idx[ii];
2767              cidx < cell_cells_idx[ii+1];
2768              cidx++) {
2769 
2770           cs_lnum_t jj = cell_cells_lst[cidx];
2771 
2772           /* Note: replaced the expressions:
2773            *  a) ptmid = 0.5 * (cell_cen[jj] - cell_cen[ii])
2774            *  b)   (cell_cen[ii] - ptmid) * f_ext[ii]
2775            *  c) - (cell_cen[jj] - ptmid) * f_ext[jj]
2776            * with:
2777            *  a) dc = cell_cen[jj] - cell_cen[ii]
2778            *  b) - 0.5 * dc * f_ext[ii]
2779            *  c) - 0.5 * dc * f_ext[jj]
2780            */
2781 
2782           cs_real_t pfac, dc[3], fctb[4];
2783 
2784           for (cs_lnum_t ll = 0; ll < 3; ll++)
2785             dc[ll] = cell_cen[jj][ll] - cell_cen[ii][ll];
2786 
2787           pfac =   (  rhsv[jj][3] - rhsv[ii][3]
2788                     - 0.5 * dc[0] * f_ext[ii][0]
2789                     - 0.5 * dc[1] * f_ext[ii][1]
2790                     - 0.5 * dc[2] * f_ext[ii][2]
2791                     - 0.5 * dc[0] * f_ext[jj][0]
2792                     - 0.5 * dc[1] * f_ext[jj][1]
2793                     - 0.5 * dc[2] * f_ext[jj][2])
2794                   / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
2795 
2796           for (cs_lnum_t ll = 0; ll < 3; ll++)
2797             fctb[ll] = dc[ll] * pfac;
2798 
2799           for (cs_lnum_t ll = 0; ll < 3; ll++)
2800             rhsv[ii][ll] += fctb[ll];
2801 
2802         }
2803       }
2804 
2805     } /* End for extended neighborhood */
2806 
2807     /* Contribution from boundary faces */
2808 
2809     for (int g_id = 0; g_id < n_b_groups; g_id++) {
2810 
2811 #     pragma omp parallel for
2812       for (int t_id = 0; t_id < n_b_threads; t_id++) {
2813 
2814         for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
2815              f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
2816              f_id++) {
2817 
2818           cs_lnum_t ii = b_face_cells[f_id];
2819 
2820           cs_real_t poro = b_poro_duq[is_porous*f_id];
2821 
2822           cs_real_t unddij = 1. / b_dist[f_id];
2823           cs_real_t udbfs = 1. / b_face_surf[f_id];
2824           cs_real_t umcbdd = (1. - coefbp[f_id]) * unddij;
2825 
2826           cs_real_t dsij[3];
2827           for (cs_lnum_t ll = 0; ll < 3; ll++)
2828             dsij[ll] =   udbfs * b_face_normal[f_id][ll]
2829                        + umcbdd*diipb[f_id][ll];
2830 
2831           cs_real_t pfac
2832             =   (coefap[f_id]*inc
2833               + (  (coefbp[f_id] -1.)
2834                  * (  rhsv[ii][3]
2835                     + (b_face_cog[f_id][0] - cell_cen[ii][0]) * f_ext[ii][0]
2836                     + (b_face_cog[f_id][1] - cell_cen[ii][1]) * f_ext[ii][1]
2837                     + (b_face_cog[f_id][2] - cell_cen[ii][2]) * f_ext[ii][2]
2838                     + poro)))
2839               * unddij;
2840 
2841           for (cs_lnum_t ll = 0; ll < 3; ll++)
2842             rhsv[ii][ll] += dsij[ll] * pfac;
2843 
2844         } /* loop on faces */
2845 
2846       } /* loop on threads */
2847 
2848     } /* loop on thread groups */
2849 
2850   } /* End of test on hydrostatic pressure */
2851 
2852   /* Compute gradient */
2853   /*------------------*/
2854 
2855   if (hyd_p_flag == 1) {
2856 
2857 #   pragma omp parallel for
2858     for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
2859       grad[c_id][0] =   cocg[c_id][0] *rhsv[c_id][0]
2860                       + cocg[c_id][3] *rhsv[c_id][1]
2861                       + cocg[c_id][5] *rhsv[c_id][2]
2862                       + f_ext[c_id][0];
2863       grad[c_id][1] =   cocg[c_id][3] *rhsv[c_id][0]
2864                       + cocg[c_id][1] *rhsv[c_id][1]
2865                       + cocg[c_id][4] *rhsv[c_id][2]
2866                       + f_ext[c_id][1];
2867       grad[c_id][2] =   cocg[c_id][5] *rhsv[c_id][0]
2868                       + cocg[c_id][4] *rhsv[c_id][1]
2869                       + cocg[c_id][2] *rhsv[c_id][2]
2870                       + f_ext[c_id][2];
2871     }
2872 
2873   }
2874   else {
2875 
2876 #   pragma omp parallel for
2877     for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
2878       grad[c_id][0] =   cocg[c_id][0] *rhsv[c_id][0]
2879                       + cocg[c_id][3] *rhsv[c_id][1]
2880                       + cocg[c_id][5] *rhsv[c_id][2];
2881       grad[c_id][1] =   cocg[c_id][3] *rhsv[c_id][0]
2882                       + cocg[c_id][1] *rhsv[c_id][1]
2883                       + cocg[c_id][4] *rhsv[c_id][2];
2884       grad[c_id][2] =   cocg[c_id][5] *rhsv[c_id][0]
2885                       + cocg[c_id][4] *rhsv[c_id][1]
2886                       + cocg[c_id][2] *rhsv[c_id][2];
2887     }
2888 
2889   }
2890 
2891   /* Synchronize halos */
2892 
2893   _sync_scalar_gradient_halo(m, CS_HALO_STANDARD, grad);
2894 
2895   BFT_FREE(rhsv);
2896 }
2897 
2898 /*----------------------------------------------------------------------------
2899  * Compute cell gradient using least-squares reconstruction for non-orthogonal
2900  * meshes (nswrgp > 1) in the anisotropic case.
2901  *
2902  * cocg is computed to account for variable B.C.'s (flux).
2903  *
2904  * parameters:
2905  *   m              <-- pointer to associated mesh structure
2906  *   fvq            <-- pointer to associated finite volume quantities
2907  *   cpl            <-- structure associated with internal coupling, or NULL
2908  *   w_stride       <-- stride for weighting coefficient
2909  *   inc            <-- if 0, solve on increment; 1 otherwise
2910  *   coefap         <-- B.C. coefficients for boundary face normals
2911  *   coefbp         <-- B.C. coefficients for boundary face normals
2912  *   pvar           <-- variable
2913  *   c_weight       <-- weighted gradient coefficient variable,
2914  *                      or NULL
2915  *   grad           <-> gradient of pvar (halo prepared for periodicity
2916  *                      of rotation)
2917  *----------------------------------------------------------------------------*/
2918 
2919 static void
_lsq_scalar_gradient_ani(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,cs_real_t inc,const cs_real_t coefap[],const cs_real_t coefbp[],const cs_real_t pvar[],const cs_real_t c_weight[restrict][6],cs_real_t grad[restrict][3])2920 _lsq_scalar_gradient_ani(const cs_mesh_t               *m,
2921                          const cs_mesh_quantities_t    *fvq,
2922                          const cs_internal_coupling_t  *cpl,
2923                          cs_real_t                      inc,
2924                          const cs_real_t                coefap[],
2925                          const cs_real_t                coefbp[],
2926                          const cs_real_t                pvar[],
2927                          const cs_real_t                c_weight[restrict][6],
2928                          cs_real_t                      grad[restrict][3])
2929 {
2930   const cs_lnum_t n_cells = m->n_cells;
2931   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
2932   const int n_i_groups = m->i_face_numbering->n_groups;
2933   const int n_i_threads = m->i_face_numbering->n_threads;
2934   const int n_b_groups = m->b_face_numbering->n_groups;
2935   const int n_b_threads = m->b_face_numbering->n_threads;
2936   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
2937   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
2938 
2939   const cs_lnum_2_t *restrict i_face_cells
2940     = (const cs_lnum_2_t *restrict)m->i_face_cells;
2941   const cs_lnum_t *restrict b_face_cells
2942     = (const cs_lnum_t *restrict)m->b_face_cells;
2943 
2944   const cs_real_3_t *restrict cell_cen
2945     = (const cs_real_3_t *restrict)fvq->cell_cen;
2946   const cs_real_3_t *restrict b_face_normal
2947     = (const cs_real_3_t *restrict)fvq->b_face_normal;
2948   const cs_real_t *restrict b_face_surf
2949     = (const cs_real_t *restrict)fvq->b_face_surf;
2950   const cs_real_t *restrict b_dist
2951     = (const cs_real_t *restrict)fvq->b_dist;
2952   const cs_real_3_t *restrict diipb
2953     = (const cs_real_3_t *restrict)fvq->diipb;
2954   const cs_real_t *restrict weight = fvq->weight;
2955 
2956   bool  *coupled_faces = (cpl == NULL) ?
2957     NULL : (bool *)cpl->coupled_faces;
2958 
2959   cs_real_4_t  *restrict rhsv;
2960   BFT_MALLOC(rhsv, n_cells_ext, cs_real_4_t);
2961 
2962   cs_cocg_6_t  *restrict cocg = NULL;
2963   BFT_MALLOC(cocg, n_cells_ext, cs_cocg_6_t);
2964 
2965 # pragma omp parallel for
2966   for (cs_lnum_t cell_id = 0; cell_id < n_cells_ext; cell_id++) {
2967     for (cs_lnum_t ll = 0; ll < 6; ll++)
2968       cocg[cell_id][ll] = 0.0;
2969   }
2970 
2971 # pragma omp parallel for
2972   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
2973     rhsv[c_id][0] = 0.0;
2974     rhsv[c_id][1] = 0.0;
2975     rhsv[c_id][2] = 0.0;
2976     rhsv[c_id][3] = pvar[c_id];
2977   }
2978 
2979   /* Reconstruct gradients using least squares for non-orthogonal meshes */
2980   /*---------------------------------------------------------------------*/
2981 
2982   /* Contribution from interior faces */
2983 
2984   for (int g_id = 0; g_id < n_i_groups; g_id++) {
2985 
2986 #   pragma omp parallel for
2987     for (int t_id = 0; t_id < n_i_threads; t_id++) {
2988 
2989       for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
2990            f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
2991            f_id++) {
2992 
2993         cs_real_t dc[3], dc_i[3], dc_j[3];
2994 
2995         cs_lnum_t ii = i_face_cells[f_id][0];
2996         cs_lnum_t jj = i_face_cells[f_id][1];
2997 
2998         cs_real_t pond = weight[f_id];
2999 
3000         for (cs_lnum_t ll = 0; ll < 3; ll++)
3001           dc[ll] = cell_cen[jj][ll] - cell_cen[ii][ll];
3002 
3003         /* cocg contribution */
3004 
3005         _compute_ani_weighting_cocg(c_weight[ii],
3006                                     c_weight[jj],
3007                                     dc,
3008                                     pond,
3009                                     dc_i,
3010                                     dc_j);
3011 
3012         cs_real_t i_dci = 1. / cs_math_3_square_norm(dc_i);
3013         cs_real_t i_dcj = 1. / cs_math_3_square_norm(dc_j);
3014 
3015         cocg[ii][0] += dc_i[0] * dc_i[0] * i_dci;
3016         cocg[ii][1] += dc_i[1] * dc_i[1] * i_dci;
3017         cocg[ii][2] += dc_i[2] * dc_i[2] * i_dci;
3018         cocg[ii][3] += dc_i[0] * dc_i[1] * i_dci;
3019         cocg[ii][4] += dc_i[1] * dc_i[2] * i_dci;
3020         cocg[ii][5] += dc_i[0] * dc_i[2] * i_dci;
3021 
3022         cocg[jj][0] += dc_j[0] * dc_j[0] * i_dcj;
3023         cocg[jj][1] += dc_j[1] * dc_j[1] * i_dcj;
3024         cocg[jj][2] += dc_j[2] * dc_j[2] * i_dcj;
3025         cocg[jj][3] += dc_j[0] * dc_j[1] * i_dcj;
3026         cocg[jj][4] += dc_j[1] * dc_j[2] * i_dcj;
3027         cocg[jj][5] += dc_j[0] * dc_j[2] * i_dcj;
3028 
3029         /* RHS contribution */
3030 
3031         /* (P_j - P_i)*/
3032         cs_real_t p_diff = (rhsv[jj][3] - rhsv[ii][3]);
3033 
3034         _compute_ani_weighting(c_weight[ii],
3035                                c_weight[jj],
3036                                p_diff,
3037                                dc,
3038                                pond,
3039                                rhsv[ii],
3040                                rhsv[jj]);
3041       } /* loop on faces */
3042 
3043     } /* loop on threads */
3044 
3045   } /* loop on thread groups */
3046 
3047   /* Contribution from coupled faces */
3048 
3049   if (cpl != NULL) {
3050     cs_internal_coupling_lsq_cocg_weighted
3051       (cpl, (const cs_real_t *)c_weight, cocg);
3052     cs_internal_coupling_lsq_scalar_gradient
3053       (cpl, (const cs_real_t *)c_weight, 6, rhsv);
3054   }
3055 
3056   /* Contribution from boundary faces */
3057 
3058   for (int g_id = 0; g_id < n_b_groups; g_id++) {
3059 
3060 #   pragma omp parallel for
3061     for (int t_id = 0; t_id < n_b_threads; t_id++) {
3062 
3063       for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
3064            f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
3065            f_id++) {
3066 
3067         if (cpl == NULL || !coupled_faces[f_id]) {
3068 
3069           cs_real_t dsij[3];
3070 
3071           cs_lnum_t ii = b_face_cells[f_id];
3072 
3073           cs_real_t umcbdd = (1. - coefbp[f_id]) / b_dist[f_id];
3074           cs_real_t udbfs = 1. / b_face_surf[f_id];
3075 
3076           cs_real_t unddij = 1. / b_dist[f_id];
3077 
3078           for (cs_lnum_t ll = 0; ll < 3; ll++)
3079             dsij[ll] =   udbfs * b_face_normal[f_id][ll]
3080                        + umcbdd*diipb[f_id][ll];
3081 
3082           /* cocg contribution */
3083 
3084           cocg[ii][0] += dsij[0]*dsij[0];
3085           cocg[ii][1] += dsij[1]*dsij[1];
3086           cocg[ii][2] += dsij[2]*dsij[2];
3087           cocg[ii][3] += dsij[0]*dsij[1];
3088           cocg[ii][4] += dsij[1]*dsij[2];
3089           cocg[ii][5] += dsij[0]*dsij[2];
3090 
3091           /* RHS contribution */
3092 
3093           cs_real_t pfac =   (coefap[f_id]*inc + (coefbp[f_id] -1.)*rhsv[ii][3])
3094                            * unddij;
3095 
3096           for (cs_lnum_t ll = 0; ll < 3; ll++)
3097             rhsv[ii][ll] += dsij[ll] * pfac;
3098 
3099         } /* face without internal coupling */
3100 
3101       } /* loop on faces */
3102 
3103     } /* loop on threads */
3104 
3105   } /* loop on thread groups */
3106 
3107   /* Invert cocg for all cells. */
3108   /*----------------------------*/
3109 
3110 # pragma omp parallel for
3111   for (cs_lnum_t cell_id = 0; cell_id < n_cells; cell_id++)
3112     _math_6_inv_cramer_sym_in_place(cocg[cell_id]);
3113 
3114   /* Compute gradient */
3115   /*------------------*/
3116 
3117 # pragma omp parallel for
3118   for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
3119     grad[c_id][0] =   cocg[c_id][0] * rhsv[c_id][0]
3120                     + cocg[c_id][3] * rhsv[c_id][1]
3121                     + cocg[c_id][5] * rhsv[c_id][2];
3122     grad[c_id][1] =   cocg[c_id][3] * rhsv[c_id][0]
3123                     + cocg[c_id][1] * rhsv[c_id][1]
3124                     + cocg[c_id][4] * rhsv[c_id][2];
3125     grad[c_id][2] =   cocg[c_id][5] * rhsv[c_id][0]
3126                     + cocg[c_id][4] * rhsv[c_id][1]
3127                     + cocg[c_id][2] * rhsv[c_id][2];
3128   }
3129 
3130   /* Synchronize halos */
3131 
3132   _sync_scalar_gradient_halo(m, CS_HALO_STANDARD, grad);
3133 
3134   BFT_FREE(cocg);
3135   BFT_FREE(rhsv);
3136 }
3137 
3138 /*----------------------------------------------------------------------------
3139  * Reconstruct the gradient of a scalar using a given gradient of
3140  * this scalar (typically lsq).
3141  *
3142  * Optionally, a volume force generating a hydrostatic pressure component
3143  * may be accounted for.
3144  *
3145  * parameters:
3146  *   m              <-- pointer to associated mesh structure
3147  *   fvq            <-- pointer to associated finite volume quantities
3148  *   cpl            <-> structure associated with internal coupling, or NULL
3149  *   hyd_p_flag     <-- flag for hydrostatic pressure
3150  *   inc            <-- if 0, solve on increment; 1 otherwise
3151  *   f_ext          <-- exterior force generating pressure
3152  *   coefap         <-- B.C. coefficients for boundary face normals
3153  *   coefbp         <-- B.C. coefficients for boundary face normals
3154  *   c_weight       <-- weighted gradient coefficient variable
3155  *   c_var          <-- variable
3156  *   r_grad         <-- gradient used for reconstruction
3157  *   grad           <-> gradient of c_var (halo prepared for periodicity
3158  *                      of rotation)
3159  *----------------------------------------------------------------------------*/
3160 
3161 static void
_reconstruct_scalar_gradient(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,int hyd_p_flag,cs_real_t inc,const cs_real_t f_ext[][3],const cs_real_t coefap[],const cs_real_t coefbp[],const cs_real_t c_weight[],const cs_real_t c_var[],cs_real_3_t * restrict r_grad,cs_real_3_t * restrict grad)3162 _reconstruct_scalar_gradient(const cs_mesh_t                 *m,
3163                              const cs_mesh_quantities_t      *fvq,
3164                              const cs_internal_coupling_t    *cpl,
3165                              int                              hyd_p_flag,
3166                              cs_real_t                        inc,
3167                              const cs_real_t                  f_ext[][3],
3168                              const cs_real_t                  coefap[],
3169                              const cs_real_t                  coefbp[],
3170                              const cs_real_t                  c_weight[],
3171                              const cs_real_t                  c_var[],
3172                              cs_real_3_t            *restrict r_grad,
3173                              cs_real_3_t            *restrict grad)
3174 {
3175   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
3176   const cs_lnum_t n_cells = m->n_cells;
3177   const int n_i_groups = m->i_face_numbering->n_groups;
3178   const int n_i_threads = m->i_face_numbering->n_threads;
3179   const int n_b_groups = m->b_face_numbering->n_groups;
3180   const int n_b_threads = m->b_face_numbering->n_threads;
3181   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
3182   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
3183 
3184   const cs_lnum_2_t *restrict i_face_cells
3185     = (const cs_lnum_2_t *restrict)m->i_face_cells;
3186   const cs_lnum_t *restrict b_face_cells
3187     = (const cs_lnum_t *restrict)m->b_face_cells;
3188 
3189   const int *restrict c_disable_flag = fvq->c_disable_flag;
3190   cs_lnum_t has_dc = fvq->has_disable_flag; /* Has cells disabled? */
3191 
3192   const cs_real_t *restrict weight = fvq->weight;
3193   const cs_real_t *restrict cell_f_vol = fvq->cell_f_vol;
3194   if (cs_glob_porous_model == 1 || cs_glob_porous_model == 2)
3195     cell_f_vol = fvq->cell_vol;
3196   const cs_real_3_t *restrict cell_cen
3197     = (const cs_real_3_t *restrict)fvq->cell_cen;
3198   const cs_real_3_t *restrict i_f_face_normal
3199     = (const cs_real_3_t *restrict)fvq->i_f_face_normal;
3200   const cs_real_3_t *restrict b_f_face_normal
3201     = (const cs_real_3_t *restrict)fvq->b_f_face_normal;
3202   const cs_real_3_t *restrict i_face_cog
3203     = (const cs_real_3_t *restrict)fvq->i_face_cog;
3204   const cs_real_3_t *restrict b_face_cog
3205     = (const cs_real_3_t *restrict)fvq->b_face_cog;
3206 
3207   const cs_real_3_t *restrict dofij
3208     = (const cs_real_3_t *restrict)fvq->dofij;
3209   const cs_real_3_t *restrict diipb
3210     = (const cs_real_3_t *restrict)fvq->diipb;
3211 
3212   const cs_real_33_t *restrict corr_grad_lin
3213     = (const cs_real_33_t *restrict)fvq->corr_grad_lin;
3214 
3215   bool  *coupled_faces = (cpl == NULL) ?
3216     NULL : (bool *)cpl->coupled_faces;
3217 
3218   /*Additional terms due to porosity */
3219   cs_field_t *f_i_poro_duq_0 = cs_field_by_name_try("i_poro_duq_0");
3220 
3221   cs_real_t *i_poro_duq_0;
3222   cs_real_t *i_poro_duq_1;
3223   cs_real_t *b_poro_duq;
3224   cs_real_t _f_ext = 0.;
3225 
3226   cs_lnum_t is_porous = 0;
3227   if (f_i_poro_duq_0 != NULL) {
3228     is_porous = 1;
3229     i_poro_duq_0 = f_i_poro_duq_0->val;
3230     i_poro_duq_1 = cs_field_by_name("i_poro_duq_1")->val;
3231     b_poro_duq = cs_field_by_name("b_poro_duq")->val;
3232   } else {
3233     i_poro_duq_0 = &_f_ext;
3234     i_poro_duq_1 = &_f_ext;
3235     b_poro_duq = &_f_ext;
3236   }
3237 
3238   /* Initialize gradient */
3239   /*---------------------*/
3240 
3241 # pragma omp parallel for
3242   for (cs_lnum_t cell_id = 0; cell_id < n_cells_ext; cell_id++) {
3243     for (cs_lnum_t j = 0; j < 3; j++)
3244       grad[cell_id][j] = 0.0;
3245   }
3246 
3247   /* Case with hydrostatic pressure */
3248   /*--------------------------------*/
3249 
3250   if (hyd_p_flag == 1) {
3251 
3252     /* Contribution from interior faces */
3253 
3254     for (int g_id = 0; g_id < n_i_groups; g_id++) {
3255 
3256 #     pragma omp parallel for
3257       for (int t_id = 0; t_id < n_i_threads; t_id++) {
3258 
3259         cs_real_t  fexd[3];
3260 
3261         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
3262              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
3263              f_id++) {
3264 
3265           cs_lnum_t c_id1 = i_face_cells[f_id][0];
3266           cs_lnum_t c_id2 = i_face_cells[f_id][1];
3267 
3268           cs_real_t ktpond = (c_weight == NULL) ?
3269              weight[f_id] :              /* no cell weighting */
3270              weight[f_id] * c_weight[c_id1] /* cell weighting active */
3271                / (      weight[f_id] * c_weight[c_id1]
3272                  + (1.0-weight[f_id])* c_weight[c_id2]);
3273 
3274           cs_real_2_t poro = {
3275             i_poro_duq_0[is_porous*f_id],
3276             i_poro_duq_1[is_porous*f_id]
3277           };
3278 
3279           fexd[0] = 0.5 * (f_ext[c_id1][0] + f_ext[c_id2][0]);
3280           fexd[1] = 0.5 * (f_ext[c_id1][1] + f_ext[c_id2][1]);
3281           fexd[2] = 0.5 * (f_ext[c_id1][2] + f_ext[c_id2][2]);
3282 
3283           /*
3284              Remark: \f$ \varia_\face = \alpha_\ij \varia_\celli
3285                                       + (1-\alpha_\ij) \varia_\cellj\f$
3286                      but for the cell \f$ \celli \f$ we remove
3287                      \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
3288                      and for the cell \f$ \cellj \f$ we remove
3289                      \f$ \varia_\cellj \sum_\face \vect{S}_\face = \vect{0} \f$
3290           */
3291 
3292           cs_real_t pfaci
3293             =  ktpond
3294                  * (  (i_face_cog[f_id][0] - cell_cen[c_id1][0])*f_ext[c_id1][0]
3295                     + (i_face_cog[f_id][1] - cell_cen[c_id1][1])*f_ext[c_id1][1]
3296                     + (i_face_cog[f_id][2] - cell_cen[c_id1][2])*f_ext[c_id1][2]
3297                     + poro[0])
3298             +  (1.0 - ktpond)
3299                  * (  (i_face_cog[f_id][0] - cell_cen[c_id2][0])*f_ext[c_id2][0]
3300                     + (i_face_cog[f_id][1] - cell_cen[c_id2][1])*f_ext[c_id2][1]
3301                     + (i_face_cog[f_id][2] - cell_cen[c_id2][2])*f_ext[c_id2][2]
3302                     + poro[1]);
3303 
3304           cs_real_t pfacj = pfaci;
3305 
3306           pfaci += (1.0-ktpond) * (c_var[c_id2] - c_var[c_id1]);
3307           pfacj -=      ktpond  * (c_var[c_id2] - c_var[c_id1]);
3308 
3309           /* Reconstruction part */
3310           cs_real_t rfac =
3311                  weight[f_id]
3312                  * ( (cell_cen[c_id1][0]-i_face_cog[f_id][0])*fexd[0]
3313                    + (cell_cen[c_id1][1]-i_face_cog[f_id][1])*fexd[1]
3314                    + (cell_cen[c_id1][2]-i_face_cog[f_id][2])*fexd[2])
3315               +  (1.0 - weight[f_id])
3316                  * ( (cell_cen[c_id2][0]-i_face_cog[f_id][0])*fexd[0]
3317                    + (cell_cen[c_id2][1]-i_face_cog[f_id][1])*fexd[1]
3318                    + (cell_cen[c_id2][2]-i_face_cog[f_id][2])*fexd[2])
3319               + (  dofij[f_id][0] * (r_grad[c_id1][0]+r_grad[c_id2][0])
3320                  + dofij[f_id][1] * (r_grad[c_id1][1]+r_grad[c_id2][1])
3321                  + dofij[f_id][2] * (r_grad[c_id1][2]+r_grad[c_id2][2])) * 0.5;
3322 
3323           for (cs_lnum_t j = 0; j < 3; j++) {
3324             grad[c_id1][j] += (pfaci + rfac) * i_f_face_normal[f_id][j];
3325             grad[c_id2][j] -= (pfacj + rfac) * i_f_face_normal[f_id][j];
3326           }
3327 
3328         } /* loop on faces */
3329 
3330       } /* loop on threads */
3331 
3332     } /* loop on thread groups */
3333 
3334     /* Contribution from boundary faces */
3335 
3336     for (int g_id = 0; g_id < n_b_groups; g_id++) {
3337 
3338 #     pragma omp parallel for
3339       for (int t_id = 0; t_id < n_b_threads; t_id++) {
3340 
3341         for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
3342              f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
3343              f_id++) {
3344 
3345           cs_lnum_t c_id = b_face_cells[f_id];
3346 
3347           cs_real_t poro = b_poro_duq[is_porous*f_id];
3348 
3349           /*
3350              Remark: for the cell \f$ \celli \f$ we remove
3351                      \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
3352            */
3353 
3354           cs_real_t pfac
3355             = coefap[f_id] * inc
3356             + coefbp[f_id]
3357               * ( (b_face_cog[f_id][0] - cell_cen[c_id][0])*f_ext[c_id][0]
3358                 + (b_face_cog[f_id][1] - cell_cen[c_id][1])*f_ext[c_id][1]
3359                 + (b_face_cog[f_id][2] - cell_cen[c_id][2])*f_ext[c_id][2]
3360                 + poro);
3361 
3362           pfac += (coefbp[f_id] - 1.0) * c_var[c_id];
3363 
3364           /* Reconstruction part */
3365           cs_real_t
3366             rfac = coefbp[f_id]
3367                    * (  diipb[f_id][0] * (r_grad[c_id][0] - f_ext[c_id][0])
3368                       + diipb[f_id][1] * (r_grad[c_id][1] - f_ext[c_id][1])
3369                       + diipb[f_id][2] * (r_grad[c_id][2] - f_ext[c_id][2]));
3370 
3371           for (cs_lnum_t j = 0; j < 3; j++) {
3372             grad[c_id][j] += (pfac + rfac) * b_f_face_normal[f_id][j];
3373           }
3374 
3375         } /* loop on faces */
3376 
3377       } /* loop on threads */
3378 
3379     } /* loop on thread groups */
3380 
3381   } /* End of test on hydrostatic pressure */
3382 
3383 
3384   /* Standard case, without hydrostatic pressure */
3385   /*---------------------------------------------*/
3386 
3387   else {
3388 
3389     /* Contribution from interior faces */
3390 
3391     for (int g_id = 0; g_id < n_i_groups; g_id++) {
3392 
3393 #     pragma omp parallel for
3394       for (int t_id = 0; t_id < n_i_threads; t_id++) {
3395 
3396         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
3397              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
3398              f_id++) {
3399 
3400           cs_lnum_t c_id1 = i_face_cells[f_id][0];
3401           cs_lnum_t c_id2 = i_face_cells[f_id][1];
3402 
3403           cs_real_t ktpond = (c_weight == NULL) ?
3404              weight[f_id] :              /* no cell weighting */
3405              weight[f_id] * c_weight[c_id1] /* cell weighting active */
3406                / (      weight[f_id] * c_weight[c_id1]
3407                  + (1.0-weight[f_id])* c_weight[c_id2]);
3408 
3409           /*
3410              Remark: \f$ \varia_\face = \alpha_\ij \varia_\celli
3411                                       + (1-\alpha_\ij) \varia_\cellj\f$
3412                      but for the cell \f$ \celli \f$ we remove
3413                      \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
3414                      and for the cell \f$ \cellj \f$ we remove
3415                      \f$ \varia_\cellj \sum_\face \vect{S}_\face = \vect{0} \f$
3416           */
3417 
3418           cs_real_t pfaci = (1.0-ktpond) * (c_var[c_id2] - c_var[c_id1]);
3419           cs_real_t pfacj =     -ktpond  * (c_var[c_id2] - c_var[c_id1]);
3420           /* Reconstruction part */
3421           cs_real_t rfac = 0.5 *
3422                     (dofij[f_id][0]*(r_grad[c_id1][0]+r_grad[c_id2][0])
3423                     +dofij[f_id][1]*(r_grad[c_id1][1]+r_grad[c_id2][1])
3424                     +dofij[f_id][2]*(r_grad[c_id1][2]+r_grad[c_id2][2]));
3425 
3426           for (cs_lnum_t j = 0; j < 3; j++) {
3427             grad[c_id1][j] += (pfaci + rfac) * i_f_face_normal[f_id][j];
3428             grad[c_id2][j] -= (pfacj + rfac) * i_f_face_normal[f_id][j];
3429           }
3430 
3431         } /* loop on faces */
3432 
3433       } /* loop on threads */
3434 
3435     } /* loop on thread groups */
3436 
3437     /* Contribution from coupled faces */
3438     if (cpl != NULL) {
3439       cs_internal_coupling_initialize_scalar_gradient(cpl, c_weight, c_var, grad);
3440       cs_internal_coupling_reconstruct_scalar_gradient(cpl, r_grad, grad);
3441     }
3442 
3443     /* Contribution from boundary faces */
3444 
3445     for (int g_id = 0; g_id < n_b_groups; g_id++) {
3446 
3447 #     pragma omp parallel for
3448       for (int t_id = 0; t_id < n_b_threads; t_id++) {
3449 
3450         for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
3451              f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
3452              f_id++) {
3453 
3454           if (cpl == NULL || !coupled_faces[f_id]) {
3455 
3456             cs_lnum_t c_id = b_face_cells[f_id];
3457 
3458             /*
3459                Remark: for the cell \f$ \celli \f$ we remove
3460                        \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
3461              */
3462 
3463             cs_real_t pfac =   inc*coefap[f_id]
3464                              + (coefbp[f_id]-1.0)*c_var[c_id];
3465 
3466             /* Reconstruction part */
3467             cs_real_t
3468               rfac =   coefbp[f_id]
3469                      * (  diipb[f_id][0] * r_grad[c_id][0]
3470                         + diipb[f_id][1] * r_grad[c_id][1]
3471                         + diipb[f_id][2] * r_grad[c_id][2]);
3472 
3473             for (cs_lnum_t j = 0; j < 3; j++) {
3474               grad[c_id][j] += (pfac + rfac) * b_f_face_normal[f_id][j];
3475             }
3476 
3477           }
3478 
3479         } /* loop on faces */
3480 
3481       } /* loop on threads */
3482 
3483     } /* loop on thread groups */
3484 
3485   }
3486 
3487 # pragma omp parallel for
3488   for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
3489     cs_real_t dvol;
3490     /* Is the cell disabled (for solid or porous)? Not the case if coupled */
3491     if (has_dc * c_disable_flag[has_dc * c_id] == 0)
3492       dvol = 1. / cell_f_vol[c_id];
3493     else
3494       dvol = 0.;
3495 
3496     grad[c_id][0] *= dvol;
3497     grad[c_id][1] *= dvol;
3498     grad[c_id][2] *= dvol;
3499 
3500     if (cs_glob_mesh_quantities_flag & CS_BAD_CELLS_WARPED_CORRECTION) {
3501       cs_real_3_t gradpa;
3502       for (cs_lnum_t i = 0; i < 3; i++) {
3503         gradpa[i] = grad[c_id][i];
3504         grad[c_id][i] = 0.;
3505       }
3506 
3507       for (cs_lnum_t i = 0; i < 3; i++)
3508         for (cs_lnum_t j = 0; j < 3; j++)
3509           grad[c_id][i] += corr_grad_lin[c_id][i][j] * gradpa[j];
3510     }
3511   }
3512 
3513   /* Synchronize halos */
3514 
3515   _sync_scalar_gradient_halo(m, CS_HALO_EXTENDED, grad);
3516 }
3517 
3518 /*----------------------------------------------------------------------------
3519  * Compute boundary face scalar values using least-squares reconstruction
3520  * for non-orthogonal meshes.
3521  *
3522  * parameters:
3523  *   m              <-- pointer to associated mesh structure
3524  *   fvq            <-- pointer to associated finite volume quantities
3525  *   cpl            <-- structure associated with internal coupling, or NULL
3526  *   halo_type      <-- halo type (extended or not)
3527  *   recompute_cocg <-- flag to recompute cocg
3528  *   nswrgp         <-- number of sweeps for gradient reconstruction
3529  *   inc            <-- if 0, solve on increment; 1 otherwise
3530  *   bc_coeff_a     <-- B.C. coefficients for boundary face normals
3531  *   bc_coeff_b     <-- B.C. coefficients for boundary face normals
3532  *   c_var          <-- variable
3533  *   c_weight       <-- weighted gradient coefficient variable,
3534  *                      or NULL
3535  *   b_f_var        --> boundary face value.
3536  *----------------------------------------------------------------------------*/
3537 
3538 static void
_lsq_scalar_b_face_val(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,cs_halo_type_t halo_type,cs_real_t inc,const cs_real_t bc_coeff_a[],const cs_real_t bc_coeff_b[],const cs_real_t c_var[],const cs_real_t c_weight[],cs_real_t b_f_var[restrict])3539 _lsq_scalar_b_face_val(const cs_mesh_t             *m,
3540                        const cs_mesh_quantities_t  *fvq,
3541                        cs_halo_type_t               halo_type,
3542                        cs_real_t                    inc,
3543                        const cs_real_t              bc_coeff_a[],
3544                        const cs_real_t              bc_coeff_b[],
3545                        const cs_real_t              c_var[],
3546                        const cs_real_t              c_weight[],
3547                        cs_real_t                    b_f_var[restrict])
3548 {
3549   const cs_lnum_t n_b_cells = m->n_b_cells;
3550 
3551   const cs_mesh_adjacencies_t *ma = cs_glob_mesh_adjacencies;
3552   const cs_lnum_t *restrict cell_b_faces_idx
3553     = (const cs_lnum_t *restrict) ma->cell_b_faces_idx;
3554   const cs_lnum_t *restrict cell_b_faces
3555     = (const cs_lnum_t *restrict) ma->cell_b_faces;
3556 
3557   const cs_real_3_t *restrict diipb
3558     = (const cs_real_3_t *restrict)fvq->diipb;
3559 
3560   cs_real_t  *_bc_coeff_a = NULL;
3561 
3562   if (inc < 1) {
3563     BFT_MALLOC(_bc_coeff_a, m->n_b_faces, cs_real_t);
3564     for (cs_lnum_t i = 0; i < m->n_b_faces; i++)
3565       _bc_coeff_a[i] = 0;
3566     bc_coeff_a = (const cs_real_t*)_bc_coeff_a;
3567   }
3568 
3569   /* Reconstruct gradients using least squares for non-orthogonal meshes */
3570 
3571 # pragma omp parallel for if (n_b_cells > CS_THR_MIN)
3572   for (cs_lnum_t ci = 0; ci < n_b_cells; ci++) {
3573 
3574     cs_real_t grad[3];
3575     cs_lnum_t c_id = m->b_cells[ci];
3576 
3577     cs_gradient_scalar_cell(m,
3578                             fvq,
3579                             c_id,
3580                             halo_type,
3581                             bc_coeff_a,
3582                             bc_coeff_b,
3583                             c_var,
3584                             c_weight,
3585                             grad);
3586 
3587     /* Update boundary face value */
3588 
3589     cs_lnum_t s_id = cell_b_faces_idx[c_id];
3590     cs_lnum_t e_id = cell_b_faces_idx[c_id+1];
3591 
3592     for (cs_lnum_t i = s_id; i < e_id; i++) {
3593 
3594       cs_lnum_t f_id = cell_b_faces[i];
3595 
3596       cs_real_t pip =   c_var[c_id]
3597                       + cs_math_3_dot_product(diipb[f_id], grad);
3598       b_f_var[f_id] = bc_coeff_a[f_id]*inc + pip*bc_coeff_b[f_id];
3599 
3600     }
3601   }
3602 
3603   BFT_FREE(_bc_coeff_a);
3604 }
3605 
3606 /*----------------------------------------------------------------------------
3607  * Compute boundary face scalar values using least-squares reconstruction
3608  * for non-orthogonal meshes in the presence of a volume force generating
3609  * a hydrostatic pressure component.
3610  *
3611  * parameters:
3612  *   m              <-- pointer to associated mesh structure
3613  *   fvq            <-- pointer to associated finite volume quantities
3614  *   cpl            <-- structure associated with internal coupling, or NULL
3615  *   halo_type      <-- halo type (extended or not)
3616  *   recompute_cocg <-- flag to recompute cocg
3617  *   nswrgp         <-- number of sweeps for gradient reconstruction
3618  *   inc            <-- if 0, solve on increment; 1 otherwise
3619  *   f_ext          <-- exterior force generating pressure
3620  *   bc_coeff_a     <-- B.C. coefficients for boundary face normals
3621  *   bc_coeff_b     <-- B.C. coefficients for boundary face normals
3622  *   c_var          <-- cell variable
3623  *   c_weight       <-- weighted gradient coefficient variable,
3624  *                      or NULL
3625  *   b_f_var        --> boundary face value.
3626  *----------------------------------------------------------------------------*/
3627 
3628 static void
_lsq_scalar_b_face_val_phyd(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,cs_halo_type_t halo_type,cs_real_t inc,const cs_real_t f_ext[][3],const cs_real_t bc_coeff_a[],const cs_real_t bc_coeff_b[],const cs_real_t c_var[],const cs_real_t c_weight[],cs_real_t b_f_var[restrict])3629 _lsq_scalar_b_face_val_phyd(const cs_mesh_t             *m,
3630                             const cs_mesh_quantities_t  *fvq,
3631                             cs_halo_type_t               halo_type,
3632                             cs_real_t                    inc,
3633                             const cs_real_t              f_ext[][3],
3634                             const cs_real_t              bc_coeff_a[],
3635                             const cs_real_t              bc_coeff_b[],
3636                             const cs_real_t              c_var[],
3637                             const cs_real_t              c_weight[],
3638                             cs_real_t                    b_f_var[restrict])
3639 {
3640   const cs_lnum_t n_b_cells = m->n_b_cells;
3641 
3642   const cs_mesh_adjacencies_t *ma = cs_glob_mesh_adjacencies;
3643   const cs_lnum_t *restrict cell_cells_idx
3644     = (const cs_lnum_t *restrict) ma->cell_cells_idx;
3645   const cs_lnum_t *restrict cell_cells_e_idx
3646     = (const cs_lnum_t *restrict) ma->cell_cells_e_idx;
3647   const cs_lnum_t *restrict cell_b_faces_idx
3648     = (const cs_lnum_t *restrict) ma->cell_b_faces_idx;
3649   const cs_lnum_t *restrict cell_cells
3650     = (const cs_lnum_t *restrict) ma->cell_cells;
3651   const cs_lnum_t *restrict cell_cells_e
3652     = (const cs_lnum_t *restrict) ma->cell_cells_e;
3653   const cs_lnum_t *restrict cell_b_faces
3654     = (const cs_lnum_t *restrict) ma->cell_b_faces;
3655 
3656   const cs_real_3_t *restrict cell_cen
3657     = (const cs_real_3_t *restrict)fvq->cell_cen;
3658   const cs_real_3_t *restrict b_face_cog
3659     = (const cs_real_3_t *restrict)fvq->b_face_cog;
3660   const cs_real_3_t *restrict b_face_normal
3661     = (const cs_real_3_t *restrict)fvq->b_face_normal;
3662   const cs_real_t *restrict b_dist
3663     = (const cs_real_t *restrict)fvq->b_dist;
3664   const cs_real_3_t *restrict diipb
3665     = (const cs_real_3_t *restrict)fvq->diipb;
3666 
3667   /*Additional terms due to porosity */
3668 
3669   cs_field_t *f_i_poro_duq_0 = cs_field_by_name_try("i_poro_duq_0");
3670 
3671   cs_real_t *b_poro_duq;
3672   cs_real_t _f_ext = 0.;
3673 
3674   cs_lnum_t is_porous = false;
3675   if (f_i_poro_duq_0 != NULL) {
3676     is_porous = 1;
3677     b_poro_duq = cs_field_by_name_try("b_poro_duq")->val;
3678   }
3679   else {
3680     b_poro_duq = &_f_ext;
3681   }
3682 
3683   /* Reconstruct gradients using least squares for non-orthogonal meshes */
3684 
3685 # pragma omp parallel for if (n_b_cells > CS_THR_MIN)
3686   for (cs_lnum_t ci = 0; ci < n_b_cells; ci++) {
3687 
3688     cs_lnum_t c_id = m->b_cells[ci];
3689 
3690     cs_real_t cocg[6] = {0., 0., 0., 0., 0., 0.};
3691     cs_real_t rhsv[3] = {0., 0., 0.};
3692 
3693     int n_adj = (halo_type == CS_HALO_EXTENDED) ? 2 : 1;
3694 
3695     for (int adj_id = 0; adj_id < n_adj; adj_id++) {
3696 
3697       const cs_lnum_t *restrict cell_cells_p;
3698       cs_lnum_t s_id, e_id;
3699 
3700       if (adj_id == 0){
3701         s_id = cell_cells_idx[c_id];
3702         e_id = cell_cells_idx[c_id+1];
3703         cell_cells_p = (const cs_lnum_t *restrict)(cell_cells);
3704       }
3705       else if (cell_cells_e_idx != NULL){
3706         s_id = cell_cells_e_idx[c_id];
3707         e_id = cell_cells_e_idx[c_id+1];
3708         cell_cells_p = (const cs_lnum_t *restrict)(cell_cells_e);
3709       }
3710       else
3711         break;
3712 
3713       if (c_weight == NULL) {
3714 
3715         for (cs_lnum_t i = s_id; i < e_id; i++) {
3716 
3717           cs_real_t dc[3];
3718           cs_lnum_t c_id1 = cell_cells_p[i];
3719           for (cs_lnum_t ll = 0; ll < 3; ll++)
3720             dc[ll] = cell_cen[c_id1][ll] - cell_cen[c_id][ll];
3721 
3722           cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
3723 
3724           cs_real_t pfac = (  c_var[c_id1] - c_var[c_id]
3725                             - 0.5 * dc[0] * f_ext[c_id][0]
3726                             - 0.5 * dc[1] * f_ext[c_id][1]
3727                             - 0.5 * dc[2] * f_ext[c_id][2]
3728                             - 0.5 * dc[0] * f_ext[c_id1][0]
3729                             - 0.5 * dc[1] * f_ext[c_id1][1]
3730                             - 0.5 * dc[2] * f_ext[c_id1][2]) * ddc;
3731 
3732           for (cs_lnum_t ll = 0; ll < 3; ll++)
3733             rhsv[ll] += dc[ll] * pfac;
3734 
3735           cocg[0] += dc[0]*dc[0]*ddc;
3736           cocg[1] += dc[1]*dc[1]*ddc;
3737           cocg[2] += dc[2]*dc[2]*ddc;
3738           cocg[3] += dc[0]*dc[1]*ddc;
3739           cocg[4] += dc[1]*dc[2]*ddc;
3740           cocg[5] += dc[0]*dc[2]*ddc;
3741 
3742         }
3743 
3744       }
3745       else {
3746 
3747         for (cs_lnum_t i = s_id; i < e_id; i++) {
3748 
3749           cs_real_t dc[3];
3750           cs_lnum_t c_id1 = cell_cells_p[i];
3751           for (cs_lnum_t ll = 0; ll < 3; ll++)
3752             dc[ll] = cell_cen[c_id1][ll] - cell_cen[c_id][ll];
3753 
3754           cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
3755 
3756           cs_real_t pfac = (  c_var[c_id1] - c_var[c_id]
3757                             - 0.5 * dc[0] * f_ext[c_id][0]
3758                             - 0.5 * dc[1] * f_ext[c_id][1]
3759                             - 0.5 * dc[2] * f_ext[c_id][2]
3760                             - 0.5 * dc[0] * f_ext[c_id1][0]
3761                             - 0.5 * dc[1] * f_ext[c_id1][1]
3762                             - 0.5 * dc[2] * f_ext[c_id1][2]) * ddc;
3763 
3764           cs_real_t _weight =   2. * c_weight[c_id1]
3765                               / (c_weight[c_id] + c_weight[c_id1]);
3766 
3767           for (cs_lnum_t ll = 0; ll < 3; ll++)
3768             rhsv[ll] += dc[ll] * pfac* _weight;
3769 
3770           cocg[0] += dc[0]*dc[0]*ddc;
3771           cocg[1] += dc[1]*dc[1]*ddc;
3772           cocg[2] += dc[2]*dc[2]*ddc;
3773           cocg[3] += dc[0]*dc[1]*ddc;
3774           cocg[4] += dc[1]*dc[2]*ddc;
3775           cocg[5] += dc[0]*dc[2]*ddc;
3776 
3777         }
3778 
3779       }
3780 
3781     } /* end of contribution from interior and extended cells */
3782 
3783     cs_lnum_t s_id = cell_b_faces_idx[c_id];
3784     cs_lnum_t e_id = cell_b_faces_idx[c_id+1];
3785 
3786     /* Contribution from boundary faces */
3787 
3788     for (cs_lnum_t i = s_id; i < e_id; i++) {
3789 
3790       cs_real_t  dsij[3];
3791 
3792       cs_lnum_t f_id = cell_b_faces[i];
3793 
3794       cs_real_t poro = b_poro_duq[is_porous*f_id];
3795 
3796       cs_real_t unddij = 1. / b_dist[f_id];
3797       cs_real_t umcbdd = (1. -bc_coeff_b[f_id]) * unddij;
3798 
3799       cs_real_t normal[3];
3800       /* Normal is vector 0 if the b_face_normal norm is too small */
3801       cs_math_3_normalise(b_face_normal[f_id], normal);
3802 
3803       for (cs_lnum_t ll = 0; ll < 3; ll++)
3804         dsij[ll] = normal[ll] + umcbdd*diipb[f_id][ll];
3805 
3806       /* (b_face_cog - cell_cen).f_ext, or IF.F_I */
3807       cs_real_t c_f_ext
3808         = cs_math_3_distance_dot_product(b_face_cog[f_id],
3809                                          cell_cen[c_id],
3810                                          f_ext[c_id]);
3811 
3812       cs_real_t pfac =  (  bc_coeff_a[f_id]*inc + (bc_coeff_b[f_id] -1.)
3813                          * (c_var[c_id] + c_f_ext + poro))
3814                        * unddij;
3815 
3816       for (cs_lnum_t ll = 0; ll < 3; ll++)
3817         rhsv[ll] += dsij[ll] * pfac;
3818 
3819       cocg[0] += dsij[0]*dsij[0];
3820       cocg[1] += dsij[1]*dsij[1];
3821       cocg[2] += dsij[2]*dsij[2];
3822       cocg[3] += dsij[0]*dsij[1];
3823       cocg[4] += dsij[1]*dsij[2];
3824       cocg[5] += dsij[0]*dsij[2];
3825 
3826     } // end of contribution from boundary cells
3827 
3828     /* Invert */
3829 
3830     cs_real_t a11 = cocg[1]*cocg[2] - cocg[4]*cocg[4];
3831     cs_real_t a12 = cocg[4]*cocg[5] - cocg[3]*cocg[2];
3832     cs_real_t a13 = cocg[3]*cocg[4] - cocg[1]*cocg[5];
3833     cs_real_t a22 = cocg[0]*cocg[2] - cocg[5]*cocg[5];
3834     cs_real_t a23 = cocg[3]*cocg[5] - cocg[0]*cocg[4];
3835     cs_real_t a33 = cocg[0]*cocg[1] - cocg[3]*cocg[3];
3836 
3837     cs_real_t det_inv = 1. / (cocg[0]*a11 + cocg[3]*a12 + cocg[5]*a13);
3838 
3839     cs_real_t grad[3];
3840 
3841     grad[0] =  (  a11 * rhsv[0]
3842                 + a12 * rhsv[1]
3843                 + a13 * rhsv[2]) * det_inv
3844                 + f_ext[c_id][0];
3845     grad[1] =  (  a12 * rhsv[0]
3846                 + a22 * rhsv[1]
3847                 + a23 * rhsv[2]) * det_inv
3848                 + f_ext[c_id][1];
3849     grad[2] =  (  a13 * rhsv[0]
3850                 + a23 * rhsv[1]
3851                 + a33 * rhsv[2]) * det_inv
3852                 + f_ext[c_id][2];
3853 
3854     /* Update boundary face value */
3855 
3856     for (cs_lnum_t i = s_id; i < e_id; i++) {
3857 
3858       cs_lnum_t f_id = cell_b_faces[i];
3859 
3860       cs_real_t pip =   c_var[c_id]
3861                       + cs_math_3_dot_product(diipb[f_id], grad);
3862       b_f_var[f_id] = bc_coeff_a[f_id]*inc + pip*bc_coeff_b[f_id];
3863 
3864     }
3865   }
3866 }
3867 
3868 /*----------------------------------------------------------------------------
3869  * Compute gradient using vertex-based face values for scalar gradient
3870  * reconstruction.
3871  *
3872  * Optionally, a volume force generating a hydrostatic pressure component
3873  * may be accounted for.
3874  *
3875  * parameters:
3876  *   m              <-- pointer to associated mesh structure
3877  *   fvq            <-- pointer to associated finite volume quantities
3878  *   cpl            <-- structure associated with internal coupling, or NULL
3879  *   halo_type      <-- halo type (extended or not)
3880  *   hyd_p_flag     <-- flag for hydrostatic pressure
3881  *   inc            <-- if 0, solve on increment; 1 otherwise
3882  *   f_ext          <-- exterior force generating pressure
3883  *   bc_coeff_a     <-- B.C. coefficients for boundary face normals
3884  *   bc_coeff_b     <-- B.C. coefficients for boundary face normals
3885  *   c_var          <-- variable
3886  *   c_weight       <-- weighted gradient coefficient variable
3887  *   grad           <-> gradient of pvar (halo prepared for periodicity
3888  *                      of rotation)
3889  *----------------------------------------------------------------------------*/
3890 
3891 static void
_fv_vtx_based_scalar_gradient(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,int hyd_p_flag,cs_real_t inc,const cs_real_3_t f_ext[],const cs_real_t bc_coeff_a[],const cs_real_t bc_coeff_b[],const cs_real_t c_var[],const cs_real_t c_weight[],cs_real_3_t * restrict grad)3892 _fv_vtx_based_scalar_gradient(const cs_mesh_t                *m,
3893                               const cs_mesh_quantities_t     *fvq,
3894                               const cs_internal_coupling_t   *cpl,
3895                               int                             hyd_p_flag,
3896                               cs_real_t                       inc,
3897                               const cs_real_3_t               f_ext[],
3898                               const cs_real_t                 bc_coeff_a[],
3899                               const cs_real_t                 bc_coeff_b[],
3900                               const cs_real_t                 c_var[],
3901                               const cs_real_t                 c_weight[],
3902                               cs_real_3_t           *restrict grad)
3903 {
3904   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
3905   const cs_lnum_t n_cells = m->n_cells;
3906   const int n_i_groups = m->i_face_numbering->n_groups;
3907   const int n_i_threads = m->i_face_numbering->n_threads;
3908   const int n_b_groups = m->b_face_numbering->n_groups;
3909   const int n_b_threads = m->b_face_numbering->n_threads;
3910   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
3911   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
3912 
3913   const cs_lnum_2_t *restrict i_face_cells
3914     = (const cs_lnum_2_t *restrict)m->i_face_cells;
3915   const cs_lnum_t *restrict b_face_cells
3916     = (const cs_lnum_t *restrict)m->b_face_cells;
3917 
3918   const int *restrict c_disable_flag = fvq->c_disable_flag;
3919   cs_lnum_t has_dc = fvq->has_disable_flag; /* Has cells disabled? */
3920 
3921   const cs_real_t *restrict weight = fvq->weight;
3922   const cs_real_t *restrict cell_f_vol = fvq->cell_f_vol;
3923   if (cs_glob_porous_model == 1 || cs_glob_porous_model == 2)
3924     cell_f_vol = fvq->cell_vol;
3925   const cs_real_3_t *restrict cell_cen
3926     = (const cs_real_3_t *restrict)fvq->cell_cen;
3927   const cs_real_3_t *restrict i_f_face_normal
3928     = (const cs_real_3_t *restrict)fvq->i_f_face_normal;
3929   const cs_real_3_t *restrict b_f_face_normal
3930     = (const cs_real_3_t *restrict)fvq->b_f_face_normal;
3931   const cs_real_3_t *restrict i_face_cog
3932     = (const cs_real_3_t *restrict)fvq->i_face_cog;
3933   const cs_real_3_t *restrict b_face_cog
3934     = (const cs_real_3_t *restrict)fvq->b_face_cog;
3935 
3936   bool  *coupled_faces = (cpl == NULL) ?
3937     NULL : (bool *)cpl->coupled_faces;
3938 
3939   /*Additional terms due to porosity */
3940   cs_field_t *f_i_poro_duq_0 = cs_field_by_name_try("i_poro_duq_0");
3941 
3942   cs_real_t *i_poro_duq_0;
3943   cs_real_t *i_poro_duq_1;
3944   cs_real_t *b_poro_duq;
3945   cs_real_t _f_ext = 0.;
3946 
3947   cs_lnum_t is_porous = 0;
3948   if (f_i_poro_duq_0 != NULL) {
3949     is_porous = 1;
3950     i_poro_duq_0 = f_i_poro_duq_0->val;
3951     i_poro_duq_1 = cs_field_by_name_try("i_poro_duq_1")->val;
3952     b_poro_duq = cs_field_by_name_try("b_poro_duq")->val;
3953   } else {
3954     i_poro_duq_0 = &_f_ext;
3955     i_poro_duq_1 = &_f_ext;
3956     b_poro_duq = &_f_ext;
3957   }
3958 
3959   /* Initialize gradient
3960      ------------------- */
3961 
3962 # pragma omp parallel for
3963   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
3964     for (cs_lnum_t j = 0; j < 3; j++)
3965       grad[c_id][j] = 0.0;
3966   }
3967 
3968   /* Pre-compute values at boundary using least squares */
3969 
3970   cs_real_t *b_f_var;
3971   BFT_MALLOC(b_f_var, m->n_b_faces, cs_real_t);
3972 
3973   if (hyd_p_flag == 1)
3974     _lsq_scalar_b_face_val_phyd(m,
3975                                 fvq,
3976                                 CS_HALO_STANDARD,
3977                                 inc,
3978                                 f_ext,
3979                                 bc_coeff_a,
3980                                 bc_coeff_b,
3981                                 c_var,
3982                                 c_weight,
3983                                 b_f_var);
3984   else
3985     _lsq_scalar_b_face_val(m,
3986                            fvq,
3987                            CS_HALO_STANDARD,
3988                            inc,
3989                            bc_coeff_a,
3990                            bc_coeff_b,
3991                            c_var,
3992                            c_weight,
3993                            b_f_var);
3994 
3995   /* Compute vertex-based values
3996      --------------------------- */
3997 
3998   cs_real_t *v_var;
3999   BFT_MALLOC(v_var, m->n_vertices, cs_real_t);
4000 
4001   cs_cell_to_vertex(CS_CELL_TO_VERTEX_LR,
4002                     0, /* verbosity */
4003                     1, /* var_dim */
4004                     0, /* tr_dim */
4005                     c_weight,
4006                     c_var,
4007                     b_f_var,
4008                     v_var);
4009 
4010   /* Interpolate to face-based values
4011      -------------------------------- */
4012 
4013   cs_real_t *i_f_var;
4014   BFT_MALLOC(i_f_var, m->n_i_faces, cs_real_t);
4015 
4016   for (int f_t = 0; f_t < 2; f_t++) {
4017 
4018     const cs_lnum_t n_faces = (f_t == 0) ? m->n_i_faces : m->n_b_faces;
4019     const cs_lnum_t *f2v_idx= NULL, *f2v_ids = NULL;
4020     cs_real_t *f_var = NULL;
4021 
4022     if (f_t == 0) {
4023       f2v_idx = m->i_face_vtx_idx;
4024       f2v_ids = m->i_face_vtx_lst;
4025       f_var = i_f_var;
4026     }
4027     else {
4028       f2v_idx = m->b_face_vtx_idx;
4029       f2v_ids = m->b_face_vtx_lst;
4030       f_var = b_f_var;
4031     }
4032 
4033 #   pragma omp parallel for if (n_faces > CS_THR_MIN)
4034     for (cs_lnum_t f_id = 0; f_id < n_faces; f_id++) {
4035       cs_lnum_t s_id = f2v_idx[f_id];
4036       cs_lnum_t e_id = f2v_idx[f_id+1];
4037       cs_real_t s = 0;
4038       for (cs_lnum_t i = s_id; i < e_id; i++)
4039         s += v_var[f2v_ids[i]];
4040       f_var[f_id] = s / (e_id-s_id);
4041     }
4042 
4043   }
4044 
4045   /* Vertex values are not needed after this stage */
4046 
4047   cs_real_t mean[4] = {0, 0, 0, 0};
4048   for (cs_lnum_t i = 0; i < n_cells; i++)
4049     mean[0] += c_var[i];
4050   mean[0] /= n_cells;
4051   for (cs_lnum_t i = 0; i < m->n_vertices; i++)
4052     mean[1] += v_var[i];
4053   mean[1] /= m->n_vertices;
4054   for (cs_lnum_t i = 0; i < m->n_i_faces; i++)
4055     mean[2] += i_f_var[i];
4056   mean[2] /= m->n_i_faces;
4057   for (cs_lnum_t i = 0; i < m->n_b_faces; i++)
4058     mean[3] += b_f_var[i];
4059   mean[3] /= m->n_b_faces;
4060 
4061   BFT_FREE(v_var);
4062 
4063   /* Case with hydrostatic pressure
4064      ------------------------------ */
4065 
4066   if (hyd_p_flag == 1) {
4067 
4068     /* Contribution from interior faces */
4069 
4070     for (int g_id = 0; g_id < n_i_groups; g_id++) {
4071 
4072 #     pragma omp parallel for
4073       for (int t_id = 0; t_id < n_i_threads; t_id++) {
4074 
4075         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
4076              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
4077              f_id++) {
4078 
4079           cs_lnum_t ii = i_face_cells[f_id][0];
4080           cs_lnum_t jj = i_face_cells[f_id][1];
4081 
4082           cs_real_t ktpond = (c_weight == NULL) ?
4083              weight[f_id] :              /* no cell weighting */
4084              weight[f_id] * c_weight[ii] /* cell weighting active */
4085                / (      weight[f_id] * c_weight[ii]
4086                  + (1.0-weight[f_id])* c_weight[jj]);
4087 
4088           cs_real_2_t poro = {
4089             i_poro_duq_0[is_porous*f_id],
4090             i_poro_duq_1[is_porous*f_id]
4091           };
4092 
4093           cs_real_t pfaci
4094             =  ktpond
4095                  * (  (i_face_cog[f_id][0] - cell_cen[ii][0])*f_ext[ii][0]
4096                     + (i_face_cog[f_id][1] - cell_cen[ii][1])*f_ext[ii][1]
4097                     + (i_face_cog[f_id][2] - cell_cen[ii][2])*f_ext[ii][2]
4098                     + poro[0])
4099             +  (1.0 - ktpond)
4100                  * (  (i_face_cog[f_id][0] - cell_cen[jj][0])*f_ext[jj][0]
4101                     + (i_face_cog[f_id][1] - cell_cen[jj][1])*f_ext[jj][1]
4102                     + (i_face_cog[f_id][2] - cell_cen[jj][2])*f_ext[jj][2]
4103                     + poro[1]);
4104           cs_real_t pfacj = pfaci;
4105 
4106           pfaci += i_f_var[f_id] - c_var[ii];
4107           pfacj += i_f_var[f_id] - c_var[jj];
4108 
4109           for (cs_lnum_t j = 0; j < 3; j++) {
4110             grad[ii][j] += pfaci * i_f_face_normal[f_id][j];
4111             grad[jj][j] -= pfacj * i_f_face_normal[f_id][j];
4112           }
4113 
4114         } /* loop on faces */
4115 
4116       } /* loop on threads */
4117 
4118     } /* loop on thread groups */
4119 
4120     /* Contribution from coupled faces */
4121     if (cpl != NULL) {
4122       assert(0); /* not handled yet */
4123     }
4124 
4125     /* Contribution from boundary faces */
4126 
4127     for (int g_id = 0; g_id < n_b_groups; g_id++) {
4128 
4129 #     pragma omp parallel for
4130       for (int t_id = 0; t_id < n_b_threads; t_id++) {
4131 
4132         for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
4133              f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
4134              f_id++) {
4135 
4136           if (cpl == NULL || !coupled_faces[f_id]) {
4137 
4138             cs_lnum_t c_id = b_face_cells[f_id];
4139 
4140             cs_real_t poro = b_poro_duq[is_porous*f_id];
4141             /*
4142                Remark: for the cell \f$ \celli \f$ we remove
4143                        \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
4144              */
4145 
4146             cs_real_t pfac = b_f_var[f_id] - c_var[c_id];
4147 
4148             pfac +=
4149                 bc_coeff_b[f_id]
4150               * ( (b_face_cog[f_id][0] - cell_cen[c_id][0])*f_ext[c_id][0]
4151                 + (b_face_cog[f_id][1] - cell_cen[c_id][1])*f_ext[c_id][1]
4152                 + (b_face_cog[f_id][2] - cell_cen[c_id][2])*f_ext[c_id][2]
4153                 + poro);
4154 
4155             for (cs_lnum_t j = 0; j < 3; j++)
4156               grad[c_id][j] += pfac * b_f_face_normal[f_id][j];
4157 
4158           } /* face without internal coupling */
4159 
4160         } /* loop on faces */
4161 
4162       } /* loop on threads */
4163 
4164     } /* loop on thread groups */
4165 
4166   } /* End of test on hydrostatic pressure */
4167 
4168   /* Standard case, without hydrostatic pressure
4169      ------------------------------------------- */
4170 
4171   else {
4172 
4173     /* Contribution from interior faces */
4174 
4175     for (int g_id = 0; g_id < n_i_groups; g_id++) {
4176 
4177 #     pragma omp parallel for
4178       for (int t_id = 0; t_id < n_i_threads; t_id++) {
4179 
4180         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
4181              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
4182              f_id++) {
4183 
4184           cs_lnum_t ii = i_face_cells[f_id][0];
4185           cs_lnum_t jj = i_face_cells[f_id][1];
4186 
4187           cs_real_t pfaci = i_f_var[f_id] - c_var[ii];
4188           cs_real_t pfacj = i_f_var[f_id] - c_var[jj];
4189 
4190           for (cs_lnum_t j = 0; j < 3; j++) {
4191             grad[ii][j] += pfaci * i_f_face_normal[f_id][j];
4192             grad[jj][j] -= pfacj * i_f_face_normal[f_id][j];
4193           }
4194 
4195         } /* loop on faces */
4196 
4197       } /* loop on threads */
4198 
4199     } /* loop on thread groups */
4200 
4201     /* Contribution from coupled faces */
4202     if (cpl != NULL) {
4203       assert(0); /* not handled yet */
4204     }
4205 
4206     /* Contribution from boundary faces */
4207 
4208     for (int g_id = 0; g_id < n_b_groups; g_id++) {
4209 
4210 #     pragma omp parallel for
4211       for (int t_id = 0; t_id < n_b_threads; t_id++) {
4212 
4213         for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
4214              f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
4215              f_id++) {
4216 
4217           if (cpl == NULL || !coupled_faces[f_id]) {
4218 
4219             cs_lnum_t ii = b_face_cells[f_id];
4220 
4221             /*
4222                Remark: for the cell \f$ \celli \f$ we remove
4223                        \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
4224              */
4225 
4226             cs_real_t pfac = b_f_var[f_id] - c_var[ii];
4227 
4228             for (cs_lnum_t j = 0; j < 3; j++)
4229               grad[ii][j] += pfac * b_f_face_normal[f_id][j];
4230 
4231           } /* face without internal coupling */
4232 
4233         } /* loop on faces */
4234 
4235       } /* loop on threads */
4236 
4237     } /* loop on thread groups */
4238 
4239   }
4240 
4241   BFT_FREE(i_f_var);
4242   BFT_FREE(b_f_var);
4243 
4244 # pragma omp parallel for
4245   for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
4246     cs_real_t dvol;
4247     /* Is the cell disabled (for solid or porous)? Not the case if coupled */
4248     if (has_dc * c_disable_flag[has_dc * c_id] == 0)
4249       dvol = 1. / cell_f_vol[c_id];
4250     else
4251       dvol = 0.;
4252 
4253     for (cs_lnum_t j = 0; j < 3; j++)
4254       grad[c_id][j] *= dvol;
4255   }
4256 
4257   /* Synchronize halos */
4258 
4259   _sync_scalar_gradient_halo(m, CS_HALO_EXTENDED, grad);
4260 }
4261 
4262 
4263 /*----------------------------------------------------------------------------
4264  * Clip the gradient of a vector if necessary. This function deals with the
4265  * standard or extended neighborhood.
4266  *
4267  * parameters:
4268  *   m              <-- pointer to associated mesh structure
4269  *   fvq            <-- pointer to associated finite volume quantities
4270  *   halo_type      <-- halo type (extended or not)
4271  *   clip_mode      <-- type of clipping for the computation of the gradient
4272  *   verbosity      <-- output level
4273  *   climgp         <-- clipping coefficient for the computation of the gradient
4274  *   pvar           <-- variable
4275  *   gradv          <-> gradient of pvar (du_i/dx_j : gradv[][i][j])
4276  *   pvar           <-- variable
4277  *----------------------------------------------------------------------------*/
4278 
4279 static void
_vector_gradient_clipping(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,cs_halo_type_t halo_type,int clip_mode,int verbosity,cs_real_t climgp,const char * var_name,const cs_real_3_t * restrict pvar,cs_real_33_t * restrict gradv)4280 _vector_gradient_clipping(const cs_mesh_t              *m,
4281                           const cs_mesh_quantities_t   *fvq,
4282                           cs_halo_type_t                halo_type,
4283                           int                           clip_mode,
4284                           int                           verbosity,
4285                           cs_real_t                     climgp,
4286                           const char                   *var_name,
4287                           const cs_real_3_t   *restrict pvar,
4288                           cs_real_33_t        *restrict gradv)
4289 {
4290   cs_real_t  global_min_factor, global_max_factor;
4291 
4292   cs_gnum_t  n_clip = 0, n_g_clip =0;
4293   cs_real_t  min_factor = 1;
4294   cs_real_t  max_factor = 0;
4295   cs_real_t  clipp_coef_sq = climgp*climgp;
4296   cs_real_t  *restrict buf = NULL, *restrict clip_factor = NULL;
4297   cs_real_t  *restrict denom = NULL, *restrict denum = NULL;
4298 
4299   const cs_lnum_t n_cells = m->n_cells;
4300   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
4301   const int n_i_groups = m->i_face_numbering->n_groups;
4302   const int n_i_threads = m->i_face_numbering->n_threads;
4303   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
4304 
4305   const cs_lnum_2_t *restrict i_face_cells
4306     = (const cs_lnum_2_t *restrict)m->i_face_cells;
4307   const cs_lnum_t *restrict cell_cells_idx
4308     = (const cs_lnum_t *restrict)m->cell_cells_idx;
4309   const cs_lnum_t *restrict cell_cells_lst
4310     = (const cs_lnum_t *restrict)m->cell_cells_lst;
4311 
4312   const cs_real_3_t *restrict cell_cen
4313     = (const cs_real_3_t *restrict)fvq->cell_cen;
4314 
4315   const cs_halo_t *halo = m->halo;
4316 
4317   if (clip_mode < 0)
4318     return;
4319 
4320   /* The gradient and the variable must be already synchronized */
4321 
4322   /* Allocate and initialize working buffers */
4323 
4324   if (clip_mode == 1)
4325     BFT_MALLOC(buf, 3*n_cells_ext, cs_real_t);
4326   else
4327     BFT_MALLOC(buf, 2*n_cells_ext, cs_real_t);
4328 
4329   denum = buf;
4330   denom = buf + n_cells_ext;
4331 
4332   if (clip_mode == 1)
4333     clip_factor = buf + 2*n_cells_ext;
4334 
4335   /* Initialization */
4336 
4337 # pragma omp parallel for
4338   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
4339     denum[c_id] = 0;
4340     denom[c_id] = 0;
4341     if (clip_mode == 1)
4342       clip_factor[c_id] = (cs_real_t)DBL_MAX;
4343   }
4344 
4345   /* Remark:
4346      denum: holds the maximum l2 norm of the variation of the gradient squared
4347      denom: holds the maximum l2 norm of the variation of the variable squared */
4348 
4349   /* First clipping Algorithm: based on the cell gradient */
4350   /*------------------------------------------------------*/
4351 
4352   if (clip_mode == 0) {
4353 
4354     for (int g_id = 0; g_id < n_i_groups; g_id++) {
4355 
4356 #     pragma omp parallel for
4357       for (int t_id = 0; t_id < n_i_threads; t_id++) {
4358 
4359         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
4360              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
4361              f_id++) {
4362 
4363           cs_lnum_t c_id1 = i_face_cells[f_id][0];
4364           cs_lnum_t c_id2 = i_face_cells[f_id][1];
4365 
4366           cs_real_t dist[3], grad_dist1[3], grad_dist2[3];
4367 
4368           for (cs_lnum_t i = 0; i < 3; i++)
4369             dist[i] = cell_cen[c_id1][i] - cell_cen[c_id2][i];
4370 
4371           for (cs_lnum_t i = 0; i < 3; i++) {
4372 
4373             grad_dist1[i] =   gradv[c_id1][i][0] * dist[0]
4374                             + gradv[c_id1][i][1] * dist[1]
4375                             + gradv[c_id1][i][2] * dist[2];
4376 
4377             grad_dist2[i] =   gradv[c_id2][i][0] * dist[0]
4378                             + gradv[c_id2][i][1] * dist[1]
4379                             + gradv[c_id2][i][2] * dist[2];
4380 
4381           }
4382 
4383           cs_real_t  dvar_sq, dist_sq1, dist_sq2;
4384 
4385           dist_sq1 =   grad_dist1[0]*grad_dist1[0]
4386                      + grad_dist1[1]*grad_dist1[1]
4387                      + grad_dist1[2]*grad_dist1[2];
4388 
4389           dist_sq2 =   grad_dist2[0]*grad_dist2[0]
4390                      + grad_dist2[1]*grad_dist2[1]
4391                      + grad_dist2[2]*grad_dist2[2];
4392 
4393           dvar_sq =     (pvar[c_id1][0]-pvar[c_id2][0])
4394                       * (pvar[c_id1][0]-pvar[c_id2][0])
4395                     +   (pvar[c_id1][1]-pvar[c_id2][1])
4396                       * (pvar[c_id1][1]-pvar[c_id2][1])
4397                     +   (pvar[c_id1][2]-pvar[c_id2][2])
4398                       * (pvar[c_id1][2]-pvar[c_id2][2]);
4399 
4400           denum[c_id1] = CS_MAX(denum[c_id1], dist_sq1);
4401           denum[c_id2] = CS_MAX(denum[c_id2], dist_sq2);
4402           denom[c_id1] = CS_MAX(denom[c_id1], dvar_sq);
4403           denom[c_id2] = CS_MAX(denom[c_id2], dvar_sq);
4404 
4405         } /* End of loop on faces */
4406 
4407       } /* End of loop on threads */
4408 
4409     } /* End of loop on thread groups */
4410 
4411     /* Complement for extended neighborhood */
4412 
4413     if (cell_cells_idx != NULL && halo_type == CS_HALO_EXTENDED) {
4414 
4415 #     pragma omp parallel for
4416       for (cs_lnum_t c_id1 = 0; c_id1 < n_cells; c_id1++) {
4417         for (cs_lnum_t cidx = cell_cells_idx[c_id1];
4418              cidx < cell_cells_idx[c_id1+1];
4419              cidx++) {
4420 
4421           cs_lnum_t c_id2 = cell_cells_lst[cidx];
4422 
4423 
4424           cs_real_t dist[3], grad_dist1[3];
4425 
4426           for (cs_lnum_t i = 0; i < 3; i++)
4427             dist[i] = cell_cen[c_id1][i] - cell_cen[c_id2][i];
4428 
4429           for (cs_lnum_t i = 0; i < 3; i++)
4430             grad_dist1[i] =   gradv[c_id1][i][0] * dist[0]
4431                             + gradv[c_id1][i][1] * dist[1]
4432                             + gradv[c_id1][i][2] * dist[2];
4433 
4434           cs_real_t  dvar_sq, dist_sq1;
4435 
4436           dist_sq1 =   grad_dist1[0]*grad_dist1[0]
4437                      + grad_dist1[1]*grad_dist1[1]
4438                      + grad_dist1[2]*grad_dist1[2];
4439 
4440           dvar_sq =     (pvar[c_id1][0]-pvar[c_id2][0])
4441                       * (pvar[c_id1][0]-pvar[c_id2][0])
4442                     +   (pvar[c_id1][1]-pvar[c_id2][1])
4443                       * (pvar[c_id1][1]-pvar[c_id2][1])
4444                     +   (pvar[c_id1][2]-pvar[c_id2][2])
4445                       * (pvar[c_id1][2]-pvar[c_id2][2]);
4446 
4447           denum[c_id1] = CS_MAX(denum[c_id1], dist_sq1);
4448           denom[c_id1] = CS_MAX(denom[c_id1], dvar_sq);
4449 
4450         }
4451       }
4452 
4453     } /* End for extended halo */
4454 
4455   }
4456 
4457   /* Second clipping Algorithm: based on the face gradient */
4458   /*-------------------------------------------------------*/
4459 
4460   else if (clip_mode == 1) {
4461 
4462     for (int g_id = 0; g_id < n_i_groups; g_id++) {
4463 
4464 #     pragma omp parallel for
4465       for (int t_id = 0; t_id < n_i_threads; t_id++) {
4466 
4467         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
4468              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
4469              f_id++) {
4470 
4471           cs_lnum_t c_id1 = i_face_cells[f_id][0];
4472           cs_lnum_t c_id2 = i_face_cells[f_id][1];
4473 
4474           cs_real_t dist[3], grad_dist1[3];
4475 
4476           for (cs_lnum_t i = 0; i < 3; i++)
4477             dist[i] = cell_cen[c_id1][i] - cell_cen[c_id2][i];
4478 
4479           for (cs_lnum_t i = 0; i < 3; i++)
4480             grad_dist1[i]
4481               = 0.5 * (  (gradv[c_id1][i][0]+gradv[c_id2][i][0])*dist[0]
4482                        + (gradv[c_id1][i][1]+gradv[c_id2][i][1])*dist[1]
4483                        + (gradv[c_id1][i][2]+gradv[c_id2][i][2])*dist[2]);
4484 
4485           cs_real_t dist_sq1, dvar_sq;
4486 
4487           dist_sq1 =   grad_dist1[0]*grad_dist1[0]
4488                      + grad_dist1[1]*grad_dist1[1]
4489                      + grad_dist1[2]*grad_dist1[2];
4490 
4491           dvar_sq =     (pvar[c_id1][0]-pvar[c_id2][0])
4492                       * (pvar[c_id1][0]-pvar[c_id2][0])
4493                     +   (pvar[c_id1][1]-pvar[c_id2][1])
4494                       * (pvar[c_id1][1]-pvar[c_id2][1])
4495                     +   (pvar[c_id1][2]-pvar[c_id2][2])
4496                       * (pvar[c_id1][2]-pvar[c_id2][2]);
4497 
4498           denum[c_id1] = CS_MAX(denum[c_id1], dist_sq1);
4499           denum[c_id2] = CS_MAX(denum[c_id2], dist_sq1);
4500           denom[c_id1] = CS_MAX(denom[c_id1], dvar_sq);
4501           denom[c_id2] = CS_MAX(denom[c_id2], dvar_sq);
4502 
4503         } /* End of loop on threads */
4504 
4505       } /* End of loop on thread groups */
4506 
4507     } /* End of loop on faces */
4508 
4509     /* Complement for extended neighborhood */
4510 
4511     if (cell_cells_idx != NULL && halo_type == CS_HALO_EXTENDED) {
4512 
4513 #     pragma omp parallel for
4514       for (cs_lnum_t c_id1 = 0; c_id1 < n_cells; c_id1++) {
4515         for (cs_lnum_t cidx = cell_cells_idx[c_id1];
4516              cidx < cell_cells_idx[c_id1+1];
4517              cidx++) {
4518 
4519           cs_lnum_t c_id2 = cell_cells_lst[cidx];
4520 
4521           cs_real_t dist[3], grad_dist1[3];
4522 
4523           for (cs_lnum_t i = 0; i < 3; i++)
4524             dist[i] = cell_cen[c_id1][i] - cell_cen[c_id2][i];
4525 
4526           for (cs_lnum_t i = 0; i < 3; i++)
4527             grad_dist1[i]
4528               = 0.5 * (  (gradv[c_id1][i][0]+gradv[c_id2][i][0])*dist[0]
4529                        + (gradv[c_id1][i][1]+gradv[c_id2][i][1])*dist[1]
4530                        + (gradv[c_id1][i][2]+gradv[c_id2][i][2])*dist[2]);
4531 
4532           cs_real_t dist_sq1, dvar_sq;
4533 
4534           dist_sq1 =   grad_dist1[0]*grad_dist1[0]
4535                      + grad_dist1[1]*grad_dist1[1]
4536                      + grad_dist1[2]*grad_dist1[2];
4537 
4538           dvar_sq =     (pvar[c_id1][0]-pvar[c_id2][0])
4539                       * (pvar[c_id1][0]-pvar[c_id2][0])
4540                     +   (pvar[c_id1][1]-pvar[c_id2][1])
4541                       * (pvar[c_id1][1]-pvar[c_id2][1])
4542                     +   (pvar[c_id1][2]-pvar[c_id2][2])
4543                       * (pvar[c_id1][2]-pvar[c_id2][2]);
4544 
4545           denum[c_id1] = CS_MAX(denum[c_id1], dist_sq1);
4546           denom[c_id1] = CS_MAX(denom[c_id1], dvar_sq);
4547 
4548         }
4549       }
4550 
4551     } /* End for extended neighborhood */
4552 
4553     /* Synchronize variable */
4554 
4555     if (halo != NULL) {
4556       cs_halo_sync_var(m->halo, halo_type, denom);
4557       cs_halo_sync_var(m->halo, halo_type, denum);
4558     }
4559 
4560   } /* End if clip_mode == 1 */
4561 
4562   /* Clipping of the gradient if denum/denom > climgp**2 */
4563 
4564   /* First clipping Algorithm: based on the cell gradient */
4565   /*------------------------------------------------------*/
4566 
4567   if (clip_mode == 0) {
4568 
4569 #   pragma omp parallel
4570     {
4571       cs_gnum_t t_n_clip = 0;
4572       cs_real_t t_min_factor = min_factor, t_max_factor = max_factor;
4573 
4574 #     pragma omp for
4575       for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
4576 
4577         if (denum[c_id] > clipp_coef_sq * denom[c_id]) {
4578 
4579           cs_real_t factor1 = sqrt(clipp_coef_sq * denom[c_id]/denum[c_id]);
4580 
4581           for (cs_lnum_t i = 0; i < 3; i++) {
4582             for (cs_lnum_t j = 0; j < 3; j++)
4583               gradv[c_id][i][j] *= factor1;
4584           }
4585 
4586           t_min_factor = CS_MIN(factor1, t_min_factor);
4587           t_max_factor = CS_MAX(factor1, t_max_factor);
4588           t_n_clip++;
4589 
4590         } /* If clipping */
4591 
4592       } /* End of loop on cells */
4593 
4594 #     pragma omp critical
4595       {
4596         min_factor = CS_MIN(min_factor, t_min_factor);
4597         max_factor = CS_MAX(max_factor, t_max_factor);
4598         n_clip += t_n_clip;
4599       }
4600     } /* End of omp parallel construct */
4601 
4602   }
4603 
4604   /* Second clipping Algorithm: based on the face gradient */
4605   /*-------------------------------------------------------*/
4606 
4607   else if (clip_mode == 1) {
4608 
4609     for (int g_id = 0; g_id < n_i_groups; g_id++) {
4610 
4611 #     pragma omp parallel for
4612       for (int t_id = 0; t_id < n_i_threads; t_id++) {
4613 
4614         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
4615              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
4616              f_id++) {
4617 
4618           cs_lnum_t c_id1 = i_face_cells[f_id][0];
4619           cs_lnum_t c_id2 = i_face_cells[f_id][1];
4620 
4621           cs_real_t factor1 = 1.0;
4622           if (denum[c_id1] > clipp_coef_sq * denom[c_id1])
4623             factor1 = sqrt(clipp_coef_sq * denom[c_id1]/denum[c_id1]);
4624 
4625           cs_real_t factor2 = 1.0;
4626           if (denum[c_id2] > clipp_coef_sq * denom[c_id2])
4627             factor2 = sqrt(clipp_coef_sq * denom[c_id2]/denum[c_id2]);
4628 
4629           cs_real_t l_min_factor = CS_MIN(factor1, factor2);
4630 
4631           clip_factor[c_id1] = CS_MIN(clip_factor[c_id1], l_min_factor);
4632           clip_factor[c_id2] = CS_MIN(clip_factor[c_id2], l_min_factor);
4633 
4634         } /* End of loop on faces */
4635 
4636       } /* End of loop on threads */
4637 
4638     } /* End of loop on thread groups */
4639 
4640     /* Complement for extended neighborhood */
4641 
4642     if (cell_cells_idx != NULL && halo_type == CS_HALO_EXTENDED) {
4643 
4644 #     pragma omp parallel for
4645       for (cs_lnum_t c_id1 = 0; c_id1 < n_cells; c_id1++) {
4646 
4647         cs_real_t l_min_factor = 1.0;
4648 
4649         for (cs_lnum_t cidx = cell_cells_idx[c_id1];
4650              cidx < cell_cells_idx[c_id1+1];
4651              cidx++) {
4652 
4653           cs_lnum_t c_id2 = cell_cells_lst[cidx];
4654           cs_real_t factor2 = 1.0;
4655 
4656           if (denum[c_id2] > clipp_coef_sq * denom[c_id2])
4657             factor2 = sqrt(clipp_coef_sq * denom[c_id2]/denum[c_id2]);
4658 
4659           l_min_factor = CS_MIN(l_min_factor, factor2);
4660 
4661         }
4662 
4663         clip_factor[c_id1] = CS_MIN(clip_factor[c_id1], l_min_factor);
4664 
4665       } /* End of loop on cells */
4666 
4667     } /* End for extended neighborhood */
4668 
4669 #   pragma omp parallel
4670     {
4671       cs_gnum_t t_n_clip = 0;
4672       cs_real_t t_min_factor = min_factor, t_max_factor = max_factor;
4673 
4674 #     pragma omp for
4675       for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
4676 
4677         for (cs_lnum_t i = 0; i < 3; i++) {
4678           for (cs_lnum_t j = 0; j < 3; j++)
4679             gradv[c_id][i][j] *= clip_factor[c_id];
4680         }
4681 
4682         if (clip_factor[c_id] < 0.99) {
4683           t_max_factor = CS_MAX(t_max_factor, clip_factor[c_id]);
4684           t_min_factor = CS_MIN(t_min_factor, clip_factor[c_id]);
4685           t_n_clip++;
4686         }
4687 
4688       } /* End of loop on cells */
4689 
4690 #     pragma omp critical
4691       {
4692         min_factor = CS_MIN(min_factor, t_min_factor);
4693         max_factor = CS_MAX(max_factor, t_max_factor);
4694         n_clip += t_n_clip;
4695       }
4696     } /* End of omp parallel construct */
4697 
4698   } /* End if clip_mode == 1 */
4699 
4700   /* Update min/max and n_clip in case of parallelism */
4701   /*--------------------------------------------------*/
4702 
4703 #if defined(HAVE_MPI)
4704 
4705   if (m->n_domains > 1) {
4706 
4707     assert(sizeof(cs_real_t) == sizeof(double));
4708 
4709     /* Global Max */
4710 
4711     MPI_Allreduce(&max_factor, &global_max_factor, 1, CS_MPI_REAL,
4712                   MPI_MAX, cs_glob_mpi_comm);
4713 
4714     max_factor = global_max_factor;
4715 
4716     /* Global min */
4717 
4718     MPI_Allreduce(&min_factor, &global_min_factor, 1, CS_MPI_REAL,
4719                   MPI_MIN, cs_glob_mpi_comm);
4720 
4721     min_factor = global_min_factor;
4722 
4723     /* Sum number of clippings */
4724 
4725     MPI_Allreduce(&n_clip, &n_g_clip, 1, CS_MPI_GNUM,
4726                   MPI_SUM, cs_glob_mpi_comm);
4727 
4728     n_clip = n_g_clip;
4729 
4730   } /* If n_domains > 1 */
4731 
4732 #endif /* defined(HAVE_MPI) */
4733 
4734   /* Output warning if necessary */
4735 
4736   if (verbosity > 1)
4737     bft_printf(_(" Variable: %s; Gradient of a vector limitation in %llu cells\n"
4738                  "   minimum factor = %14.5e; maximum factor = %14.5e\n"),
4739                var_name,
4740                (unsigned long long)n_clip, min_factor, max_factor);
4741 
4742   /* Synchronize the updated Gradient */
4743 
4744   if (m->halo != NULL) {
4745     cs_halo_sync_var_strided(m->halo, halo_type, (cs_real_t *)gradv, 9);
4746     if (cs_glob_mesh->have_rotation_perio)
4747       cs_halo_perio_sync_var_tens(m->halo, halo_type, (cs_real_t *)gradv);
4748   }
4749 
4750   BFT_FREE(buf);
4751 }
4752 
4753 /*----------------------------------------------------------------------------
4754  * Initialize the gradient of a vector for gradient reconstruction.
4755  *
4756  * A non-reconstructed gradient is computed at this stage.
4757  *
4758  * parameters:
4759  *   m              <-- pointer to associated mesh structure
4760  *   fvq            <-- pointer to associated finite volume quantities
4761  *   cpl            <-- structure associated with internal coupling, or NULL
4762  *   halo_type      <-- halo type (extended or not)
4763  *   inc            <-- if 0, solve on increment; 1 otherwise
4764  *   coefav         <-- B.C. coefficients for boundary face normals
4765  *   coefbv         <-- B.C. coefficients for boundary face normals
4766  *   pvar           <-- variable
4767  *   c_weight       <-- weighted gradient coefficient variable
4768  *   grad           --> gradient of pvar (du_i/dx_j : grad[][i][j])
4769  *----------------------------------------------------------------------------*/
4770 
4771 static void
_initialize_vector_gradient(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,cs_halo_type_t halo_type,int inc,const cs_real_3_t * restrict coefav,const cs_real_33_t * restrict coefbv,const cs_real_3_t * restrict pvar,const cs_real_t * restrict c_weight,cs_real_33_t * restrict grad)4772 _initialize_vector_gradient(const cs_mesh_t              *m,
4773                             const cs_mesh_quantities_t   *fvq,
4774                             const cs_internal_coupling_t *cpl,
4775                             cs_halo_type_t                halo_type,
4776                             int                           inc,
4777                             const cs_real_3_t   *restrict coefav,
4778                             const cs_real_33_t  *restrict coefbv,
4779                             const cs_real_3_t   *restrict pvar,
4780                             const cs_real_t     *restrict c_weight,
4781                             cs_real_33_t        *restrict grad)
4782 {
4783   const cs_lnum_t n_cells = m->n_cells;
4784   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
4785   const int n_i_groups = m->i_face_numbering->n_groups;
4786   const int n_i_threads = m->i_face_numbering->n_threads;
4787   const int n_b_groups = m->b_face_numbering->n_groups;
4788   const int n_b_threads = m->b_face_numbering->n_threads;
4789   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
4790   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
4791 
4792   const cs_lnum_2_t *restrict i_face_cells
4793     = (const cs_lnum_2_t *restrict)m->i_face_cells;
4794   const cs_lnum_t *restrict b_face_cells
4795     = (const cs_lnum_t *restrict)m->b_face_cells;
4796 
4797   const int *restrict c_disable_flag = fvq->c_disable_flag;
4798   cs_lnum_t has_dc = fvq->has_disable_flag; /* Has cells disabled? */
4799 
4800   const cs_real_t *restrict weight = fvq->weight;
4801   const cs_real_t *restrict cell_f_vol = fvq->cell_f_vol;
4802   if (cs_glob_porous_model == 1 || cs_glob_porous_model == 2)
4803     cell_f_vol = fvq->cell_vol;
4804 
4805   const cs_real_3_t *restrict i_f_face_normal
4806     = (const cs_real_3_t *restrict)fvq->i_f_face_normal;
4807   const cs_real_3_t *restrict b_f_face_normal
4808     = (const cs_real_3_t *restrict)fvq->b_f_face_normal;
4809 
4810   bool  *coupled_faces = (cpl == NULL) ?
4811     NULL : (bool *)cpl->coupled_faces;
4812 
4813   /* Computation without reconstruction */
4814   /*------------------------------------*/
4815 
4816   /* Initialization */
4817 
4818 # pragma omp parallel for
4819   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
4820     for (cs_lnum_t i = 0; i < 3; i++) {
4821       for (cs_lnum_t j = 0; j < 3; j++)
4822         grad[c_id][i][j] = 0.0;
4823     }
4824   }
4825 
4826   /* Interior faces contribution */
4827 
4828   for (int g_id = 0; g_id < n_i_groups; g_id++) {
4829 
4830 #   pragma omp parallel for
4831     for (int t_id = 0; t_id < n_i_threads; t_id++) {
4832 
4833       for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
4834            f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
4835            f_id++) {
4836 
4837         cs_lnum_t c_id1 = i_face_cells[f_id][0];
4838         cs_lnum_t c_id2 = i_face_cells[f_id][1];
4839 
4840         cs_real_t pond = weight[f_id];
4841 
4842         cs_real_t ktpond = (c_weight == NULL) ?
4843           pond :                    // no cell weighting
4844           pond * c_weight[c_id1] // cell weighting active
4845             / (      pond * c_weight[c_id1]
4846               + (1.0-pond)* c_weight[c_id2]);
4847 
4848         /*
4849            Remark: \f$ \varia_\face = \alpha_\ij \varia_\celli
4850                                     + (1-\alpha_\ij) \varia_\cellj\f$
4851                    but for the cell \f$ \celli \f$ we remove
4852                    \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
4853                    and for the cell \f$ \cellj \f$ we remove
4854                    \f$ \varia_\cellj \sum_\face \vect{S}_\face = \vect{0} \f$
4855         */
4856         for (cs_lnum_t i = 0; i < 3; i++) {
4857           cs_real_t pfaci = (1.0-ktpond) * (pvar[c_id2][i] - pvar[c_id1][i]);
4858           cs_real_t pfacj = - ktpond * (pvar[c_id2][i] - pvar[c_id1][i]);
4859 
4860           for (cs_lnum_t j = 0; j < 3; j++) {
4861             grad[c_id1][i][j] += pfaci * i_f_face_normal[f_id][j];
4862             grad[c_id2][i][j] -= pfacj * i_f_face_normal[f_id][j];
4863           }
4864         }
4865 
4866       } /* End of loop on faces */
4867 
4868     } /* End of loop on threads */
4869 
4870   } /* End of loop on thread groups */
4871 
4872   /* Contribution from coupled faces */
4873   if (cpl != NULL)
4874     cs_internal_coupling_initialize_vector_gradient
4875       (cpl, c_weight, pvar, grad);
4876 
4877   /* Boundary face treatment */
4878 
4879   for (int g_id = 0; g_id < n_b_groups; g_id++) {
4880 
4881 #   pragma omp parallel for
4882     for (int t_id = 0; t_id < n_b_threads; t_id++) {
4883 
4884       for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
4885            f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
4886            f_id++) {
4887 
4888         if (cpl == NULL || !coupled_faces[f_id]) {
4889 
4890           cs_lnum_t c_id = b_face_cells[f_id];
4891 
4892           for (cs_lnum_t i = 0; i < 3; i++) {
4893             cs_real_t pfac = inc*coefav[f_id][i];
4894 
4895             for (cs_lnum_t k = 0; k < 3; k++) {
4896               if (i == k)
4897                 pfac += (coefbv[f_id][i][k] - 1.0) * pvar[c_id][k];
4898               else
4899                 pfac += coefbv[f_id][i][k] * pvar[c_id][k];
4900             }
4901 
4902             for (cs_lnum_t j = 0; j < 3; j++)
4903               grad[c_id][i][j] += pfac * b_f_face_normal[f_id][j];
4904           }
4905         }
4906 
4907       } /* loop on faces */
4908 
4909     } /* loop on threads */
4910 
4911   } /* loop on thread groups */
4912 
4913 # pragma omp parallel for
4914   for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
4915     cs_real_t dvol;
4916     /* Is the cell disabled (for solid or porous)? Not the case if coupled */
4917     if (has_dc * c_disable_flag[has_dc * c_id] == 0)
4918       dvol = 1. / cell_f_vol[c_id];
4919     else
4920       dvol = 0.;
4921 
4922     for (cs_lnum_t i = 0; i < 3; i++) {
4923       for (cs_lnum_t j = 0; j < 3; j++)
4924         grad[c_id][i][j] *= dvol;
4925     }
4926   }
4927 
4928   /* Periodicity and parallelism treatment */
4929 
4930   if (m->halo != NULL) {
4931     cs_halo_sync_var_strided(m->halo, halo_type, (cs_real_t *)grad, 9);
4932     if (cs_glob_mesh->have_rotation_perio)
4933       cs_halo_perio_sync_var_tens(m->halo, halo_type, (cs_real_t *)grad);
4934   }
4935 }
4936 
4937 /*----------------------------------------------------------------------------
4938  * Reconstruct the gradient of a vector using a given gradient of
4939  * this vector (typically lsq).
4940  *
4941  * parameters:
4942  *   m              <-- pointer to associated mesh structure
4943  *   fvq            <-- pointer to associated finite volume quantities
4944  *   cpl            <-- structure associated with internal coupling, or NULL
4945  *   inc            <-- if 0, solve on increment; 1 otherwise
4946  *   coefav         <-- B.C. coefficients for boundary face normals
4947  *   coefbv         <-- B.C. coefficients for boundary face normals
4948  *   pvar           <-- variable
4949  *   c_weight       <-- weighted gradient coefficient variable
4950  *   r_grad         --> gradient used for reconstruction
4951  *   grad           --> gradient of pvar (du_i/dx_j : grad[][i][j])
4952  *----------------------------------------------------------------------------*/
4953 
4954 static void
_reconstruct_vector_gradient(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,cs_halo_type_t halo_type,int inc,const cs_real_3_t * restrict coefav,const cs_real_33_t * restrict coefbv,const cs_real_3_t * restrict pvar,const cs_real_t * restrict c_weight,cs_real_33_t * restrict r_grad,cs_real_33_t * restrict grad)4955 _reconstruct_vector_gradient(const cs_mesh_t              *m,
4956                              const cs_mesh_quantities_t   *fvq,
4957                              const cs_internal_coupling_t *cpl,
4958                              cs_halo_type_t                halo_type,
4959                              int                           inc,
4960                              const cs_real_3_t   *restrict coefav,
4961                              const cs_real_33_t  *restrict coefbv,
4962                              const cs_real_3_t   *restrict pvar,
4963                              const cs_real_t     *restrict c_weight,
4964                              cs_real_33_t        *restrict r_grad,
4965                              cs_real_33_t        *restrict grad)
4966 {
4967   const cs_lnum_t n_cells = m->n_cells;
4968   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
4969   const int n_i_groups = m->i_face_numbering->n_groups;
4970   const int n_i_threads = m->i_face_numbering->n_threads;
4971   const int n_b_groups = m->b_face_numbering->n_groups;
4972   const int n_b_threads = m->b_face_numbering->n_threads;
4973   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
4974   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
4975 
4976   const cs_lnum_2_t *restrict i_face_cells
4977     = (const cs_lnum_2_t *restrict)m->i_face_cells;
4978   const cs_lnum_t *restrict b_face_cells
4979     = (const cs_lnum_t *restrict)m->b_face_cells;
4980 
4981   const int *restrict c_disable_flag = fvq->c_disable_flag;
4982   cs_lnum_t has_dc = fvq->has_disable_flag; /* Has cells disabled? */
4983 
4984   const cs_real_t *restrict weight = fvq->weight;
4985   const cs_real_t *restrict cell_f_vol = fvq->cell_f_vol;
4986   if (cs_glob_porous_model == 1 || cs_glob_porous_model == 2)
4987     cell_f_vol = fvq->cell_vol;
4988 
4989   const cs_real_3_t *restrict i_f_face_normal
4990     = (const cs_real_3_t *restrict)fvq->i_f_face_normal;
4991   const cs_real_3_t *restrict b_f_face_normal
4992     = (const cs_real_3_t *restrict)fvq->b_f_face_normal;
4993   const cs_real_3_t *restrict diipb
4994     = (const cs_real_3_t *restrict)fvq->diipb;
4995   const cs_real_3_t *restrict dofij
4996     = (const cs_real_3_t *restrict)fvq->dofij;
4997   const cs_real_33_t *restrict corr_grad_lin
4998     = (const cs_real_33_t *restrict)fvq->corr_grad_lin;
4999 
5000   bool  *coupled_faces = (cpl == NULL) ?
5001     NULL : (bool *)cpl->coupled_faces;
5002 
5003   /* Initialize gradient */
5004   /*---------------------*/
5005 
5006   /* Initialization */
5007 
5008 # pragma omp parallel for
5009   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
5010     for (cs_lnum_t i = 0; i < 3; i++) {
5011       for (cs_lnum_t j = 0; j < 3; j++)
5012         grad[c_id][i][j] = 0.0;
5013     }
5014   }
5015 
5016   /* Interior faces contribution */
5017 
5018   for (int g_id = 0; g_id < n_i_groups; g_id++) {
5019 
5020 #   pragma omp parallel for
5021     for (int t_id = 0; t_id < n_i_threads; t_id++) {
5022 
5023       for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
5024            f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
5025            f_id++) {
5026 
5027         cs_lnum_t c_id1 = i_face_cells[f_id][0];
5028         cs_lnum_t c_id2 = i_face_cells[f_id][1];
5029 
5030         cs_real_t pond = weight[f_id];
5031 
5032         cs_real_t ktpond = (c_weight == NULL) ?
5033           pond :                    // no cell weighting
5034           pond * c_weight[c_id1] // cell weighting active
5035             / (      pond * c_weight[c_id1]
5036               + (1.0-pond)* c_weight[c_id2]);
5037 
5038         /*
5039            Remark: \f$ \varia_\face = \alpha_\ij \varia_\celli
5040                                     + (1-\alpha_\ij) \varia_\cellj\f$
5041                    but for the cell \f$ \celli \f$ we remove
5042                    \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
5043                    and for the cell \f$ \cellj \f$ we remove
5044                    \f$ \varia_\cellj \sum_\face \vect{S}_\face = \vect{0} \f$
5045         */
5046 
5047         for (cs_lnum_t i = 0; i < 3; i++) {
5048 
5049           cs_real_t pfaci = (1.0-ktpond) * (pvar[c_id2][i] - pvar[c_id1][i]);
5050           cs_real_t pfacj = - ktpond * (pvar[c_id2][i] - pvar[c_id1][i]);
5051 
5052           /* Reconstruction part */
5053           cs_real_t rfac = 0.5 * (  dofij[f_id][0]*(  r_grad[c_id1][i][0]
5054                                                     + r_grad[c_id2][i][0])
5055                                   + dofij[f_id][1]*(  r_grad[c_id1][i][1]
5056                                                     + r_grad[c_id2][i][1])
5057                                   + dofij[f_id][2]*(  r_grad[c_id1][i][2]
5058                                                     + r_grad[c_id2][i][2]));
5059 
5060           for (cs_lnum_t j = 0; j < 3; j++) {
5061             grad[c_id1][i][j] += (pfaci + rfac) * i_f_face_normal[f_id][j];
5062             grad[c_id2][i][j] -= (pfacj + rfac) * i_f_face_normal[f_id][j];
5063           }
5064 
5065         }
5066 
5067       } /* End of loop on faces */
5068 
5069     } /* End of loop on threads */
5070 
5071   } /* End of loop on thread groups */
5072 
5073   /* Contribution from coupled faces */
5074   if (cpl != NULL) {
5075     cs_internal_coupling_initialize_vector_gradient(cpl, c_weight, pvar, grad);
5076     cs_internal_coupling_reconstruct_vector_gradient(cpl, r_grad, grad);
5077   }
5078 
5079   /* Boundary face treatment */
5080 
5081   for (int g_id = 0; g_id < n_b_groups; g_id++) {
5082 
5083 #   pragma omp parallel for
5084     for (int t_id = 0; t_id < n_b_threads; t_id++) {
5085 
5086       for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
5087            f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
5088            f_id++) {
5089 
5090         if (cpl == NULL || !coupled_faces[f_id]) {
5091           cs_lnum_t c_id = b_face_cells[f_id];
5092 
5093           /*
5094              Remark: for the cell \f$ \celli \f$ we remove
5095                      \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
5096            */
5097 
5098           for (cs_lnum_t i = 0; i < 3; i++) {
5099 
5100             cs_real_t pfac = inc*coefav[f_id][i];
5101 
5102             for (cs_lnum_t k = 0; k < 3; k++)
5103               pfac += coefbv[f_id][i][k] * pvar[c_id][k];
5104 
5105             pfac -= pvar[c_id][i];
5106 
5107             /* Reconstruction part */
5108             cs_real_t rfac = 0.;
5109             for (cs_lnum_t k = 0; k < 3; k++) {
5110               cs_real_t vecfac =   r_grad[c_id][k][0] * diipb[f_id][0]
5111                                  + r_grad[c_id][k][1] * diipb[f_id][1]
5112                                  + r_grad[c_id][k][2] * diipb[f_id][2];
5113               rfac += coefbv[f_id][i][k] * vecfac;
5114             }
5115 
5116             for (cs_lnum_t j = 0; j < 3; j++)
5117               grad[c_id][i][j] += (pfac + rfac) * b_f_face_normal[f_id][j];
5118           }
5119         }
5120 
5121       } /* loop on faces */
5122 
5123     } /* loop on threads */
5124 
5125   } /* loop on thread groups */
5126 
5127 # pragma omp parallel for
5128   for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
5129     cs_real_t dvol;
5130     /* Is the cell disabled (for solid or porous)? Not the case if coupled */
5131     if (has_dc * c_disable_flag[has_dc * c_id] == 0)
5132       dvol = 1. / cell_f_vol[c_id];
5133     else
5134       dvol = 0.;
5135 
5136     for (cs_lnum_t i = 0; i < 3; i++) {
5137       for (cs_lnum_t j = 0; j < 3; j++)
5138         grad[c_id][i][j] *= dvol;
5139     }
5140 
5141     if (cs_glob_mesh_quantities_flag & CS_BAD_CELLS_WARPED_CORRECTION) {
5142       cs_real_t gradpa[3];
5143       for (cs_lnum_t i = 0; i < 3; i++) {
5144         for (cs_lnum_t j = 0; j < 3; j++) {
5145           gradpa[j] = grad[c_id][i][j];
5146           grad[c_id][i][j] = 0.;
5147         }
5148 
5149         for (cs_lnum_t j = 0; j < 3; j++)
5150           for (cs_lnum_t k = 0; k < 3; k++)
5151             grad[c_id][i][j] += corr_grad_lin[c_id][j][k] * gradpa[k];
5152       }
5153     }
5154   }
5155 
5156   /* Periodicity and parallelism treatment */
5157 
5158   if (m->halo != NULL) {
5159     cs_halo_sync_var_strided(m->halo, halo_type, (cs_real_t *)grad, 9);
5160     if (cs_glob_mesh->have_rotation_perio)
5161       cs_halo_perio_sync_var_tens(m->halo, halo_type, (cs_real_t *)grad);
5162   }
5163 }
5164 
5165 /*----------------------------------------------------------------------------
5166  * Compute the gradient of a vector with an iterative technique in order to
5167  * handle non-orthoganalities (n_r_sweeps > 1).
5168  *
5169  * We do not take into account any volumic force here.
5170  *
5171  * parameters:
5172  *   m              <-- pointer to associated mesh structure
5173  *   fvq            <-> pointer to associated finite volume quantities
5174  *   cpl            <-> structure associated with internal coupling, or NULL
5175  *   var_name       <-- variable's name
5176  *   gradient_info  <-- pointer to performance logging structure, or NULL
5177  *   halo_type      <-- halo type (extended or not)
5178  *   inc            <-- if 0, solve on increment; 1 otherwise
5179  *   n_r_sweeps     --> >1: with reconstruction
5180  *   verbosity      --> verbosity level
5181  *   epsrgp         --> precision for iterative gradient calculation
5182  *   coefav         <-- B.C. coefficients for boundary face normals
5183  *   coefbv         <-- B.C. coefficients for boundary face normals
5184  *   pvar           <-- variable
5185  *   c_weight       <-- weighted gradient coefficient variable
5186  *   grad           <-> gradient of pvar (du_i/dx_j : grad[][i][j])
5187  *----------------------------------------------------------------------------*/
5188 
5189 static void
_iterative_vector_gradient(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,const char * var_name,cs_gradient_info_t * gradient_info,cs_halo_type_t halo_type,int inc,int n_r_sweeps,int verbosity,cs_real_t epsrgp,const cs_real_3_t * restrict coefav,const cs_real_33_t * restrict coefbv,const cs_real_3_t * restrict pvar,const cs_real_t * c_weight,cs_real_33_t * restrict grad)5190 _iterative_vector_gradient(const cs_mesh_t               *m,
5191                            const cs_mesh_quantities_t    *fvq,
5192                            const cs_internal_coupling_t  *cpl,
5193                            const char                    *var_name,
5194                            cs_gradient_info_t            *gradient_info,
5195                            cs_halo_type_t                 halo_type,
5196                            int                            inc,
5197                            int                            n_r_sweeps,
5198                            int                            verbosity,
5199                            cs_real_t                      epsrgp,
5200                            const cs_real_3_t   *restrict  coefav,
5201                            const cs_real_33_t  *restrict  coefbv,
5202                            const cs_real_3_t   *restrict  pvar,
5203                            const cs_real_t               *c_weight,
5204                            cs_real_33_t         *restrict grad)
5205 {
5206   int isweep = 0;
5207 
5208   cs_real_33_t *rhs;
5209 
5210   const cs_lnum_t n_cells = m->n_cells;
5211   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
5212   const int n_i_groups = m->i_face_numbering->n_groups;
5213   const int n_i_threads = m->i_face_numbering->n_threads;
5214   const int n_b_groups = m->b_face_numbering->n_groups;
5215   const int n_b_threads = m->b_face_numbering->n_threads;
5216   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
5217   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
5218 
5219   const cs_lnum_2_t *restrict i_face_cells
5220     = (const cs_lnum_2_t *restrict)m->i_face_cells;
5221   const cs_lnum_t *restrict b_face_cells
5222     = (const cs_lnum_t *restrict)m->b_face_cells;
5223 
5224   const int *restrict c_disable_flag = fvq->c_disable_flag;
5225   cs_lnum_t has_dc = fvq->has_disable_flag; /* Has cells disabled? */
5226 
5227   const cs_real_t *restrict weight = fvq->weight;
5228   const cs_real_t *restrict cell_f_vol = fvq->cell_f_vol;
5229   if (cs_glob_porous_model == 1 || cs_glob_porous_model == 2)
5230     cell_f_vol = fvq->cell_vol;
5231   const cs_real_3_t *restrict i_f_face_normal
5232     = (const cs_real_3_t *restrict)fvq->i_f_face_normal;
5233   const cs_real_3_t *restrict b_f_face_normal
5234     = (const cs_real_3_t *restrict)fvq->b_f_face_normal;
5235   const cs_real_3_t *restrict diipb
5236     = (const cs_real_3_t *restrict)fvq->diipb;
5237   const cs_real_3_t *restrict dofij
5238     = (const cs_real_3_t *restrict)fvq->dofij;
5239 
5240   int gq_id = (cpl == NULL) ? 0 : cpl->id+1;
5241   cs_gradient_quantities_t  *gq = _gradient_quantities_get(gq_id);
5242 
5243   cs_real_33_t *restrict cocg = gq->cocg_it;
5244   if (cocg == NULL)
5245     cocg = _compute_cell_cocg_it(m, fvq, cpl, gq);
5246 
5247   bool  *coupled_faces = (cpl == NULL) ?
5248     NULL : (bool *)cpl->coupled_faces;
5249 
5250   BFT_MALLOC(rhs, n_cells_ext, cs_real_33_t);
5251 
5252   /* Gradient reconstruction to handle non-orthogonal meshes */
5253   /*---------------------------------------------------------*/
5254 
5255   /* L2 norm */
5256 
5257   cs_real_t l2_norm = _l2_norm_1(9*n_cells, (cs_real_t *)grad);
5258   cs_real_t l2_residual = l2_norm;
5259 
5260   if (l2_norm > cs_math_epzero) {
5261 
5262     /* Iterative process */
5263     /*-------------------*/
5264 
5265     for (isweep = 1; isweep < n_r_sweeps && l2_residual > epsrgp*l2_norm; isweep++) {
5266 
5267       /* Computation of the Right Hand Side*/
5268 
5269 #     pragma omp parallel for
5270       for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
5271         for (cs_lnum_t i = 0; i < 3; i++) {
5272           for (cs_lnum_t j = 0; j < 3; j++)
5273             rhs[c_id][i][j] = -grad[c_id][i][j] * cell_f_vol[c_id];
5274         }
5275       }
5276 
5277       /* Interior face treatment */
5278 
5279       for (int g_id = 0; g_id < n_i_groups; g_id++) {
5280 
5281 #       pragma omp parallel for
5282         for (int t_id = 0; t_id < n_i_threads; t_id++) {
5283 
5284           for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
5285                f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
5286                f_id++) {
5287 
5288             cs_lnum_t c_id1 = i_face_cells[f_id][0];
5289             cs_lnum_t c_id2 = i_face_cells[f_id][1];
5290             cs_real_t pond = weight[f_id];
5291 
5292             cs_real_t ktpond = (c_weight == NULL) ?
5293               pond :                     // no cell weighting
5294               pond  * c_weight[c_id1] // cell weighting active
5295                 / (      pond  * c_weight[c_id1]
5296                   + (1.0-pond) * c_weight[c_id2]);
5297 
5298             /*
5299                Remark: \f$ \varia_\face = \alpha_\ij \varia_\celli
5300                                         + (1-\alpha_\ij) \varia_\cellj\f$
5301                        but for the cell \f$ \celli \f$ we remove
5302                        \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
5303                        and for the cell \f$ \cellj \f$ we remove
5304                        \f$ \varia_\cellj \sum_\face \vect{S}_\face = \vect{0} \f$
5305             */
5306 
5307             for (cs_lnum_t i = 0; i < 3; i++) {
5308 
5309               /* Reconstruction part */
5310               cs_real_t
5311                 pfaci = 0.5 * (    (grad[c_id1][i][0] + grad[c_id2][i][0])
5312                                  * dofij[f_id][0]
5313                                +   (grad[c_id1][i][1] + grad[c_id2][i][1])
5314                                  * dofij[f_id][1]
5315                                +   (grad[c_id1][i][2] + grad[c_id2][i][2])
5316                                  * dofij[f_id][2]);
5317               cs_real_t pfacj = pfaci;
5318 
5319               pfaci += (1.0-ktpond) * (pvar[c_id2][i] - pvar[c_id1][i]);
5320               pfacj -=      ktpond  * (pvar[c_id2][i] - pvar[c_id1][i]);
5321 
5322               for (cs_lnum_t j = 0; j < 3; j++) {
5323                 rhs[c_id1][i][j] += pfaci * i_f_face_normal[f_id][j];
5324                 rhs[c_id2][i][j] -= pfacj * i_f_face_normal[f_id][j];
5325               }
5326             }
5327 
5328           } /* loop on faces */
5329 
5330         } /* loop on threads */
5331 
5332       } /* loop on thread groups */
5333 
5334       /* Contribution from coupled faces */
5335       if (cpl != NULL)
5336         cs_internal_coupling_iterative_vector_gradient
5337           (cpl, c_weight, grad, pvar, rhs);
5338 
5339       /* Boundary face treatment */
5340 
5341       for (int g_id = 0; g_id < n_b_groups; g_id++) {
5342 
5343 #       pragma omp parallel for
5344         for (int t_id = 0; t_id < n_b_threads; t_id++) {
5345 
5346           for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
5347                f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
5348                f_id++) {
5349 
5350             if (cpl == NULL || !coupled_faces[f_id]) {
5351 
5352               cs_lnum_t c_id = b_face_cells[f_id];
5353 
5354               for (cs_lnum_t i = 0; i < 3; i++) {
5355 
5356                 /*
5357                    Remark: for the cell \f$ \celli \f$ we remove
5358                     \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
5359                  */
5360 
5361                 cs_real_t pfac = inc*coefav[f_id][i];
5362 
5363                 for (cs_lnum_t k = 0; k < 3; k++) {
5364                   /* Reconstruction part */
5365                   cs_real_t vecfac =   grad[c_id][k][0] * diipb[f_id][0]
5366                                      + grad[c_id][k][1] * diipb[f_id][1]
5367                                      + grad[c_id][k][2] * diipb[f_id][2];
5368                   pfac += coefbv[f_id][i][k] * vecfac;
5369 
5370                   if (i == k)
5371                     pfac += (coefbv[f_id][i][k] - 1.0) * pvar[c_id][k];
5372                   else
5373                     pfac += coefbv[f_id][i][k] * pvar[c_id][k];
5374                 }
5375 
5376                 for (cs_lnum_t j = 0; j < 3; j++)
5377                   rhs[c_id][i][j] += pfac * b_f_face_normal[f_id][j];
5378 
5379               }
5380             }
5381 
5382           } /* loop on faces */
5383 
5384         } /* loop on threads */
5385 
5386       } /* loop on thread groups */
5387 
5388       /* Increment of the gradient */
5389 
5390 #     pragma omp parallel for
5391       for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
5392         cs_real_t dvol;
5393         /* Is the cell disabled (for solid or porous)? Not the case if coupled */
5394         if (has_dc * c_disable_flag[has_dc * c_id] == 0)
5395           dvol = 1. / cell_f_vol[c_id];
5396         else
5397           dvol = 0.;
5398 
5399         for (cs_lnum_t i = 0; i < 3; i++) {
5400           for (cs_lnum_t j = 0; j < 3; j++)
5401             rhs[c_id][i][j] *= dvol;
5402         }
5403 
5404         for (cs_lnum_t i = 0; i < 3; i++) {
5405           for (cs_lnum_t j = 0; j < 3; j++) {
5406             for (cs_lnum_t k = 0; k < 3; k++)
5407               grad[c_id][i][j] += rhs[c_id][i][k] * cocg[c_id][k][j];
5408           }
5409         }
5410       }
5411 
5412       /* Periodicity and parallelism treatment */
5413 
5414       if (m->halo != NULL) {
5415         cs_halo_sync_var_strided(m->halo, halo_type, (cs_real_t *)grad, 9);
5416         if (cs_glob_mesh->have_rotation_perio)
5417           cs_halo_perio_sync_var_tens(m->halo, halo_type, (cs_real_t *)grad);
5418       }
5419 
5420       /* Convergence test (L2 norm) */
5421 
5422       l2_residual = _l2_norm_1(9*n_cells, (cs_real_t *)rhs);
5423 
5424     } /* End of the iterative process */
5425 
5426     /* Printing */
5427 
5428     if (l2_residual < epsrgp*l2_norm) {
5429       if (verbosity >= 2) {
5430         bft_printf
5431           (_(" %s: isweep = %d, normed residual: %e, norm: %e, var: %s\n"),
5432            __func__, isweep, l2_residual/l2_norm, l2_norm, var_name);
5433       }
5434     }
5435     else if (isweep >= n_r_sweeps) {
5436       if (verbosity >= 0) {
5437         bft_printf(_(" Warning:\n"
5438                      " --------\n"
5439                      "   %s; variable: %s; sweeps: %d\n"
5440                      "   %*s  normed residual: %11.4e; norm: %11.4e\n"),
5441                    __func__, var_name, isweep,
5442                    (int)(strlen(__func__)), " ", l2_residual/l2_norm, l2_norm);
5443       }
5444     }
5445 
5446   }
5447 
5448   if (gradient_info != NULL)
5449     _gradient_info_update_iter(gradient_info, isweep);
5450 
5451   BFT_FREE(rhs);
5452 }
5453 
5454 /*----------------------------------------------------------------------------
5455  * Compute the gradient of a vector with an iterative technique in order to
5456  * handle non-orthoganalities (n_r_sweeps > 1).
5457  *
5458  * We do not take into account any volumic force here.
5459  *
5460  * parameters:
5461  *   m              <-- pointer to associated mesh structure
5462  *   fvq            <-> pointer to associated finite volume quantities
5463  *   var_name       <-- variable's name
5464  *   gradient_info  <-- pointer to performance logging structure, or NULL
5465  *   halo_type      <-- halo type (extended or not)
5466  *   inc            <-- if 0, solve on increment; 1 otherwise
5467  *   n_r_sweeps     --> >1: with reconstruction
5468  *   verbosity      --> verbosity level
5469  *   epsrgp         --> precision for iterative gradient calculation
5470  *   coefav         <-- B.C. coefficients for boundary face normals
5471  *   coefbv         <-- B.C. coefficients for boundary face normals
5472  *   pvar           <-- variable
5473  *   grad          <-> gradient of pvar (du_i/dx_j : grad[][i][j])
5474  *----------------------------------------------------------------------------*/
5475 
5476 static void
_iterative_tensor_gradient(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const char * var_name,cs_gradient_info_t * gradient_info,cs_halo_type_t halo_type,int inc,int n_r_sweeps,int verbosity,cs_real_t epsrgp,const cs_real_6_t * restrict coefat,const cs_real_66_t * restrict coefbt,const cs_real_6_t * restrict pvar,cs_real_63_t * restrict grad)5477 _iterative_tensor_gradient(const cs_mesh_t              *m,
5478                            const cs_mesh_quantities_t   *fvq,
5479                            const char                   *var_name,
5480                            cs_gradient_info_t           *gradient_info,
5481                            cs_halo_type_t                halo_type,
5482                            int                           inc,
5483                            int                           n_r_sweeps,
5484                            int                           verbosity,
5485                            cs_real_t                     epsrgp,
5486                            const cs_real_6_t   *restrict coefat,
5487                            const cs_real_66_t  *restrict coefbt,
5488                            const cs_real_6_t   *restrict pvar,
5489                            cs_real_63_t        *restrict grad)
5490 {
5491   int isweep = 0;
5492 
5493   cs_real_63_t *rhs;
5494 
5495   const cs_lnum_t n_cells = m->n_cells;
5496   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
5497   const int n_i_groups = m->i_face_numbering->n_groups;
5498   const int n_i_threads = m->i_face_numbering->n_threads;
5499   const int n_b_groups = m->b_face_numbering->n_groups;
5500   const int n_b_threads = m->b_face_numbering->n_threads;
5501   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
5502   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
5503 
5504   const cs_lnum_2_t *restrict i_face_cells
5505     = (const cs_lnum_2_t *restrict)m->i_face_cells;
5506   const cs_lnum_t *restrict b_face_cells
5507     = (const cs_lnum_t *restrict)m->b_face_cells;
5508 
5509   const int *restrict c_disable_flag = fvq->c_disable_flag;
5510   cs_lnum_t has_dc = fvq->has_disable_flag; /* Has cells disabled? */
5511 
5512   const cs_real_t *restrict weight = fvq->weight;
5513   const cs_real_t *restrict cell_f_vol = fvq->cell_f_vol;
5514   if (cs_glob_porous_model == 1 || cs_glob_porous_model == 2)
5515     cell_f_vol = fvq->cell_vol;
5516   const cs_real_3_t *restrict i_f_face_normal
5517     = (const cs_real_3_t *restrict)fvq->i_f_face_normal;
5518   const cs_real_3_t *restrict b_f_face_normal
5519     = (const cs_real_3_t *restrict)fvq->b_f_face_normal;
5520   const cs_real_3_t *restrict diipb
5521     = (const cs_real_3_t *restrict)fvq->diipb;
5522   const cs_real_3_t *restrict dofij
5523     = (const cs_real_3_t *restrict)fvq->dofij;
5524 
5525   cs_gradient_quantities_t  *gq = _gradient_quantities_get(0);
5526 
5527   cs_real_33_t *restrict cocg = gq->cocg_it;
5528   if (cocg == NULL)
5529     cocg = _compute_cell_cocg_it(m, fvq, NULL, gq);
5530 
5531   BFT_MALLOC(rhs, n_cells_ext, cs_real_63_t);
5532 
5533   /* Gradient reconstruction to handle non-orthogonal meshes */
5534   /*---------------------------------------------------------*/
5535 
5536   /* L2 norm */
5537 
5538   cs_real_t l2_norm = _l2_norm_1(18*n_cells, (cs_real_t *)grad);
5539   cs_real_t l2_residual = l2_norm ;
5540 
5541   if (l2_norm > cs_math_epzero) {
5542 
5543     /* Iterative process */
5544     /*-------------------*/
5545 
5546     for (isweep = 1; isweep < n_r_sweeps && l2_residual > epsrgp*l2_norm; isweep++) {
5547 
5548       /* Computation of the Right Hand Side*/
5549 
5550 #     pragma omp parallel for
5551       for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
5552         for (cs_lnum_t i = 0; i < 6; i++) {
5553           for (cs_lnum_t j = 0; j < 3; j++)
5554             rhs[c_id][i][j] = - cell_f_vol[c_id] * grad[c_id][i][j];
5555         }
5556       }
5557 
5558       /* Interior face treatment */
5559 
5560       for (int g_id = 0; g_id < n_i_groups; g_id++) {
5561 
5562 #       pragma omp parallel for
5563         for (int t_id = 0; t_id < n_i_threads; t_id++) {
5564 
5565           for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
5566                f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
5567                f_id++) {
5568 
5569             cs_lnum_t c_id1 = i_face_cells[f_id][0];
5570             cs_lnum_t c_id2 = i_face_cells[f_id][1];
5571             cs_real_t pond = weight[f_id];
5572 
5573             /*
5574                Remark: \f$ \varia_\face = \alpha_\ij \varia_\celli
5575                                         + (1-\alpha_\ij) \varia_\cellj\f$
5576                        but for the cell \f$ \celli \f$ we remove
5577                        \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
5578                        and for the cell \f$ \cellj \f$ we remove
5579                        \f$ \varia_\cellj \sum_\face \vect{S}_\face = \vect{0} \f$
5580             */
5581 
5582             for (cs_lnum_t i = 0; i < 6; i++) {
5583 
5584               /* Reconstruction part */
5585               cs_real_t
5586                 pfaci = 0.5 * (    (grad[c_id1][i][0] + grad[c_id2][i][0])
5587                                  * dofij[f_id][0]
5588                                +   (grad[c_id1][i][1] + grad[c_id2][i][1])
5589                                  * dofij[f_id][1]
5590                                +   (grad[c_id1][i][2] + grad[c_id2][i][2])
5591                                  * dofij[f_id][2]);
5592               cs_real_t pfacj = pfaci;
5593 
5594               pfaci += (1.0-pond) * (pvar[c_id2][i] - pvar[c_id1][i]);
5595               pfacj -=       pond * (pvar[c_id2][i] - pvar[c_id1][i]);
5596               for (cs_lnum_t j = 0; j < 3; j++) {
5597                 rhs[c_id1][i][j] += pfaci * i_f_face_normal[f_id][j];
5598                 rhs[c_id2][i][j] -= pfacj * i_f_face_normal[f_id][j];
5599               }
5600             }
5601 
5602           } /* loop on faces */
5603 
5604         } /* loop on threads */
5605 
5606       } /* loop on thread groups */
5607 
5608       /* Boundary face treatment */
5609 
5610       for (int g_id = 0; g_id < n_b_groups; g_id++) {
5611 
5612 #       pragma omp parallel for
5613         for (int t_id = 0; t_id < n_b_threads; t_id++) {
5614 
5615           for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
5616                f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
5617                f_id++) {
5618 
5619             cs_lnum_t c_id = b_face_cells[f_id];
5620 
5621             for (cs_lnum_t i = 0; i < 6; i++) {
5622 
5623               /*
5624                  Remark: for the cell \f$ \celli \f$ we remove
5625                          \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
5626                */
5627 
5628               cs_real_t pfac = inc*coefat[f_id][i];
5629 
5630               for (cs_lnum_t k = 0; k < 6; k++) {
5631                 /* Reconstruction part */
5632                 cs_real_t vecfac =   grad[c_id][k][0] * diipb[f_id][0]
5633                                    + grad[c_id][k][1] * diipb[f_id][1]
5634                                    + grad[c_id][k][2] * diipb[f_id][2];
5635                 pfac += coefbt[f_id][i][k] * vecfac;
5636 
5637                 if (i == k)
5638                   pfac += (coefbt[f_id][i][k] - 1.0) * pvar[c_id][k];
5639                 else
5640                   pfac += coefbt[f_id][i][k] * pvar[c_id][k];
5641               }
5642 
5643               for (cs_lnum_t j = 0; j < 3; j++)
5644                 rhs[c_id][i][j] += pfac * b_f_face_normal[f_id][j];
5645 
5646             }
5647 
5648           } /* loop on faces */
5649 
5650         } /* loop on threads */
5651 
5652       } /* loop on thread groups */
5653 
5654       /* Increment of the gradient */
5655 
5656 #     pragma omp parallel for
5657       for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
5658         cs_real_t dvol;
5659         /* Is the cell disabled (for solid or porous)? Not the case if coupled */
5660         if (has_dc * c_disable_flag[has_dc * c_id] == 0)
5661           dvol = 1. / cell_f_vol[c_id];
5662         else
5663           dvol = 0.;
5664 
5665         for (cs_lnum_t i = 0; i < 6; i++) {
5666           for (cs_lnum_t j = 0; j < 3; j++)
5667             rhs[c_id][i][j] *= dvol;
5668         }
5669 
5670         for (cs_lnum_t i = 0; i < 6; i++) {
5671           for (cs_lnum_t j = 0; j < 3; j++) {
5672             for (cs_lnum_t k = 0; k < 3; k++)
5673               grad[c_id][i][j] += rhs[c_id][i][k] * cocg[c_id][k][j];
5674           }
5675         }
5676       }
5677 
5678       /* Periodicity and parallelism treatment */
5679 
5680       if (m->halo != NULL) {
5681         cs_halo_sync_var_strided(m->halo, halo_type, (cs_real_t *)grad, 18);
5682         if (cs_glob_mesh->have_rotation_perio)
5683           cs_halo_perio_sync_var_sym_tens_grad(m->halo,
5684                                                halo_type,
5685                                                (cs_real_t *)grad);
5686       }
5687 
5688       /* Convergence test (L2 norm) */
5689 
5690       ///FIXME
5691       l2_residual = _l2_norm_1(18*n_cells, (cs_real_t *)rhs);
5692 
5693     } /* End of the iterative process */
5694 
5695     /* Printing */
5696 
5697     if (l2_residual < epsrgp*l2_norm) {
5698       if (verbosity >= 2) {
5699         bft_printf
5700           (_(" %s: isweep = %d, normed residual: %e, norm: %e, var: %s\n"),
5701            __func__, isweep, l2_residual/l2_norm, l2_norm, var_name);
5702       }
5703     }
5704     else if (isweep >= n_r_sweeps) {
5705       if (verbosity >= 0) {
5706         bft_printf(_(" Warning:\n"
5707                      " --------\n"
5708                      "   %s; variable: %s; sweeps: %d\n"
5709                      "   %*s  normed residual: %11.4e; norm: %11.4e\n"),
5710                    __func__, var_name, isweep,
5711                    (int)(strlen(__func__)), " ", l2_residual/l2_norm, l2_norm);
5712       }
5713     }
5714 
5715   }
5716 
5717   if (gradient_info != NULL)
5718     _gradient_info_update_iter(gradient_info, isweep);
5719 
5720   BFT_FREE(rhs);
5721 }
5722 
5723 /*----------------------------------------------------------------------------
5724  * Complete initialization of cocg for lsq vector and tensor gradient.
5725  *
5726  * parameters:
5727  *   c_id          <-- cell id
5728  *   halo_type     <-- halo type
5729  *   madj          <-- pointer to mesh adjacencies structure
5730  *   fvq           <-- pointer to associated finite volume quantities
5731  *   cocgb         --> cocg
5732  *----------------------------------------------------------------------------*/
5733 
5734 #if defined(__INTEL_COMPILER)
5735 #pragma optimization_level 2 /* Bug with O3 or above with icc 18.0.1 20171018
5736                                 at least on Xeon(R) Gold 6140 ? */
5737 #endif
5738 
5739 static void
_complete_cocg_lsq(cs_lnum_t c_id,const cs_mesh_adjacencies_t * madj,const cs_mesh_quantities_t * fvq,const cs_cocg_t cocg[6],cs_real_t cocgb[restrict3][3])5740 _complete_cocg_lsq(cs_lnum_t                     c_id,
5741                    const cs_mesh_adjacencies_t  *madj,
5742                    const cs_mesh_quantities_t   *fvq,
5743                    const cs_cocg_t               cocg[6],
5744                    cs_real_t                     cocgb[restrict 3][3])
5745 {
5746   /* Short variable accesses */
5747 
5748   const cs_real_3_t *restrict b_face_normal
5749     = (const cs_real_3_t *restrict)fvq->b_face_normal;
5750 
5751   cs_lnum_t s_id, e_id;
5752 
5753   /* initialize cocgb */
5754 
5755   cocgb[0][0] = cocg[0];
5756   cocgb[0][1] = cocg[3];
5757   cocgb[0][2] = cocg[5];
5758   cocgb[1][0] = cocg[3];
5759   cocgb[1][1] = cocg[1];
5760   cocgb[1][2] = cocg[4];
5761   cocgb[2][0] = cocg[5];
5762   cocgb[2][1] = cocg[4];
5763   cocgb[2][2] = cocg[2];
5764 
5765   /* Contribution from boundary faces */
5766 
5767   const cs_lnum_t  *restrict cell_b_faces
5768     = (const cs_lnum_t  *restrict)(madj->cell_b_faces);
5769 
5770   s_id = madj->cell_b_faces_idx[c_id];
5771   e_id = madj->cell_b_faces_idx[c_id+1];
5772 
5773   for (cs_lnum_t i = s_id; i < e_id; i++) {
5774 
5775     cs_lnum_t f_id = cell_b_faces[i];
5776 
5777     cs_real_3_t normal;
5778     /* Normal is vector 0 if the b_face_normal norm is too small */
5779     cs_math_3_normalise(b_face_normal[f_id], normal);
5780 
5781     for (cs_lnum_t ii = 0; ii < 3; ii++) {
5782       for (cs_lnum_t jj = 0; jj < 3; jj++)
5783         cocgb[ii][jj] += normal[ii] * normal[jj];
5784     }
5785 
5786   }
5787 }
5788 
5789 /*----------------------------------------------------------------------------
5790  * Compute cocg and RHS at boundaries for lsq vector gradient.
5791  *
5792  * parameters:
5793  *   c_id             <-- cell id
5794  *   inc              <-- if 0, solve on increment; 1 otherwise
5795  *   madj             <-- pointer to mesh adjacencies structure
5796  *   fvq              <-- pointer to associated finite volume quantities
5797  *   _33_9_idx        <-- symmetric indexes mapping
5798  *   pvar             <-- variable
5799  *   coefav           <-- B.C. coefficients for boundary face normals
5800  *   coefbv           <-- B.C. coefficients for boundary face normals
5801  *   cocg             <-- cocg values
5802  *   rhs              <-- right hand side
5803  *   cocgb_v          --> boundary cocg vector values
5804  *   rhsb_v           --> boundary RHS values
5805  *----------------------------------------------------------------------------*/
5806 
5807 static void
_compute_cocgb_rhsb_lsq_v(cs_lnum_t c_id,const int inc,const cs_mesh_adjacencies_t * madj,const cs_mesh_quantities_t * fvq,cs_lnum_t _33_9_idx[const restrict9][2],const cs_real_3_t * restrict pvar,const cs_real_3_t * restrict coefav,const cs_real_33_t * restrict coefbv,const cs_real_t cocg[3][3],const cs_real_t rhs[3][3],cs_real_t cocgb_v[restrict45],cs_real_t rhsb_v[restrict9])5808 _compute_cocgb_rhsb_lsq_v(cs_lnum_t                     c_id,
5809                           const int                     inc,
5810                           const cs_mesh_adjacencies_t  *madj,
5811                           const cs_mesh_quantities_t   *fvq,
5812                           cs_lnum_t              _33_9_idx[const restrict 9][2],
5813                           const cs_real_3_t            *restrict pvar,
5814                           const cs_real_3_t            *restrict coefav,
5815                           const cs_real_33_t           *restrict coefbv,
5816                           const cs_real_t               cocg[3][3],
5817                           const cs_real_t               rhs[3][3],
5818                           cs_real_t                     cocgb_v[restrict 45],
5819                           cs_real_t                     rhsb_v[restrict 9])
5820 {
5821   /* Short variable accesses */
5822 
5823   const cs_real_3_t *restrict diipb
5824     = (const cs_real_3_t *restrict)fvq->diipb;
5825 
5826   const cs_real_3_t *restrict b_face_normal
5827     = (const cs_real_3_t *restrict)fvq->b_face_normal;
5828   const cs_real_t *restrict b_dist
5829     = (const cs_real_t *restrict)fvq->b_dist;
5830 
5831   cs_lnum_t s_id, e_id;
5832 
5833   /* initialize cocg and rhsfor lsq vector gradient */
5834 
5835   for (int ll = 0; ll < 9; ll++) {
5836 
5837     /* index of row first coefficient */
5838     int ll_9 = ll*(ll+1)/2;
5839 
5840     for (int mm = 0; mm <= ll; mm++) {
5841       /* initialize */
5842       cocgb_v[ll_9+mm] = 0.;
5843 
5844       /* contribution of t[kk][qq] */
5845       int pp = _33_9_idx[ll][0];
5846       int qq = _33_9_idx[ll][1];
5847 
5848       /* derivative with respect to t[rr][ss] */
5849       int rr = _33_9_idx[mm][0];
5850       int ss = _33_9_idx[mm][1];
5851 
5852       /* part from cocg_s (BCs independant) */
5853       if (pp == rr)
5854         cocgb_v[ll_9+mm] = cocg[qq][ss];
5855 
5856       /* part already computed from rhs */
5857       rhsb_v[ll] = rhs[pp][qq];
5858     }
5859   }
5860 
5861   s_id = madj->cell_b_faces_idx[c_id];
5862   e_id = madj->cell_b_faces_idx[c_id+1];
5863 
5864   const cs_lnum_t   *restrict cell_b_faces
5865     = (const cs_lnum_t *restrict)(madj->cell_b_faces);
5866 
5867   for (cs_lnum_t i = s_id; i < e_id; i++) {
5868 
5869     cs_lnum_t f_id = cell_b_faces[i];
5870 
5871     /* build cocgb_v matrix */
5872 
5873     const cs_real_t *restrict iipbf = diipb[f_id];
5874 
5875     cs_real_3_t nb;
5876     /* Normal is vector 0 if the b_face_normal norm is too small */
5877     cs_math_3_normalise(b_face_normal[f_id], nb);
5878 
5879     cs_real_t db = 1./b_dist[f_id];
5880     cs_real_t db2 = db*db;
5881 
5882     /* A and (B - I) */
5883     cs_real_t a[3];
5884     cs_real_t bt[3][3];
5885     for (int ll = 0; ll < 3; ll++) {
5886       for (int pp = 0; pp < 3; pp++)
5887         bt[ll][pp] = coefbv[f_id][ll][pp];
5888     }
5889     for (int ll = 0; ll < 3; ll++) {
5890       a[ll] = inc*coefav[f_id][ll];
5891       bt[ll][ll] -= 1;
5892     }
5893 
5894     /* cocgb */
5895 
5896     for (int ll = 0; ll < 9; ll++) {
5897 
5898       /* contribution of t[kk][qq] */
5899       int kk = _33_9_idx[ll][0];
5900       int qq = _33_9_idx[ll][1];
5901 
5902       int ll_9 = ll*(ll+1)/2;
5903       for (int pp = 0; pp <= ll; pp++) {
5904 
5905         /* derivative with respect to t[rr][ss] */
5906         int rr = _33_9_idx[pp][0];
5907         int ss = _33_9_idx[pp][1];
5908 
5909         /* part from derivative of 1/2*|| B*t*II'/db ||^2 */
5910         cs_real_t cocgv = 0.;
5911         for (int mm = 0; mm < 3; mm++)
5912           cocgv += bt[mm][kk]*bt[mm][rr];
5913         cocgb_v[ll_9+pp] += cocgv*(iipbf[qq]*iipbf[ss])*db2;
5914 
5915         /* part from derivative of -< t*nb , B*t*II'/db > */
5916         cocgb_v[ll_9+pp] -= (  nb[ss]*bt[rr][kk]*iipbf[qq]
5917                              + nb[qq]*bt[kk][rr]*iipbf[ss])
5918                              *db;
5919       }
5920     }
5921 
5922     /* rhsb */
5923 
5924     for (int ll = 0; ll < 9; ll++) {
5925       int pp = _33_9_idx[ll][0];
5926       int qq = _33_9_idx[ll][1];
5927 
5928       /* part from derivative of < (B-1)*t*II'/db , (A+(B-1)*v)/db > */
5929       cs_real_t rhsv = 0.;
5930       for (int rr = 0; rr < 3; rr++) {
5931         rhsv +=   bt[rr][pp]*diipb[f_id][qq]
5932                             *(a[rr]+ bt[rr][0]*pvar[c_id][0]
5933                                    + bt[rr][1]*pvar[c_id][1]
5934                                    + bt[rr][2]*pvar[c_id][2]);
5935       }
5936 
5937       rhsb_v[ll] -= rhsv*db2;
5938     }
5939 
5940   }
5941 
5942   /* Crout factorization of 9x9 symmetric cocg at boundaries */
5943 
5944   _fact_crout_pp(9, cocgb_v);
5945 }
5946 
5947 /*----------------------------------------------------------------------------
5948  * Compute cocg and RHS at boundaries for lsq tensor gradient.
5949  *
5950  * parameters:
5951  *   c_id             <-- cell id
5952  *   inc              <-- if 0, solve on increment; 1 otherwise
5953  *   madj             <-- pointer to mesh adjacencies structure
5954  *   fvq              <-- pointer to associated finite volume quantities
5955  *   _63_18_idx       <-- symmetric indexes mapping
5956  *   pvar             <-- variable
5957  *   coefat           <-- B.C. coefficients for boundary face normals
5958  *   coefbt           <-- B.C. coefficients for boundary face normals
5959  *   cocg             <-- cocg values
5960  *   rhs              <-- right hand side
5961  *   cocgb_t          --> boundary cocg vector values
5962  *   rhsb_t           --> boundary RHS values
5963  *----------------------------------------------------------------------------*/
5964 
5965 static void
_compute_cocgb_rhsb_lsq_t(cs_lnum_t c_id,const int inc,const cs_mesh_adjacencies_t * madj,const cs_mesh_quantities_t * fvq,cs_lnum_t _63_18_idx[const restrict18][2],const cs_real_6_t * restrict pvar,const cs_real_6_t * restrict coefat,const cs_real_66_t * restrict coefbt,const cs_real_t cocg[3][3],const cs_real_t rhs[6][3],cs_real_t cocgb_t[restrict171],cs_real_t rhsb_t[restrict18])5966 _compute_cocgb_rhsb_lsq_t(cs_lnum_t                     c_id,
5967                           const int                     inc,
5968                           const cs_mesh_adjacencies_t  *madj,
5969                           const cs_mesh_quantities_t   *fvq,
5970                           cs_lnum_t            _63_18_idx[const restrict 18][2],
5971                           const cs_real_6_t            *restrict pvar,
5972                           const cs_real_6_t            *restrict coefat,
5973                           const cs_real_66_t           *restrict coefbt,
5974                           const cs_real_t               cocg[3][3],
5975                           const cs_real_t               rhs[6][3],
5976                           cs_real_t                     cocgb_t[restrict 171],
5977                           cs_real_t                     rhsb_t[restrict 18])
5978 {
5979   /* Short variable accesses */
5980 
5981   const cs_real_3_t *restrict diipb
5982     = (const cs_real_3_t *restrict)fvq->diipb;
5983 
5984   const cs_real_3_t *restrict b_face_normal
5985     = (const cs_real_3_t *restrict)fvq->b_face_normal;
5986   const cs_real_t *restrict b_face_surf
5987     = (const cs_real_t *restrict)fvq->b_face_surf;
5988   const cs_real_t *restrict b_dist
5989     = (const cs_real_t *restrict)fvq->b_dist;
5990 
5991   cs_lnum_t s_id, e_id;
5992 
5993   /* initialize cocg and rhs for lsq tensor gradient */
5994 
5995   for (int ll = 0; ll < 18; ll++) {
5996 
5997     /* index of row first coefficient */
5998     int ll_18 = ll*(ll+1)/2;
5999 
6000     for (int mm = 0; mm <= ll; mm++) {
6001       /* initialize */
6002       cocgb_t[ll_18+mm] = 0.;
6003 
6004       /* contribution of t[kk][qq] */
6005       int pp = _63_18_idx[ll][0];
6006       int qq = _63_18_idx[ll][1];
6007 
6008       /* derivative with respect to t[rr][ss] */
6009       int rr = _63_18_idx[mm][0];
6010       int ss = _63_18_idx[mm][1];
6011 
6012       /* part from cocg (BCs independant) */
6013       if (pp == rr)
6014         cocgb_t[ll_18+mm] = cocg[qq][ss];
6015 
6016       /* part already computed from rhs */
6017       rhsb_t[ll] = rhs[pp][qq];
6018     }
6019   }
6020 
6021   s_id = madj->cell_b_faces_idx[c_id];
6022   e_id = madj->cell_b_faces_idx[c_id+1];
6023 
6024   const cs_lnum_t   *restrict cell_b_faces
6025     = (const cs_lnum_t *restrict)(madj->cell_b_faces);
6026 
6027   for (cs_lnum_t i = s_id; i < e_id; i++) {
6028 
6029     cs_lnum_t f_id = cell_b_faces[i];
6030 
6031     /* build cocgb_v matrix */
6032 
6033     cs_real_t udbfs = 1. / b_face_surf[f_id];
6034     const cs_real_t *restrict iipbf = diipb[f_id];
6035 
6036     /* db = I'F / ||I'F|| */
6037     cs_real_t  nb[3];
6038     for (int ii = 0; ii < 3; ii++)
6039       nb[ii] = udbfs * b_face_normal[f_id][ii];
6040 
6041     cs_real_t db = 1./b_dist[f_id];
6042     cs_real_t db2 = db*db;
6043 
6044     /* A and (B - I) */
6045     cs_real_t a[6];
6046     cs_real_t bt[6][6];
6047     for (int ll = 0; ll < 6; ll++) {
6048       for (int pp = 0; pp < 6; pp++)
6049         bt[ll][pp] = coefbt[f_id][ll][pp];
6050     }
6051     for (int ll = 0; ll < 6; ll++) {
6052       a[ll] = inc*coefat[f_id][ll];
6053       bt[ll][ll] -= 1;
6054     }
6055 
6056     /* cocgb */
6057 
6058     for (int ll = 0; ll < 18; ll++) {
6059 
6060       /* contribution of t[kk][qq] */
6061       int kk = _63_18_idx[ll][0];
6062       int qq = _63_18_idx[ll][1];
6063 
6064       int ll_18 = ll*(ll+1)/2;
6065       for (int pp = 0; pp <= ll; pp++) {
6066 
6067         /* derivative with respect to t[rr][ss] */
6068         int rr = _63_18_idx[pp][0];
6069         int ss = _63_18_idx[pp][1];
6070 
6071         /* part from derivative of 1/2*|| B*t*IIp/db ||^2 */
6072         cs_real_t cocgt = 0.;
6073         for (int mm = 0; mm < 6; mm++)
6074           cocgt += bt[mm][kk]*bt[mm][rr];
6075         cocgb_t[ll_18+pp] += cocgt * (iipbf[qq]*iipbf[ss]) * db2;
6076 
6077         /* part from derivative of -< t*nb , B*t*IIp/db > */
6078         cocgb_t[ll_18+pp] -= (  nb[ss]*bt[rr][kk]*iipbf[qq]
6079                               + nb[qq]*bt[kk][rr]*iipbf[ss])
6080                               *db;
6081       }
6082     }
6083 
6084     /* rhsb */
6085 
6086     for (int ll = 0; ll < 18; ll++) {
6087       int pp = _63_18_idx[ll][0];
6088       int qq = _63_18_idx[ll][1];
6089 
6090       /* part from derivative of < (B-1)*t*IIp/db , (A+(B-1)*v)/db > */
6091       cs_real_t rhst = 0.;
6092       for (int rr = 0; rr < 6; rr++) {
6093         cs_real_t tfac = a[rr];
6094         for (int kk = 0; kk < 6; kk++) {
6095           tfac += bt[rr][kk]*pvar[c_id][kk];
6096         }
6097         rhst += bt[rr][pp]*diipb[f_id][qq]*tfac;
6098       }
6099 
6100       rhsb_t[ll] -= rhst*db2;
6101     }
6102 
6103   }
6104 
6105   /* Crout factorization of 18x18 symmetric cocg at boundaries */
6106 
6107   _fact_crout_pp(18, cocgb_t);
6108 }
6109 
6110 /*----------------------------------------------------------------------------
6111  * Compute cell gradient of a vector using least-squares reconstruction for
6112  * non-orthogonal meshes (n_r_sweeps > 1).
6113  *
6114  * parameters:
6115  *   m              <-- pointer to associated mesh structure
6116  *   madj           <-- pointer to mesh adjacencies structure
6117  *   fvq            <-- pointer to associated finite volume quantities
6118  *   cpl            <-- structure associated with internal coupling, or NULL
6119  *   halo_type      <-- halo type (extended or not)
6120  *   inc            <-- if 0, solve on increment; 1 otherwise
6121  *   coefav         <-- B.C. coefficients for boundary face normals
6122  *   coefbv         <-- B.C. coefficients for boundary face normals
6123  *   pvar           <-- variable
6124  *   gradv          --> gradient of pvar (du_i/dx_j : gradv[][i][j])
6125  *----------------------------------------------------------------------------*/
6126 
6127 static void
_lsq_vector_gradient(const cs_mesh_t * m,const cs_mesh_adjacencies_t * madj,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,const cs_halo_type_t halo_type,const int inc,const cs_real_3_t * restrict coefav,const cs_real_33_t * restrict coefbv,const cs_real_3_t * restrict pvar,const cs_real_t * restrict c_weight,cs_real_33_t * restrict gradv)6128 _lsq_vector_gradient(const cs_mesh_t               *m,
6129                      const cs_mesh_adjacencies_t   *madj,
6130                      const cs_mesh_quantities_t    *fvq,
6131                      const cs_internal_coupling_t  *cpl,
6132                      const cs_halo_type_t           halo_type,
6133                      const int                      inc,
6134                      const cs_real_3_t    *restrict coefav,
6135                      const cs_real_33_t   *restrict coefbv,
6136                      const cs_real_3_t    *restrict pvar,
6137                      const cs_real_t      *restrict c_weight,
6138                      cs_real_33_t         *restrict gradv)
6139 {
6140   const cs_lnum_t n_cells = m->n_cells;
6141   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
6142   const int n_i_groups = m->i_face_numbering->n_groups;
6143   const int n_i_threads = m->i_face_numbering->n_threads;
6144   const int n_b_groups = m->b_face_numbering->n_groups;
6145   const int n_b_threads = m->b_face_numbering->n_threads;
6146   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
6147   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
6148 
6149   const cs_lnum_2_t *restrict i_face_cells
6150     = (const cs_lnum_2_t *restrict)m->i_face_cells;
6151   const cs_lnum_t *restrict b_face_cells
6152     = (const cs_lnum_t *restrict)m->b_face_cells;
6153   const cs_lnum_t *restrict cell_cells_idx
6154     = (const cs_lnum_t *restrict)m->cell_cells_idx;
6155   const cs_lnum_t *restrict cell_cells_lst
6156     = (const cs_lnum_t *restrict)m->cell_cells_lst;
6157 
6158   const cs_real_3_t *restrict cell_cen
6159     = (const cs_real_3_t *restrict)fvq->cell_cen;
6160   const cs_real_t *restrict weight = fvq->weight;
6161   const cs_real_t *restrict b_dist = fvq->b_dist;
6162   const cs_real_3_t *restrict b_face_normal
6163     = (const cs_real_3_t *restrict)fvq->b_face_normal;
6164 
6165   cs_cocg_6_t  *restrict cocgb_s = NULL;
6166   cs_cocg_6_t *restrict cocg = NULL;
6167   _get_cell_cocg_lsq(m, halo_type, false, fvq, cpl, &cocg, &cocgb_s);
6168 
6169   cs_real_33_t *rhs;
6170 
6171   BFT_MALLOC(rhs, n_cells_ext, cs_real_33_t);
6172 
6173   bool  *coupled_faces = (cpl == NULL) ?
6174     NULL : (bool *)cpl->coupled_faces;
6175 
6176   /* Compute Right-Hand Side */
6177   /*-------------------------*/
6178 
6179 # pragma omp parallel for
6180   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
6181     for (cs_lnum_t i = 0; i < 3; i++)
6182       for (cs_lnum_t j = 0; j < 3; j++)
6183         rhs[c_id][i][j] = 0.0;
6184   }
6185 
6186   /* Contribution from interior faces */
6187 
6188   for (int g_id = 0; g_id < n_i_groups; g_id++) {
6189 
6190 #   pragma omp parallel for
6191     for (int t_id = 0; t_id < n_i_threads; t_id++) {
6192 
6193       for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
6194            f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
6195            f_id++) {
6196 
6197         cs_lnum_t c_id1 = i_face_cells[f_id][0];
6198         cs_lnum_t c_id2 = i_face_cells[f_id][1];
6199 
6200         cs_real_t  dc[3], fctb[3];
6201 
6202         for (cs_lnum_t i = 0; i < 3; i++)
6203           dc[i] = cell_cen[c_id2][i] - cell_cen[c_id1][i];
6204 
6205         cs_real_t ddc = 1./(dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
6206 
6207         if (c_weight != NULL) {
6208           cs_real_t pond = weight[f_id];
6209           cs_real_t denom = 1. / (  pond       *c_weight[c_id1]
6210                                   + (1. - pond)*c_weight[c_id2]);
6211 
6212           for (cs_lnum_t i = 0; i < 3; i++) {
6213             cs_real_t pfac = (pvar[c_id2][i] - pvar[c_id1][i]) * ddc;
6214 
6215             for (cs_lnum_t j = 0; j < 3; j++) {
6216               fctb[j] = dc[j] * pfac;
6217               rhs[c_id1][i][j] += c_weight[c_id2] * denom * fctb[j];
6218               rhs[c_id2][i][j] += c_weight[c_id1] * denom * fctb[j];
6219             }
6220           }
6221         }
6222         else {
6223           for (cs_lnum_t i = 0; i < 3; i++) {
6224             cs_real_t pfac = (pvar[c_id2][i] - pvar[c_id1][i]) * ddc;
6225 
6226             for (cs_lnum_t j = 0; j < 3; j++) {
6227               fctb[j] = dc[j] * pfac;
6228               rhs[c_id1][i][j] += fctb[j];
6229               rhs[c_id2][i][j] += fctb[j];
6230             }
6231           }
6232         }
6233 
6234       } /* loop on faces */
6235 
6236     } /* loop on threads */
6237 
6238   } /* loop on thread groups */
6239 
6240   /* Contribution from extended neighborhood */
6241 
6242   if (halo_type == CS_HALO_EXTENDED) {
6243 
6244 #   pragma omp parallel for
6245     for (cs_lnum_t c_id1 = 0; c_id1 < n_cells; c_id1++) {
6246       for (cs_lnum_t cidx = cell_cells_idx[c_id1];
6247            cidx < cell_cells_idx[c_id1+1];
6248            cidx++) {
6249 
6250         cs_lnum_t c_id2 = cell_cells_lst[cidx];
6251 
6252         cs_real_t dc[3];
6253 
6254         for (cs_lnum_t i = 0; i < 3; i++)
6255           dc[i] = cell_cen[c_id2][i] - cell_cen[c_id1][i];
6256 
6257         cs_real_t ddc = 1./(dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
6258 
6259         for (cs_lnum_t i = 0; i < 3; i++) {
6260 
6261           cs_real_t pfac = (pvar[c_id2][i] - pvar[c_id1][i]) * ddc;
6262 
6263           for (cs_lnum_t j = 0; j < 3; j++) {
6264             rhs[c_id1][i][j] += dc[j] * pfac;
6265           }
6266         }
6267       }
6268     }
6269 
6270   } /* End for extended neighborhood */
6271 
6272   /* Contribution from coupled faces */
6273 
6274   if (cpl != NULL)
6275     cs_internal_coupling_lsq_vector_gradient
6276       (cpl,
6277        c_weight,
6278        1, /* w_stride */
6279        pvar,
6280        rhs);
6281 
6282   /* Contribution from boundary faces */
6283 
6284   for (int g_id = 0; g_id < n_b_groups; g_id++) {
6285 
6286 #   pragma omp parallel for
6287     for (int t_id = 0; t_id < n_b_threads; t_id++) {
6288 
6289       for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
6290            f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
6291            f_id++) {
6292 
6293         if (cpl == NULL || !coupled_faces[f_id]) {
6294 
6295           cs_lnum_t c_id1 = b_face_cells[f_id];
6296 
6297           cs_real_t n_d_dist[3];
6298           /* Normal is vector 0 if the b_face_normal norm is too small */
6299           cs_math_3_normalise(b_face_normal[f_id], n_d_dist);
6300 
6301           cs_real_t d_b_dist = 1. / b_dist[f_id];
6302 
6303           /* Normal divided by b_dist */
6304           for (cs_lnum_t i = 0; i < 3; i++)
6305             n_d_dist[i] *= d_b_dist;
6306 
6307           for (cs_lnum_t i = 0; i < 3; i++) {
6308             cs_real_t pfac = (coefav[f_id][i]*inc
6309                  + ( coefbv[f_id][0][i] * pvar[c_id1][0]
6310                    + coefbv[f_id][1][i] * pvar[c_id1][1]
6311                    + coefbv[f_id][2][i] * pvar[c_id1][2]
6312                    -                      pvar[c_id1][i]));
6313 
6314             for (cs_lnum_t j = 0; j < 3; j++)
6315               rhs[c_id1][i][j] += n_d_dist[j] * pfac;
6316           }
6317         }
6318 
6319       } /* loop on faces */
6320 
6321     } /* loop on threads */
6322 
6323   } /* loop on thread groups */
6324 
6325   /* Compute gradient */
6326   /*------------------*/
6327 
6328   for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
6329     for (cs_lnum_t i = 0; i < 3; i++) {
6330       gradv[c_id][i][0] =   rhs[c_id][i][0] * cocg[c_id][0]
6331                           + rhs[c_id][i][1] * cocg[c_id][3]
6332                           + rhs[c_id][i][2] * cocg[c_id][5];
6333 
6334       gradv[c_id][i][1] =   rhs[c_id][i][0] * cocg[c_id][3]
6335                           + rhs[c_id][i][1] * cocg[c_id][1]
6336                           + rhs[c_id][i][2] * cocg[c_id][4];
6337 
6338       gradv[c_id][i][2] =   rhs[c_id][i][0] * cocg[c_id][5]
6339                           + rhs[c_id][i][1] * cocg[c_id][4]
6340                           + rhs[c_id][i][2] * cocg[c_id][2];
6341     }
6342   }
6343 
6344   /* Compute gradient on boundary cells */
6345   /*------------------------------------*/
6346 
6347   #pragma omp parallel
6348   {
6349     cs_lnum_t t_s_id, t_e_id;
6350     cs_parall_thread_range(m->n_b_cells, sizeof(cs_real_t), &t_s_id, &t_e_id);
6351 
6352     /* Build indices bijection between [1-9] and [1-3]*[1-3] */
6353 
6354     cs_lnum_t _33_9_idx[9][2];
6355     int nn = 0;
6356     for (int ll = 0; ll < 3; ll++) {
6357       for (int mm = 0; mm < 3; mm++) {
6358         _33_9_idx[nn][0] = ll;
6359         _33_9_idx[nn][1] = mm;
6360         nn++;
6361       }
6362     }
6363 
6364     /* Loop on boundary cells */
6365 
6366     for (cs_lnum_t b_c_id = t_s_id; b_c_id < t_e_id; b_c_id++) {
6367 
6368       cs_lnum_t c_id = m->b_cells[b_c_id];
6369 
6370       cs_real_t cocgb[3][3], cocgb_v[45], rhsb_v[9], x[9];
6371 
6372       _complete_cocg_lsq(c_id, madj, fvq, cocgb_s[b_c_id], cocgb);
6373 
6374       _compute_cocgb_rhsb_lsq_v
6375         (c_id,
6376          inc,
6377          madj,
6378          fvq,
6379          _33_9_idx,
6380          (const cs_real_3_t *)pvar,
6381          (const cs_real_3_t *)coefav,
6382          (const cs_real_33_t *)coefbv,
6383          (const cs_real_3_t *)cocgb,
6384          (const cs_real_3_t *)rhs[c_id],
6385          cocgb_v,
6386          rhsb_v);
6387 
6388       _fw_and_bw_ldtl_pp(cocgb_v,
6389                          9,
6390                          x,
6391                          rhsb_v);
6392 
6393       for (int kk = 0; kk < 9; kk++) {
6394         int ii = _33_9_idx[kk][0];
6395         int jj = _33_9_idx[kk][1];
6396         gradv[c_id][ii][jj] = x[kk];
6397       }
6398 
6399     }
6400 
6401   }
6402 
6403   /* Periodicity and parallelism treatment */
6404 
6405   if (m->halo != NULL) {
6406     cs_halo_sync_var_strided(m->halo, halo_type, (cs_real_t *)gradv, 9);
6407     if (cs_glob_mesh->have_rotation_perio)
6408       cs_halo_perio_sync_var_tens(m->halo, halo_type, (cs_real_t *)gradv);
6409   }
6410 
6411   BFT_FREE(rhs);
6412 }
6413 
6414 /*----------------------------------------------------------------------------
6415  * Compute boundary face vector values using least-squares reconstruction
6416  * for non-orthogonal meshes.
6417  *
6418  * parameters:
6419  *   m              <-- pointer to associated mesh structure
6420  *   ma             <-- pointer to mesh adjacencies structure
6421  *   fvq            <-- pointer to associated finite volume quantities
6422  *   cpl            <-- structure associated with internal coupling, or NULL
6423  *   halo_type      <-- halo type (extended or not)
6424  *   inc            <-- if 0, solve on increment; 1 otherwise
6425  *   coefav         <-- B.C. coefficients for boundary face normals
6426  *   coefbv         <-- B.C. coefficients for boundary face normals
6427  *   c_var          <-- cell variable
6428  *   c_weight       <-- weighted gradient coefficient variable,
6429  *                      or NULL
6430  *   b_f_var        --> boundary face value.
6431  *----------------------------------------------------------------------------*/
6432 
6433 static void
_lsq_vector_b_face_val(const cs_mesh_t * m,const cs_mesh_adjacencies_t * ma,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,const cs_halo_type_t halo_type,const int inc,const cs_real_t bc_coeff_a[][3],const cs_real_t bc_coeff_b[][3][3],const cs_real_t c_var[][3],const cs_real_t c_weight[],cs_real_t b_f_var[restrict][3])6434 _lsq_vector_b_face_val(const cs_mesh_t               *m,
6435                        const cs_mesh_adjacencies_t   *ma,
6436                        const cs_mesh_quantities_t    *fvq,
6437                        const cs_internal_coupling_t  *cpl,
6438                        const cs_halo_type_t           halo_type,
6439                        const int                      inc,
6440                        const cs_real_t                bc_coeff_a[][3],
6441                        const cs_real_t                bc_coeff_b[][3][3],
6442                        const cs_real_t                c_var[][3],
6443                        const cs_real_t                c_weight[],
6444                        cs_real_t                      b_f_var[restrict][3])
6445 {
6446   const cs_lnum_t n_b_cells = m->n_b_cells;
6447 
6448   const cs_lnum_t *restrict cell_b_faces_idx
6449     = (const cs_lnum_t *restrict) ma->cell_b_faces_idx;
6450   const cs_lnum_t *restrict cell_b_faces
6451     = (const cs_lnum_t *restrict) ma->cell_b_faces;
6452 
6453   const cs_real_3_t *restrict diipb
6454     = (const cs_real_3_t *restrict)fvq->diipb;
6455 
6456   bool  *coupled_faces = (cpl == NULL) ?
6457     NULL : (bool *)cpl->coupled_faces;
6458 
6459   cs_real_3_t  *_bc_coeff_a = NULL;
6460 
6461   if (inc == 0) {
6462     BFT_MALLOC(_bc_coeff_a, m->n_b_faces, cs_real_3_t);
6463     for (cs_lnum_t i = 0; i < m->n_b_faces; i++) {
6464       for (cs_lnum_t j = 0; j < 3; j++)
6465         _bc_coeff_a[i][j] = 0;
6466     }
6467     bc_coeff_a = (const cs_real_3_t*)_bc_coeff_a;
6468   }
6469 
6470   /* Reconstruct gradients using least squares for non-orthogonal meshes */
6471 
6472 # pragma omp parallel for if (n_b_cells > CS_THR_MIN)
6473   for (cs_lnum_t ci = 0; ci < n_b_cells; ci++) {
6474 
6475     cs_real_t gradc[3][3];
6476     cs_lnum_t c_id = m->b_cells[ci];
6477 
6478     cs_gradient_vector_cell(m,
6479                             fvq,
6480                             c_id,
6481                             halo_type,
6482                             bc_coeff_a,
6483                             bc_coeff_b,
6484                             c_var,
6485                             c_weight,
6486                             gradc);
6487 
6488     /* Compute boundary face values */
6489 
6490     cs_lnum_t s_id = cell_b_faces_idx[c_id];
6491     cs_lnum_t e_id = cell_b_faces_idx[c_id+1];
6492 
6493     for (cs_lnum_t i = s_id; i < e_id; i++) {
6494 
6495       cs_lnum_t f_id = cell_b_faces[i];
6496 
6497       if (cpl == NULL || !coupled_faces[f_id]) {
6498 
6499         const cs_real_t *_c_var = c_var[c_id];
6500         cs_real_t pip[3];
6501         for (cs_lnum_t k = 0; k < 3; k++) {
6502           pip[k] =   _c_var[k]
6503                    + cs_math_3_dot_product(diipb[f_id], gradc[k]);
6504         }
6505         for (cs_lnum_t k = 0; k < 3; k++) {
6506           b_f_var[f_id][k] =   inc * bc_coeff_a[f_id][k]
6507                              + bc_coeff_b[f_id][0][k] * pip[0]
6508                              + bc_coeff_b[f_id][1][k] * pip[1]
6509                              + bc_coeff_b[f_id][2][k] * pip[2];
6510         }
6511 
6512       }
6513     }
6514 
6515   }
6516 
6517   BFT_FREE(_bc_coeff_a);
6518 }
6519 
6520 /*----------------------------------------------------------------------------
6521  * Compute gradient using vertex-based face values for scalar gradient
6522  * reconstruction.
6523  *
6524  * parameters:
6525  *   m              <-- pointer to associated mesh structure
6526  *   fvq            <-- pointer to associated finite volume quantities
6527  *   cpl            <-- structure associated with internal coupling, or NULL
6528  *   inc            <-- if 0, solve on increment; 1 otherwise
6529  *   bc_coeff_a     <-- B.C. coefficients for boundary face normals
6530  *   bc_coeff_b     <-- B.C. coefficients for boundary face normals
6531  *   c_var          <-- variable
6532  *   c_weight       <-- weighted gradient coefficient variable
6533  *   grad           --> gradient of c_var (du_i/dx_j : grad[][i][j])
6534  *----------------------------------------------------------------------------*/
6535 
6536 static void
_fv_vtx_based_vector_gradient(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,const cs_internal_coupling_t * cpl,cs_real_t inc,const cs_real_t bc_coeff_a[][3],const cs_real_t bc_coeff_b[][3][3],const cs_real_t c_var[][3],const cs_real_t c_weight[],cs_real_t grad[restrict][3][3])6537 _fv_vtx_based_vector_gradient(const cs_mesh_t               *m,
6538                               const cs_mesh_quantities_t    *fvq,
6539                               const cs_internal_coupling_t  *cpl,
6540                               cs_real_t                      inc,
6541                               const cs_real_t                bc_coeff_a[][3],
6542                               const cs_real_t                bc_coeff_b[][3][3],
6543                               const cs_real_t                c_var[][3],
6544                               const cs_real_t                c_weight[],
6545                               cs_real_t                      grad[restrict][3][3])
6546 {
6547   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
6548   const cs_lnum_t n_cells = m->n_cells;
6549   const int n_i_groups = m->i_face_numbering->n_groups;
6550   const int n_i_threads = m->i_face_numbering->n_threads;
6551   const int n_b_groups = m->b_face_numbering->n_groups;
6552   const int n_b_threads = m->b_face_numbering->n_threads;
6553   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
6554   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
6555 
6556   const cs_mesh_adjacencies_t *ma = cs_glob_mesh_adjacencies;
6557 
6558   const cs_lnum_2_t *restrict i_face_cells
6559     = (const cs_lnum_2_t *restrict)m->i_face_cells;
6560   const cs_lnum_t *restrict b_face_cells
6561     = (const cs_lnum_t *restrict)m->b_face_cells;
6562 
6563   const int *restrict c_disable_flag = fvq->c_disable_flag;
6564   cs_lnum_t has_dc = fvq->has_disable_flag; /* Has cells disabled? */
6565 
6566   const cs_real_t *restrict cell_f_vol = fvq->cell_f_vol;
6567   if (cs_glob_porous_model == 1 || cs_glob_porous_model == 2)
6568     cell_f_vol = fvq->cell_vol;
6569   const cs_real_3_t *restrict i_f_face_normal
6570     = (const cs_real_3_t *restrict)fvq->i_f_face_normal;
6571   const cs_real_3_t *restrict b_f_face_normal
6572     = (const cs_real_3_t *restrict)fvq->b_f_face_normal;
6573 
6574   bool  *coupled_faces = (cpl == NULL) ?
6575     NULL : (bool *)cpl->coupled_faces;
6576 
6577   /* Initialize gradient
6578      ------------------- */
6579 
6580 # pragma omp parallel for
6581   for (cs_lnum_t cell_id = 0; cell_id < n_cells_ext; cell_id++) {
6582     for (cs_lnum_t i = 0; i < 3; i++) {
6583       for (cs_lnum_t j = 0; j < 3; j++)
6584         grad[cell_id][i][j] = 0.0;
6585     }
6586   }
6587 
6588   /* Pre-compute gradient at boundary using least squares */
6589 
6590   cs_real_3_t *b_f_var;
6591   BFT_MALLOC(b_f_var, m->n_b_faces, cs_real_3_t);
6592 
6593   _lsq_vector_b_face_val(m,
6594                          ma,
6595                          fvq,
6596                          cpl,
6597                          CS_HALO_STANDARD,
6598                          inc,
6599                          bc_coeff_a,
6600                          bc_coeff_b,
6601                          c_var,
6602                          c_weight,
6603                          b_f_var);
6604 
6605   /* Compute vertex-based values
6606      --------------------------- */
6607 
6608   cs_real_3_t *v_var;
6609   BFT_MALLOC(v_var, m->n_vertices, cs_real_3_t);
6610 
6611   cs_cell_to_vertex(CS_CELL_TO_VERTEX_LR,
6612                     0, /* verbosity */
6613                     3, /* var_dim */
6614                     0,
6615                     c_weight,
6616                     (const cs_real_t *)c_var,
6617                     (const cs_real_t *)b_f_var,
6618                     (cs_real_t *)v_var);
6619 
6620   /* Interpolate to face-based values
6621      -------------------------------- */
6622 
6623   cs_real_3_t *i_f_var;
6624   BFT_MALLOC(i_f_var, m->n_i_faces, cs_real_3_t);
6625 
6626   for (int f_t = 0; f_t < 2; f_t++) {
6627 
6628     const cs_lnum_t n_faces = (f_t == 0) ? m->n_i_faces : m->n_b_faces;
6629     const cs_lnum_t *f2v_idx= NULL, *f2v_ids = NULL;
6630     cs_real_3_t *f_var = NULL;
6631 
6632     if (f_t == 0) {
6633       f2v_idx = m->i_face_vtx_idx;
6634       f2v_ids = m->i_face_vtx_lst;
6635       f_var = i_f_var;
6636     }
6637     else {
6638       f2v_idx = m->b_face_vtx_idx;
6639       f2v_ids = m->b_face_vtx_lst;
6640       f_var = b_f_var;
6641     }
6642 
6643 #   pragma omp parallel for if (n_faces > CS_THR_MIN)
6644     for (cs_lnum_t f_id = 0; f_id < n_faces; f_id++) {
6645       cs_lnum_t s_id = f2v_idx[f_id];
6646       cs_lnum_t e_id = f2v_idx[f_id+1];
6647       cs_real_t s[3] = {0, 0, 0};
6648       for (cs_lnum_t i = s_id; i < e_id; i++) {
6649         for (cs_lnum_t k = 0; k < 3; k++)
6650           s[k] += v_var[f2v_ids[i]][k];
6651       }
6652       for (cs_lnum_t k = 0; k < 3; k++)
6653         f_var[f_id][k] = s[k] / (e_id-s_id);
6654     }
6655 
6656   }
6657 
6658   /* Vertex values are not needed after this stage */
6659 
6660   BFT_FREE(v_var);
6661 
6662   /* Contribution from interior faces */
6663 
6664   for (int g_id = 0; g_id < n_i_groups; g_id++) {
6665 
6666 #   pragma omp parallel for
6667     for (int t_id = 0; t_id < n_i_threads; t_id++) {
6668 
6669       for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
6670            f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
6671            f_id++) {
6672 
6673         cs_lnum_t ii = i_face_cells[f_id][0];
6674         cs_lnum_t jj = i_face_cells[f_id][1];
6675 
6676         for (cs_lnum_t k = 0; k < 3; k++) {
6677 
6678           cs_real_t pfaci = i_f_var[f_id][k] - c_var[ii][k];
6679           cs_real_t pfacj = i_f_var[f_id][k] - c_var[jj][k];
6680 
6681           for (cs_lnum_t l = 0; l < 3; l++) {
6682             grad[ii][k][l] += pfaci * i_f_face_normal[f_id][l];
6683             grad[jj][k][l] -= pfacj * i_f_face_normal[f_id][l];
6684           }
6685 
6686         }
6687 
6688       } /* loop on faces */
6689 
6690     } /* loop on threads */
6691 
6692   } /* loop on thread groups */
6693 
6694   /* Contribution from coupled faces */
6695   if (cpl != NULL) {
6696     assert(0); /* not handled yet */
6697   }
6698 
6699   /* Contribution from boundary faces */
6700 
6701   for (int g_id = 0; g_id < n_b_groups; g_id++) {
6702 
6703 #   pragma omp parallel for
6704     for (int t_id = 0; t_id < n_b_threads; t_id++) {
6705 
6706       for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
6707            f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
6708            f_id++) {
6709 
6710         if (cpl == NULL || !coupled_faces[f_id]) {
6711 
6712           cs_lnum_t ii = b_face_cells[f_id];
6713 
6714           /*
6715             Remark: for the cell \f$ \celli \f$ we remove
6716             \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
6717           */
6718 
6719           for (cs_lnum_t k = 0; k < 3; k++) {
6720 
6721             cs_real_t pfac = b_f_var[f_id][k] - c_var[ii][k];
6722 
6723             for (cs_lnum_t l = 0; l < 3; l++)
6724               grad[ii][k][l] += pfac * b_f_face_normal[f_id][l];
6725 
6726           }
6727 
6728         } /* face without internal coupling */
6729 
6730       } /* loop on faces */
6731 
6732     }
6733 
6734   }
6735 
6736   BFT_FREE(i_f_var);
6737   BFT_FREE(b_f_var);
6738 
6739 # pragma omp parallel for
6740   for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
6741     cs_real_t dvol;
6742     /* Is the cell disabled (for solid or porous)? Not the case if coupled */
6743     if (has_dc * c_disable_flag[has_dc * c_id] == 0)
6744       dvol = 1. / cell_f_vol[c_id];
6745     else
6746       dvol = 0.;
6747 
6748     for (cs_lnum_t i = 0; i < 3; i++) {
6749       for (cs_lnum_t j = 0; j < 3; j++)
6750         grad[c_id][i][j] *= dvol;
6751     }
6752   }
6753 
6754   /* Synchronize halos */
6755 
6756   if (m->halo != NULL) {
6757     cs_halo_sync_var_strided(m->halo, CS_HALO_STANDARD, (cs_real_t *)grad, 9);
6758     if (cs_glob_mesh->have_rotation_perio)
6759       cs_halo_perio_sync_var_tens(m->halo,
6760                                   CS_HALO_STANDARD,
6761                                   (cs_real_t *)grad);
6762   }
6763 }
6764 
6765 /*----------------------------------------------------------------------------
6766  * Compute cell gradient of a tensor using least-squares reconstruction for
6767  * non-orthogonal meshes (n_r_sweeps > 1).
6768  *
6769  * parameters:
6770  *   m              <-- pointer to associated mesh structure
6771  *   madj           <-- pointer to mesh adjacencies structure
6772  *   fvq            <-- pointer to associated finite volume quantities
6773  *   halo_type      <-- halo type (extended or not)
6774  *   inc            <-- if 0, solve on increment; 1 otherwise
6775  *   coefat         <-- B.C. coefficients for boundary face normals
6776  *   coefbt         <-- B.C. coefficients for boundary face normals
6777  *   pvar           <-- variable
6778  *   gradt          --> gradient of pvar (du_i/dx_j : gradv[][i][j])
6779  *----------------------------------------------------------------------------*/
6780 
6781 static void
_lsq_tensor_gradient(const cs_mesh_t * m,const cs_mesh_adjacencies_t * madj,const cs_mesh_quantities_t * fvq,const cs_halo_type_t halo_type,const int inc,const cs_real_6_t * restrict coefat,const cs_real_66_t * restrict coefbt,const cs_real_6_t * restrict pvar,const cs_real_t * restrict c_weight,cs_real_63_t * restrict gradt)6782 _lsq_tensor_gradient(const cs_mesh_t              *m,
6783                      const cs_mesh_adjacencies_t  *madj,
6784                      const cs_mesh_quantities_t   *fvq,
6785                      const cs_halo_type_t          halo_type,
6786                      const int                     inc,
6787                      const cs_real_6_t   *restrict coefat,
6788                      const cs_real_66_t  *restrict coefbt,
6789                      const cs_real_6_t   *restrict pvar,
6790                      const cs_real_t     *restrict c_weight,
6791                      cs_real_63_t        *restrict gradt)
6792 {
6793   const cs_lnum_t n_cells = m->n_cells;
6794   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
6795   const int n_i_groups = m->i_face_numbering->n_groups;
6796   const int n_i_threads = m->i_face_numbering->n_threads;
6797   const int n_b_groups = m->b_face_numbering->n_groups;
6798   const int n_b_threads = m->b_face_numbering->n_threads;
6799   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
6800   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
6801 
6802   const cs_lnum_2_t *restrict i_face_cells
6803     = (const cs_lnum_2_t *restrict)m->i_face_cells;
6804   const cs_lnum_t *restrict b_face_cells
6805     = (const cs_lnum_t *restrict)m->b_face_cells;
6806   const cs_lnum_t *restrict cell_cells_idx
6807     = (const cs_lnum_t *restrict)m->cell_cells_idx;
6808   const cs_lnum_t *restrict cell_cells_lst
6809     = (const cs_lnum_t *restrict)m->cell_cells_lst;
6810 
6811   const cs_real_3_t *restrict cell_cen
6812     = (const cs_real_3_t *restrict)fvq->cell_cen;
6813   const cs_real_t *restrict weight = fvq->weight;
6814   const cs_real_t *restrict b_dist = fvq->b_dist;
6815   const cs_real_3_t *restrict b_face_normal
6816     = (const cs_real_3_t *restrict)fvq->b_face_normal;
6817 
6818   cs_cocg_6_t *restrict cocgb_s = NULL;
6819   cs_cocg_6_t *restrict cocg = NULL;
6820   _get_cell_cocg_lsq(m, halo_type, false, fvq, NULL, &cocg, &cocgb_s);
6821 
6822   cs_real_63_t *rhs;
6823 
6824   BFT_MALLOC(rhs, n_cells_ext, cs_real_63_t);
6825 
6826   /* Reinitialize cocg at boundaries, using saved cocgb */
6827 
6828 # pragma omp parallel for
6829   for (cs_lnum_t ii = 0; ii < m->n_b_cells; ii++) {
6830     cs_lnum_t c_id = m->b_cells[ii];
6831     for (cs_lnum_t ll = 0; ll < 6; ll++)
6832       cocg[c_id][ll] = cocgb_s[ii][ll];
6833   }
6834 
6835   /* Compute Right-Hand Side */
6836   /*-------------------------*/
6837 
6838 # pragma omp parallel for
6839   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
6840     for (cs_lnum_t i = 0; i < 6; i++) {
6841       for (cs_lnum_t j = 0; j < 3; j++)
6842         rhs[c_id][i][j] = 0.0;
6843     }
6844   }
6845 
6846   /* Contribution from interior faces */
6847 
6848   for (int g_id = 0; g_id < n_i_groups; g_id++) {
6849 
6850 #   pragma omp parallel for
6851     for (int t_id = 0; t_id < n_i_threads; t_id++) {
6852 
6853       for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
6854            f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
6855            f_id++) {
6856 
6857         cs_lnum_t c_id1 = i_face_cells[f_id][0];
6858         cs_lnum_t c_id2 = i_face_cells[f_id][1];
6859 
6860         cs_real_3_t dc, fctb;
6861         for (cs_lnum_t i = 0; i < 3; i++)
6862           dc[i] = cell_cen[c_id2][i] - cell_cen[c_id1][i];
6863 
6864         cs_real_t ddc = 1./(dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
6865 
6866         if (c_weight != NULL) {
6867           cs_real_t pond = weight[f_id];
6868           cs_real_t denom = 1. / (  pond       *c_weight[c_id1]
6869                                   + (1. - pond)*c_weight[c_id2]);
6870 
6871           for (cs_lnum_t i = 0; i < 6; i++) {
6872             cs_real_t pfac =  (pvar[c_id2][i] - pvar[c_id1][i]) * ddc;
6873 
6874             for (cs_lnum_t j = 0; j < 3; j++) {
6875               fctb[j] = dc[j] * pfac;
6876               rhs[c_id1][i][j] += c_weight[c_id2] * denom * fctb[j];
6877               rhs[c_id2][i][j] += c_weight[c_id1] * denom * fctb[j];
6878             }
6879           }
6880         }
6881         else {
6882           for (cs_lnum_t i = 0; i < 6; i++) {
6883             cs_real_t pfac =  (pvar[c_id2][i] - pvar[c_id1][i]) * ddc;
6884 
6885             for (cs_lnum_t j = 0; j < 3; j++) {
6886               fctb[j] = dc[j] * pfac;
6887               rhs[c_id1][i][j] += fctb[j];
6888               rhs[c_id2][i][j] += fctb[j];
6889             }
6890           }
6891         }
6892 
6893       } /* loop on faces */
6894 
6895     } /* loop on threads */
6896 
6897   } /* loop on thread groups */
6898 
6899   /* Contribution from extended neighborhood */
6900 
6901   if (halo_type == CS_HALO_EXTENDED) {
6902 
6903 #   pragma omp parallel for
6904     for (cs_lnum_t c_id1 = 0; c_id1 < n_cells; c_id1++) {
6905       for (cs_lnum_t cidx = cell_cells_idx[c_id1];
6906            cidx < cell_cells_idx[c_id1+1];
6907            cidx++) {
6908 
6909         cs_lnum_t c_id2 = cell_cells_lst[cidx];
6910 
6911         cs_real_3_t dc;
6912         for (cs_lnum_t i = 0; i < 3; i++)
6913           dc[i] = cell_cen[c_id2][i] - cell_cen[c_id1][i];
6914 
6915         cs_real_t ddc = 1./(dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
6916 
6917         for (cs_lnum_t i = 0; i < 6; i++) {
6918 
6919           cs_real_t pfac = (pvar[c_id2][i] - pvar[c_id1][i]) * ddc;
6920 
6921           for (cs_lnum_t j = 0; j < 3; j++) {
6922             rhs[c_id1][i][j] += dc[j] * pfac;
6923           }
6924         }
6925       }
6926     }
6927 
6928   } /* End for extended neighborhood */
6929 
6930   /* Contribution from boundary faces */
6931 
6932   for (int g_id = 0; g_id < n_b_groups; g_id++) {
6933 
6934 #   pragma omp parallel for
6935     for (int t_id = 0; t_id < n_b_threads; t_id++) {
6936 
6937       for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
6938            f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
6939            f_id++) {
6940 
6941         cs_lnum_t c_id1 = b_face_cells[f_id];
6942 
6943         cs_real_3_t n_d_dist;
6944         /* Normal is vector 0 if the b_face_normal norm is too small */
6945         cs_math_3_normalise(b_face_normal[f_id], n_d_dist);
6946 
6947         cs_real_t d_b_dist = 1. / b_dist[f_id];
6948 
6949         /* Normal divided by b_dist */
6950         for (cs_lnum_t i = 0; i < 3; i++)
6951           n_d_dist[i] *= d_b_dist;
6952 
6953         for (cs_lnum_t i = 0; i < 6; i++) {
6954           cs_real_t pfac = coefat[f_id][i]*inc - pvar[c_id1][i];
6955           for (cs_lnum_t j = 0; j < 6; j++)
6956             pfac += coefbt[f_id][j][i] * pvar[c_id1][j];
6957 
6958           for (cs_lnum_t j = 0; j < 3; j++)
6959             rhs[c_id1][i][j] += pfac * n_d_dist[j];
6960         }
6961 
6962       } /* loop on faces */
6963 
6964     } /* loop on threads */
6965 
6966   } /* loop on thread groups */
6967 
6968   /* Compute gradient */
6969   /*------------------*/
6970 
6971   for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
6972     for (cs_lnum_t i = 0; i < 6; i++) {
6973       gradt[c_id][i][0] =   rhs[c_id][i][0] * cocg[c_id][0]
6974                           + rhs[c_id][i][1] * cocg[c_id][3]
6975                           + rhs[c_id][i][2] * cocg[c_id][5];
6976 
6977       gradt[c_id][i][1] =   rhs[c_id][i][0] * cocg[c_id][3]
6978                           + rhs[c_id][i][1] * cocg[c_id][1]
6979                           + rhs[c_id][i][2] * cocg[c_id][4];
6980 
6981       gradt[c_id][i][2] =   rhs[c_id][i][0] * cocg[c_id][5]
6982                           + rhs[c_id][i][1] * cocg[c_id][4]
6983                           + rhs[c_id][i][2] * cocg[c_id][2];
6984     }
6985   }
6986 
6987   /* Compute gradient on boundary cells */
6988   /*------------------------------------*/
6989 
6990   #pragma omp parallel
6991   {
6992     cs_lnum_t t_s_id, t_e_id;
6993     cs_parall_thread_range(m->n_b_cells, sizeof(cs_real_t), &t_s_id, &t_e_id);
6994 
6995     /* Build indices bijection between [1-18] and [1-6]*[1-3] */
6996 
6997     cs_lnum_t _63_18_idx[18][2];
6998     int nn = 0;
6999     for (int ll = 0; ll < 6; ll++) {
7000       for (int mm = 0; mm < 3; mm++) {
7001         _63_18_idx[nn][0] = ll;
7002         _63_18_idx[nn][1] = mm;
7003         nn++;
7004       }
7005     }
7006 
7007     /* Loop on boundary cells */
7008 
7009     for (cs_lnum_t b_c_id = t_s_id; b_c_id < t_e_id; b_c_id++) {
7010 
7011       cs_lnum_t c_id = m->b_cells[b_c_id];
7012 
7013       cs_real_t cocgb[3][3], cocgb_t[171], rhsb_t[18], x[18];
7014 
7015       _complete_cocg_lsq(c_id, madj, fvq, cocg[c_id], cocgb);
7016 
7017       _compute_cocgb_rhsb_lsq_t
7018         (c_id,
7019          inc,
7020          madj,
7021          fvq,
7022          _63_18_idx,
7023          (const cs_real_6_t *)pvar,
7024          (const cs_real_6_t *)coefat,
7025          (const cs_real_66_t *)coefbt,
7026          (const cs_real_3_t *)cocgb,
7027          (const cs_real_3_t *)rhs[c_id],
7028          cocgb_t,
7029          rhsb_t);
7030 
7031       _fw_and_bw_ldtl_pp(cocgb_t,
7032                          18,
7033                          x,
7034                          rhsb_t);
7035 
7036       for (int kk = 0; kk < 18; kk++) {
7037         int ii = _63_18_idx[kk][0];
7038         int jj = _63_18_idx[kk][1];
7039         gradt[c_id][ii][jj] = x[kk];
7040       }
7041 
7042     }
7043 
7044   }
7045 
7046   /* Periodicity and parallelism treatment */
7047 
7048   if (m->halo != NULL) {
7049     cs_halo_sync_var_strided(m->halo, halo_type, (cs_real_t *)gradt, 18);
7050     if (cs_glob_mesh->have_rotation_perio)
7051       cs_halo_perio_sync_var_tens(m->halo, halo_type, (cs_real_t *)gradt);
7052   }
7053 
7054   BFT_FREE(rhs);
7055 }
7056 
7057 /*----------------------------------------------------------------------------
7058  * Initialize the gradient of a tensor for gradient reconstruction.
7059  *
7060  * A non-reconstructed gradient is computed at this stage.
7061  *
7062  * parameters:
7063  *   m              <-- pointer to associated mesh structure
7064  *   fvq            <-- pointer to associated finite volume quantities
7065  *   halo_type      <-- halo type (extended or not)
7066  *   inc            <-- if 0, solve on increment; 1 otherwise
7067  *   coefat         <-- B.C. coefficients for boundary face normals
7068  *   coefbt         <-- B.C. coefficients for boundary face normals
7069  *   pvar           <-- variable
7070  *   grad          --> gradient of pvar (dts_i/dx_j : grad[][i][j])
7071  *----------------------------------------------------------------------------*/
7072 
7073 static void
_initialize_tensor_gradient(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,cs_halo_type_t halo_type,int inc,const cs_real_6_t * restrict coefat,const cs_real_66_t * restrict coefbt,const cs_real_6_t * restrict pvar,cs_real_63_t * restrict grad)7074 _initialize_tensor_gradient(const cs_mesh_t              *m,
7075                             const cs_mesh_quantities_t   *fvq,
7076                             cs_halo_type_t                halo_type,
7077                             int                           inc,
7078                             const cs_real_6_t   *restrict coefat,
7079                             const cs_real_66_t  *restrict coefbt,
7080                             const cs_real_6_t   *restrict pvar,
7081                             cs_real_63_t        *restrict grad)
7082 {
7083   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
7084   const cs_lnum_t n_cells = m->n_cells;
7085   const int n_i_groups = m->i_face_numbering->n_groups;
7086   const int n_i_threads = m->i_face_numbering->n_threads;
7087   const int n_b_groups = m->b_face_numbering->n_groups;
7088   const int n_b_threads = m->b_face_numbering->n_threads;
7089   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
7090   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
7091 
7092   const cs_lnum_2_t *restrict i_face_cells
7093     = (const cs_lnum_2_t *restrict)m->i_face_cells;
7094   const cs_lnum_t *restrict b_face_cells
7095     = (const cs_lnum_t *restrict)m->b_face_cells;
7096 
7097   const int *restrict c_disable_flag = fvq->c_disable_flag;
7098   cs_lnum_t has_dc = fvq->has_disable_flag; /* Has cells disabled? */
7099 
7100   const cs_real_t *restrict weight = fvq->weight;
7101   const cs_real_t *restrict cell_f_vol = fvq->cell_f_vol;
7102   if (cs_glob_porous_model == 1 || cs_glob_porous_model == 2)
7103     cell_f_vol = fvq->cell_vol;
7104   const cs_real_3_t *restrict i_f_face_normal
7105     = (const cs_real_3_t *restrict)fvq->i_f_face_normal;
7106   const cs_real_3_t *restrict b_f_face_normal
7107     = (const cs_real_3_t *restrict)fvq->b_f_face_normal;
7108 
7109   /* Computation without reconstruction */
7110   /*------------------------------------*/
7111 
7112   /* Initialization */
7113 
7114 # pragma omp parallel for
7115   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
7116     for (cs_lnum_t i = 0; i < 6; i++) {
7117       for (cs_lnum_t j = 0; j < 3; j++)
7118         grad[c_id][i][j] = 0.0;
7119     }
7120   }
7121 
7122   /* Interior faces contribution */
7123 
7124   for (int g_id = 0; g_id < n_i_groups; g_id++) {
7125 
7126 #   pragma omp parallel for
7127     for (int t_id = 0; t_id < n_i_threads; t_id++) {
7128 
7129       for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
7130            f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
7131            f_id++) {
7132 
7133         cs_lnum_t c_id1 = i_face_cells[f_id][0];
7134         cs_lnum_t c_id2 = i_face_cells[f_id][1];
7135 
7136         cs_real_t pond = weight[f_id];
7137 
7138         /*
7139            Remark: \f$ \varia_\face = \alpha_\ij \varia_\celli
7140                                     + (1-\alpha_\ij) \varia_\cellj\f$
7141                    but for the cell \f$ \celli \f$ we remove
7142                    \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
7143                    and for the cell \f$ \cellj \f$ we remove
7144                    \f$ \varia_\cellj \sum_\face \vect{S}_\face = \vect{0} \f$
7145         */
7146         for (cs_lnum_t i = 0; i < 6; i++) {
7147           cs_real_t pfaci = (1.0-pond) * (pvar[c_id2][i] - pvar[c_id1][i]);
7148           cs_real_t pfacj =     - pond * (pvar[c_id2][i] - pvar[c_id1][i]);
7149           for (cs_lnum_t j = 0; j < 3; j++) {
7150             grad[c_id1][i][j] += pfaci * i_f_face_normal[f_id][j];
7151             grad[c_id2][i][j] -= pfacj * i_f_face_normal[f_id][j];
7152           }
7153         }
7154 
7155       } /* End of loop on faces */
7156 
7157     } /* End of loop on threads */
7158 
7159   } /* End of loop on thread groups */
7160 
7161   /* Boundary face treatment */
7162 
7163   for (int g_id = 0; g_id < n_b_groups; g_id++) {
7164 
7165 #   pragma omp parallel for
7166     for (int t_id = 0; t_id < n_b_threads; t_id++) {
7167 
7168       for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
7169            f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
7170            f_id++) {
7171 
7172         cs_lnum_t c_id = b_face_cells[f_id];
7173 
7174         /*
7175            Remark: for the cell \f$ \celli \f$ we remove
7176                    \f$ \varia_\celli \sum_\face \vect{S}_\face = \vect{0} \f$
7177          */
7178         for (cs_lnum_t i = 0; i < 6; i++) {
7179           cs_real_t pfac = inc*coefat[f_id][i];
7180 
7181           for (cs_lnum_t k = 0; k < 6; k++) {
7182             if (i == k)
7183               pfac += (coefbt[f_id][i][k] - 1.0) * pvar[c_id][k];
7184             else
7185               pfac += coefbt[f_id][i][k] * pvar[c_id][k] ;
7186 
7187           }
7188 
7189           for (cs_lnum_t j = 0; j < 3; j++)
7190             grad[c_id][i][j] += pfac * b_f_face_normal[f_id][j];
7191         }
7192 
7193       } /* loop on faces */
7194 
7195     } /* loop on threads */
7196 
7197   } /* loop on thread groups */
7198 
7199 # pragma omp parallel for
7200   for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
7201     cs_real_t dvol;
7202     /* Is the cell disabled (for solid or porous)? Not the case if coupled */
7203     if (has_dc * c_disable_flag[has_dc * c_id] == 0)
7204       dvol = 1. / cell_f_vol[c_id];
7205     else
7206       dvol = 0.;
7207 
7208     for (cs_lnum_t i = 0; i < 6; i++) {
7209       for (cs_lnum_t j = 0; j < 3; j++)
7210         grad[c_id][i][j] *= dvol;
7211     }
7212   }
7213 
7214   /* Periodicity and parallelism treatment */
7215 
7216   if (m->halo != NULL) {
7217     cs_halo_sync_var_strided(m->halo, halo_type, (cs_real_t *)grad, 18);
7218     if (cs_glob_mesh->have_rotation_perio)
7219       cs_halo_perio_sync_var_sym_tens_grad(m->halo,
7220                                            halo_type,
7221                                            (cs_real_t *)grad);
7222   }
7223 }
7224 
7225 /*----------------------------------------------------------------------------*/
7226 /*!
7227  * \brief  Compute cell gradient of scalar field or component of vector or
7228  *         tensor field.
7229  *
7230  * This variant of the \ref cs_gradient_scalar function assumes ghost cell
7231  * values for input arrays (var and optionally c_weight)
7232  * have already been synchronized.
7233  *
7234  * \param[in]       var_name        variable name
7235  * \param[in]       gradient_info   performance logging structure, or NULL
7236  * \param[in]       gradient_type   gradient type
7237  * \param[in]       halo_type       halo type
7238  * \param[in]       inc             if 0, solve on increment; 1 otherwise
7239  * \param[in]       recompute_cocg  should COCG FV quantities be recomputed ?
7240  * \param[in]       n_r_sweeps      if > 1, number of reconstruction sweeps
7241  * \param[in]       hyd_p_flag      flag for hydrostatic pressure
7242  * \param[in]       w_stride        stride for weighting coefficient
7243  * \param[in]       verbosity       verbosity level
7244  * \param[in]       clip_mode       clipping mode
7245  * \param[in]       epsilon         precision for iterative gradient calculation
7246  * \param[in]       clip_coeff      clipping coefficient
7247  * \param[in]       f_ext           exterior force generating
7248  *                                  the hydrostatic pressure
7249  * \param[in]       bc_coeff_a      boundary condition term a
7250  * \param[in]       bc_coeff_b      boundary condition term b
7251  * \param[in]       var             gradient's base variable
7252  * \param[in]       c_weight        weighted gradient coefficient variable,
7253  *                                  or NULL
7254  * \param[in]       cpl             structure associated with internal coupling,
7255  *                                  or NULL
7256  * \param[out]      grad            gradient
7257  */
7258 /*----------------------------------------------------------------------------*/
7259 
7260 static void
_gradient_scalar(const char * var_name,cs_gradient_info_t * gradient_info,cs_gradient_type_t gradient_type,cs_halo_type_t halo_type,int inc,bool recompute_cocg,int n_r_sweeps,int hyd_p_flag,int w_stride,int verbosity,int clip_mode,double epsilon,double clip_coeff,cs_real_t f_ext[][3],const cs_real_t bc_coeff_a[],const cs_real_t bc_coeff_b[],const cs_real_t var[restrict],const cs_real_t c_weight[restrict],const cs_internal_coupling_t * cpl,cs_real_t grad[restrict][3])7261 _gradient_scalar(const char                    *var_name,
7262                  cs_gradient_info_t            *gradient_info,
7263                  cs_gradient_type_t             gradient_type,
7264                  cs_halo_type_t                 halo_type,
7265                  int                            inc,
7266                  bool                           recompute_cocg,
7267                  int                            n_r_sweeps,
7268                  int                            hyd_p_flag,
7269                  int                            w_stride,
7270                  int                            verbosity,
7271                  int                            clip_mode,
7272                  double                         epsilon,
7273                  double                         clip_coeff,
7274                  cs_real_t                      f_ext[][3],
7275                  const cs_real_t                bc_coeff_a[],
7276                  const cs_real_t                bc_coeff_b[],
7277                  const cs_real_t                var[restrict],
7278                  const cs_real_t                c_weight[restrict],
7279                  const cs_internal_coupling_t  *cpl,
7280                  cs_real_t                      grad[restrict][3])
7281 {
7282   const cs_mesh_t  *mesh = cs_glob_mesh;
7283   cs_mesh_quantities_t  *fvq = cs_glob_mesh_quantities;
7284 
7285   cs_lnum_t n_b_faces = mesh->n_b_faces;
7286   cs_lnum_t n_cells_ext = mesh->n_cells_with_ghosts;
7287 
7288   static int last_fvm_count = 0;
7289 
7290   if (n_r_sweeps > 0) {
7291     int prev_fvq_count = last_fvm_count;
7292     last_fvm_count = cs_mesh_quantities_compute_count();
7293     if (last_fvm_count != prev_fvq_count)
7294       recompute_cocg = true;
7295   }
7296 
7297   /* Use Neumann BC's as default if not provided */
7298 
7299   cs_real_t *_bc_coeff_a = NULL;
7300   cs_real_t *_bc_coeff_b = NULL;
7301 
7302   if (bc_coeff_a == NULL) {
7303     BFT_MALLOC(_bc_coeff_a, n_b_faces, cs_real_t);
7304     for (cs_lnum_t i = 0; i < n_b_faces; i++)
7305       _bc_coeff_a[i] = 0;
7306     bc_coeff_a = _bc_coeff_a;
7307   }
7308   if (bc_coeff_b == NULL) {
7309     BFT_MALLOC(_bc_coeff_b, n_b_faces, cs_real_t);
7310     for (cs_lnum_t i = 0; i < n_b_faces; i++)
7311       _bc_coeff_b[i] = 1;
7312     bc_coeff_b = _bc_coeff_b;
7313   }
7314 
7315   /* Allocate work arrays */
7316 
7317   /* Compute gradient */
7318 
7319   switch (gradient_type) {
7320 
7321   case CS_GRADIENT_GREEN_ITER:
7322 
7323     _initialize_scalar_gradient(mesh,
7324                                 fvq,
7325                                 cpl,
7326                                 hyd_p_flag,
7327                                 inc,
7328                                 (const cs_real_3_t *)f_ext,
7329                                 bc_coeff_a,
7330                                 bc_coeff_b,
7331                                 var,
7332                                 c_weight,
7333                                 grad);
7334 
7335     _iterative_scalar_gradient(mesh,
7336                                fvq,
7337                                cpl,
7338                                var_name,
7339                                gradient_info,
7340                                n_r_sweeps,
7341                                hyd_p_flag,
7342                                verbosity,
7343                                inc,
7344                                epsilon,
7345                                (const cs_real_3_t *)f_ext,
7346                                bc_coeff_a,
7347                                bc_coeff_b,
7348                                var,
7349                                c_weight,
7350                                grad);
7351     break;
7352 
7353   case CS_GRADIENT_LSQ:
7354 
7355     if (w_stride == 6 && c_weight != NULL)
7356       _lsq_scalar_gradient_ani(mesh,
7357                                fvq,
7358                                cpl,
7359                                inc,
7360                                bc_coeff_a,
7361                                bc_coeff_b,
7362                                var,
7363                                (const cs_real_6_t *)c_weight,
7364                                grad);
7365     else
7366       _lsq_scalar_gradient(mesh,
7367                            fvq,
7368                            cpl,
7369                            halo_type,
7370                            recompute_cocg,
7371                            hyd_p_flag,
7372                            inc,
7373                            (const cs_real_3_t *)f_ext,
7374                            bc_coeff_a,
7375                            bc_coeff_b,
7376                            var,
7377                            c_weight,
7378                            grad);
7379 
7380     _scalar_gradient_clipping(halo_type,
7381                               clip_mode,
7382                               verbosity,
7383                               clip_coeff,
7384                               var_name,
7385                               var, grad);
7386     break;
7387 
7388   case CS_GRADIENT_GREEN_LSQ:
7389     {
7390       cs_real_3_t  *restrict r_grad;
7391       BFT_MALLOC(r_grad, n_cells_ext, cs_real_3_t);
7392 
7393       if (w_stride == 6 && c_weight != NULL)
7394         _lsq_scalar_gradient_ani(mesh,
7395                                  fvq,
7396                                  cpl,
7397                                  inc,
7398                                  bc_coeff_a,
7399                                  bc_coeff_b,
7400                                  var,
7401                                  (const cs_real_6_t *)c_weight,
7402                                  grad);
7403       else
7404         _lsq_scalar_gradient(mesh,
7405                              fvq,
7406                              cpl,
7407                              halo_type,
7408                              recompute_cocg,
7409                              hyd_p_flag,
7410                              inc,
7411                              (const cs_real_3_t *)f_ext,
7412                              bc_coeff_a,
7413                              bc_coeff_b,
7414                              var,
7415                              c_weight,
7416                              r_grad);
7417 
7418       _scalar_gradient_clipping(halo_type,
7419                                 clip_mode,
7420                                 verbosity,
7421                                 clip_coeff,
7422                                 var_name,
7423                                 var, r_grad);
7424 
7425       _reconstruct_scalar_gradient(mesh,
7426                                    fvq,
7427                                    cpl,
7428                                    hyd_p_flag,
7429                                    inc,
7430                                    (const cs_real_3_t *)f_ext,
7431                                    bc_coeff_a,
7432                                    bc_coeff_b,
7433                                    c_weight,
7434                                    var,
7435                                    r_grad,
7436                                    grad);
7437 
7438       BFT_FREE(r_grad);
7439     }
7440     break;
7441 
7442   case CS_GRADIENT_GREEN_VTX:
7443     _fv_vtx_based_scalar_gradient(mesh,
7444                                   fvq,
7445                                   cpl,
7446                                   hyd_p_flag,
7447                                   inc,
7448                                   (const cs_real_3_t *)f_ext,
7449                                   bc_coeff_a,
7450                                   bc_coeff_b,
7451                                   var,
7452                                   c_weight,
7453                                   grad);
7454     break;
7455 
7456   }
7457 
7458   if (cs_glob_mesh_quantities_flag & CS_BAD_CELLS_REGULARISATION)
7459     cs_bad_cells_regularisation_vector(grad, 0);
7460 
7461   BFT_FREE(_bc_coeff_a);
7462   BFT_FREE(_bc_coeff_b);
7463 }
7464 
7465 /*----------------------------------------------------------------------------*/
7466 /*!
7467  * \brief  Compute cell gradient of vector field.
7468  *
7469  * \param[in]       var_name        variable name
7470  * \param[in]       gradient_info   performance logging structure, or NULL
7471  * \param[in]       gradient_type   gradient type
7472  * \param[in]       halo_type       halo type
7473  * \param[in]       inc             if 0, solve on increment; 1 otherwise
7474  * \param[in]       n_r_sweeps      if > 1, number of reconstruction sweeps
7475  * \param[in]       verbosity       verbosity level
7476  * \param[in]       clip_mode       clipping mode
7477  * \param[in]       epsilon         precision for iterative gradient calculation
7478  * \param[in]       clip_coeff      clipping coefficient
7479  * \param[in]       bc_coeff_a      boundary condition term a
7480  * \param[in]       bc_coeff_b      boundary condition term b
7481  * \param[in]       var             gradient's base variable
7482  * \param[in]       c_weight        weighted gradient coefficient variable,
7483  *                                  or NULL
7484  * \param[in]       cpl             structure associated with internal coupling,
7485  *                                  or NULL
7486  * \param[out]      grad            gradient
7487                                     (\f$ \der{u_i}{x_j} \f$ is grad[][i][j])
7488  */
7489 /*----------------------------------------------------------------------------*/
7490 
7491 static void
_gradient_vector(const char * var_name,cs_gradient_info_t * gradient_info,cs_gradient_type_t gradient_type,cs_halo_type_t halo_type,int inc,int n_r_sweeps,int verbosity,int clip_mode,double epsilon,double clip_coeff,const cs_real_3_t bc_coeff_a[],const cs_real_33_t bc_coeff_b[],const cs_real_3_t * restrict var,const cs_real_t * restrict c_weight,const cs_internal_coupling_t * cpl,cs_real_33_t * restrict grad)7492 _gradient_vector(const char                     *var_name,
7493                  cs_gradient_info_t             *gradient_info,
7494                  cs_gradient_type_t              gradient_type,
7495                  cs_halo_type_t                  halo_type,
7496                  int                             inc,
7497                  int                             n_r_sweeps,
7498                  int                             verbosity,
7499                  int                             clip_mode,
7500                  double                          epsilon,
7501                  double                          clip_coeff,
7502                  const cs_real_3_t               bc_coeff_a[],
7503                  const cs_real_33_t              bc_coeff_b[],
7504                  const cs_real_3_t     *restrict var,
7505                  const cs_real_t       *restrict c_weight,
7506                  const cs_internal_coupling_t   *cpl,
7507                  cs_real_33_t          *restrict grad)
7508 {
7509   const cs_mesh_t  *mesh = cs_glob_mesh;
7510   const cs_mesh_quantities_t *fvq = cs_glob_mesh_quantities;
7511 
7512   const cs_lnum_t n_cells_ext = mesh->n_cells_with_ghosts;
7513   const cs_lnum_t n_b_faces = mesh->n_b_faces;
7514 
7515   /* Use Neumann BC's as default if not provided */
7516 
7517   cs_real_3_t *_bc_coeff_a = NULL;
7518   cs_real_33_t *_bc_coeff_b = NULL;
7519 
7520   if (bc_coeff_a == NULL) {
7521     BFT_MALLOC(_bc_coeff_a, n_b_faces, cs_real_3_t);
7522     for (cs_lnum_t i = 0; i < n_b_faces; i++) {
7523       for (cs_lnum_t j = 0; j < 3; j++)
7524         _bc_coeff_a[i][j] = 0;
7525     }
7526     bc_coeff_a = (const cs_real_3_t *)_bc_coeff_a;
7527   }
7528   if (bc_coeff_b == NULL) {
7529     BFT_MALLOC(_bc_coeff_b, n_b_faces, cs_real_33_t);
7530     for (cs_lnum_t i = 0; i < n_b_faces; i++) {
7531       for (cs_lnum_t j = 0; j < 3; j++) {
7532         for (cs_lnum_t k = 0; k < 3; k++)
7533           _bc_coeff_b[i][j][k] = 0;
7534         _bc_coeff_b[i][j][j] = 1;
7535       }
7536     }
7537     bc_coeff_b = (const cs_real_33_t *)_bc_coeff_b;
7538   }
7539 
7540   /* Compute gradient */
7541 
7542   switch (gradient_type) {
7543 
7544   case CS_GRADIENT_GREEN_ITER:
7545 
7546     _initialize_vector_gradient(mesh,
7547                                 fvq,
7548                                 cpl,
7549                                 halo_type,
7550                                 inc,
7551                                 bc_coeff_a,
7552                                 bc_coeff_b,
7553                                 var,
7554                                 c_weight,
7555                                 grad);
7556 
7557     /* If reconstructions are required */
7558 
7559     if (n_r_sweeps > 1)
7560       _iterative_vector_gradient(mesh,
7561                                  fvq,
7562                                  cpl,
7563                                  var_name,
7564                                  gradient_info,
7565                                  halo_type,
7566                                  inc,
7567                                  n_r_sweeps,
7568                                  verbosity,
7569                                  epsilon,
7570                                  bc_coeff_a,
7571                                  bc_coeff_b,
7572                                  (const cs_real_3_t *)var,
7573                                  c_weight,
7574                                  grad);
7575 
7576     break;
7577 
7578   case CS_GRADIENT_LSQ:
7579 
7580     _lsq_vector_gradient(mesh,
7581                          cs_glob_mesh_adjacencies,
7582                          fvq,
7583                          cpl,
7584                          halo_type,
7585                          inc,
7586                          bc_coeff_a,
7587                          bc_coeff_b,
7588                          (const cs_real_3_t *)var,
7589                          c_weight,
7590                          grad);
7591 
7592     _vector_gradient_clipping(mesh,
7593                               fvq,
7594                               halo_type,
7595                               clip_mode,
7596                               verbosity,
7597                               clip_coeff,
7598                               var_name,
7599                               (const cs_real_3_t *)var,
7600                               grad);
7601 
7602     break;
7603 
7604   case CS_GRADIENT_GREEN_LSQ:
7605 
7606     {
7607       cs_real_33_t  *restrict r_gradv;
7608       BFT_MALLOC(r_gradv, n_cells_ext, cs_real_33_t);
7609 
7610       _lsq_vector_gradient(mesh,
7611                            cs_glob_mesh_adjacencies,
7612                            fvq,
7613                            cpl,
7614                            halo_type,
7615                            inc,
7616                            bc_coeff_a,
7617                            bc_coeff_b,
7618                            (const cs_real_3_t *)var,
7619                            c_weight,
7620                            r_gradv);
7621 
7622       _vector_gradient_clipping(mesh,
7623                                 fvq,
7624                                 halo_type,
7625                                 clip_mode,
7626                                 verbosity,
7627                                 clip_coeff,
7628                                 var_name,
7629                                 (const cs_real_3_t *)var,
7630                                 r_gradv);
7631 
7632       _reconstruct_vector_gradient(mesh,
7633                                    fvq,
7634                                    cpl,
7635                                    halo_type,
7636                                    inc,
7637                                    bc_coeff_a,
7638                                    bc_coeff_b,
7639                                    (const cs_real_3_t *)var,
7640                                    c_weight,
7641                                    r_gradv,
7642                                    grad);
7643 
7644       BFT_FREE(r_gradv);
7645     }
7646     break;
7647 
7648   case CS_GRADIENT_GREEN_VTX:
7649 
7650     _fv_vtx_based_vector_gradient(mesh,
7651                                   fvq,
7652                                   cpl,
7653                                   inc,
7654                                   bc_coeff_a,
7655                                   bc_coeff_b,
7656                                   var,
7657                                   c_weight,
7658                                   grad);
7659 
7660     break;
7661 
7662   }
7663 
7664   if (cs_glob_mesh_quantities_flag & CS_BAD_CELLS_REGULARISATION)
7665     cs_bad_cells_regularisation_tensor((cs_real_9_t *)grad, 0);
7666 
7667   BFT_FREE(_bc_coeff_a);
7668   BFT_FREE(_bc_coeff_b);
7669 }
7670 
7671 /*----------------------------------------------------------------------------*/
7672 /*!
7673  * \brief  Compute the square of the Frobenius norm of a symmetric tensor
7674  *
7675  * \param[in]  t
7676  *
7677  * \return the square of the norm
7678  */
7679 /*----------------------------------------------------------------------------*/
7680 
7681 static inline cs_real_t
_tensor_norm_2(const cs_real_t t[6])7682 _tensor_norm_2(const cs_real_t  t[6])
7683 {
7684   cs_real_t retval =     t[0]*t[0] +   t[1]*t[1] +   t[2]*t[2]
7685                      + 2*t[3]*t[3] + 2*t[4]*t[4] + 2*t[5]*t[5];
7686   return retval;
7687 }
7688 
7689 /*----------------------------------------------------------------------------
7690  * Clip the gradient of a symmetric tensor if necessary. This function deals
7691  * with the standard or extended neighborhood.
7692  *
7693  * parameters:
7694  *   m              <-- pointer to associated mesh structure
7695  *   fvq            <-- pointer to associated finite volume quantities
7696  *   halo_type      <-- halo type (extended or not)
7697  *   clip_mode      <-- type of clipping for the computation of the gradient
7698  *   verbosity      <-- output level
7699  *   climgp         <-- clipping coefficient for the computation of the gradient
7700  *   pvar           <-- variable
7701  *   gradt          <-> gradient of pvar (du_i/dx_j : gradt[][i][j])
7702  *----------------------------------------------------------------------------*/
7703 
7704 static void
_tensor_gradient_clipping(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,cs_halo_type_t halo_type,int clip_mode,int verbosity,cs_real_t climgp,const char * var_name,const cs_real_6_t * restrict pvar,cs_real_63_t * restrict gradt)7705 _tensor_gradient_clipping(const cs_mesh_t              *m,
7706                           const cs_mesh_quantities_t   *fvq,
7707                           cs_halo_type_t                halo_type,
7708                           int                           clip_mode,
7709                           int                           verbosity,
7710                           cs_real_t                     climgp,
7711                           const char                   *var_name,
7712                           const cs_real_6_t   *restrict pvar,
7713                           cs_real_63_t        *restrict gradt)
7714 {
7715   cs_real_t  global_min_factor, global_max_factor;
7716 
7717   cs_gnum_t  n_clip = 0, n_g_clip = 0;
7718   cs_real_t  min_factor = 1;
7719   cs_real_t  max_factor = 0;
7720   cs_real_t  clipp_coef_sq = climgp*climgp;
7721   cs_real_t  *restrict buf = NULL, *restrict clip_factor = NULL;
7722   cs_real_t  *restrict denom = NULL, *restrict denum = NULL;
7723 
7724   const cs_lnum_t n_cells = m->n_cells;
7725   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
7726   const int n_i_groups = m->i_face_numbering->n_groups;
7727   const int n_i_threads = m->i_face_numbering->n_threads;
7728   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
7729 
7730   const cs_lnum_2_t *restrict i_face_cells
7731     = (const cs_lnum_2_t *restrict)m->i_face_cells;
7732   const cs_lnum_t *restrict cell_cells_idx
7733     = (const cs_lnum_t *restrict)m->cell_cells_idx;
7734   const cs_lnum_t *restrict cell_cells_lst
7735     = (const cs_lnum_t *restrict)m->cell_cells_lst;
7736 
7737   const cs_real_3_t *restrict cell_cen
7738     = (const cs_real_3_t *restrict)fvq->cell_cen;
7739 
7740   const cs_halo_t *halo = m->halo;
7741 
7742   if (clip_mode <= CS_GRADIENT_LIMIT_NONE)
7743     return;
7744 
7745   /* The gradient and the variable must be already synchronized */
7746 
7747   /* Allocate and initialize working buffers */
7748 
7749   if (clip_mode == CS_GRADIENT_LIMIT_FACE)
7750     BFT_MALLOC(buf, 3*n_cells_ext, cs_real_t);
7751   else
7752     BFT_MALLOC(buf, 2*n_cells_ext, cs_real_t);
7753 
7754   denum = buf;
7755   denom = buf + n_cells_ext;
7756 
7757   if (clip_mode == CS_GRADIENT_LIMIT_FACE)
7758     clip_factor = buf + 2*n_cells_ext;
7759 
7760   /* Initialization */
7761 
7762 # pragma omp parallel for
7763   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
7764     denum[c_id] = 0;
7765     denom[c_id] = 0;
7766     if (clip_mode == CS_GRADIENT_LIMIT_FACE)
7767       clip_factor[c_id] = (cs_real_t)DBL_MAX;
7768   }
7769 
7770   /* Remark:
7771      denum: holds the maximum l2 norm of the variation of the gradient squared
7772      denom: holds the maximum l2 norm of the variation of the variable squared */
7773 
7774   /* First clipping Algorithm: based on the cell gradient */
7775   /*------------------------------------------------------*/
7776 
7777   if (clip_mode == CS_GRADIENT_LIMIT_CELL) {
7778 
7779     for (int g_id = 0; g_id < n_i_groups; g_id++) {
7780 
7781 #     pragma omp parallel for
7782       for (int t_id = 0; t_id < n_i_threads; t_id++) {
7783 
7784         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
7785              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
7786              f_id++) {
7787 
7788           cs_lnum_t c_id1 = i_face_cells[f_id][0];
7789           cs_lnum_t c_id2 = i_face_cells[f_id][1];
7790 
7791           cs_real_t dist[3];
7792           for (cs_lnum_t i = 0; i < 3; i++)
7793             dist[i] = cell_cen[c_id1][i] - cell_cen[c_id2][i];
7794 
7795           cs_real_t grad_dist1[6], grad_dist2[6], var_dist[6];
7796 
7797           for (cs_lnum_t i = 0; i < 6; i++) {
7798 
7799             grad_dist1[i] =   gradt[c_id1][i][0] * dist[0]
7800                             + gradt[c_id1][i][1] * dist[1]
7801                             + gradt[c_id1][i][2] * dist[2];
7802 
7803             grad_dist2[i] =   gradt[c_id2][i][0] * dist[0]
7804                             + gradt[c_id2][i][1] * dist[1]
7805                             + gradt[c_id2][i][2] * dist[2];
7806 
7807             var_dist[i] = pvar[c_id1][i] - pvar[c_id2][i];
7808 
7809           }
7810 
7811           cs_real_t dist_sq1 = _tensor_norm_2(grad_dist1);
7812           cs_real_t dist_sq2 = _tensor_norm_2(grad_dist2);
7813 
7814           cs_real_t dvar_sq = _tensor_norm_2(var_dist);
7815 
7816           denum[c_id1] = CS_MAX(denum[c_id1], dist_sq1);
7817           denum[c_id2] = CS_MAX(denum[c_id2], dist_sq2);
7818           denom[c_id1] = CS_MAX(denom[c_id1], dvar_sq);
7819           denom[c_id2] = CS_MAX(denom[c_id2], dvar_sq);
7820 
7821         } /* End of loop on faces */
7822 
7823       } /* End of loop on threads */
7824 
7825     } /* End of loop on thread groups */
7826 
7827     /* Complement for extended neighborhood */
7828 
7829     if (cell_cells_idx != NULL && halo_type == CS_HALO_EXTENDED) {
7830 
7831 #     pragma omp parallel for
7832       for (cs_lnum_t c_id1 = 0; c_id1 < n_cells; c_id1++) {
7833         for (cs_lnum_t cidx = cell_cells_idx[c_id1];
7834              cidx < cell_cells_idx[c_id1+1];
7835              cidx++) {
7836 
7837           cs_lnum_t c_id2 = cell_cells_lst[cidx];
7838 
7839           cs_real_t grad_dist1[6], var_dist[6];
7840 
7841           cs_real_t dist[3];
7842           for (cs_lnum_t i = 0; i < 3; i++)
7843             dist[i] = cell_cen[c_id1][i] - cell_cen[c_id2][i];
7844 
7845           for (cs_lnum_t i = 0; i < 6; i++) {
7846 
7847             grad_dist1[i] =   gradt[c_id1][i][0] * dist[0]
7848                             + gradt[c_id1][i][1] * dist[1]
7849                             + gradt[c_id1][i][2] * dist[2];
7850 
7851             var_dist[i] = pvar[c_id1][i] - pvar[c_id2][i];
7852 
7853           }
7854 
7855           cs_real_t dist_sq1 = _tensor_norm_2(grad_dist1);
7856 
7857           cs_real_t dvar_sq = _tensor_norm_2(var_dist);
7858 
7859           denum[c_id1] = CS_MAX(denum[c_id1], dist_sq1);
7860           denom[c_id1] = CS_MAX(denom[c_id1], dvar_sq);
7861 
7862         }
7863       }
7864 
7865     } /* End for extended halo */
7866 
7867   }
7868 
7869   /* Second clipping Algorithm: based on the face gradient */
7870   /*-------------------------------------------------------*/
7871 
7872   else if (clip_mode == CS_GRADIENT_LIMIT_FACE) {
7873 
7874     for (int g_id = 0; g_id < n_i_groups; g_id++) {
7875 
7876 #     pragma omp parallel for
7877       for (int t_id = 0; t_id < n_i_threads; t_id++) {
7878 
7879         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
7880              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
7881              f_id++) {
7882 
7883           cs_lnum_t c_id1 = i_face_cells[f_id][0];
7884           cs_lnum_t c_id2 = i_face_cells[f_id][1];
7885 
7886           cs_real_t dist[3];
7887           for (cs_lnum_t i = 0; i < 3; i++)
7888             dist[i] = cell_cen[c_id1][i] - cell_cen[c_id2][i];
7889 
7890           cs_real_t grad_dist1[6], var_dist[6];
7891 
7892           for (cs_lnum_t i = 0; i < 6; i++) {
7893             grad_dist1[i]
7894               = 0.5 * (  (gradt[c_id1][i][0]+gradt[c_id2][i][0])*dist[0]
7895                        + (gradt[c_id1][i][1]+gradt[c_id2][i][1])*dist[1]
7896                        + (gradt[c_id1][i][2]+gradt[c_id2][i][2])*dist[2]);
7897             var_dist[i] = pvar[c_id1][i] - pvar[c_id2][i];
7898           }
7899 
7900           cs_real_t dist_sq1 = _tensor_norm_2(grad_dist1);
7901           cs_real_t dvar_sq = _tensor_norm_2(var_dist);
7902 
7903           denum[c_id1] = CS_MAX(denum[c_id1], dist_sq1);
7904           denum[c_id2] = CS_MAX(denum[c_id2], dist_sq1);
7905           denom[c_id1] = CS_MAX(denom[c_id1], dvar_sq);
7906           denom[c_id2] = CS_MAX(denom[c_id2], dvar_sq);
7907 
7908         } /* End of loop on threads */
7909 
7910       } /* End of loop on thread groups */
7911 
7912     } /* End of loop on faces */
7913 
7914     /* Complement for extended neighborhood */
7915 
7916     if (cell_cells_idx != NULL && halo_type == CS_HALO_EXTENDED) {
7917 
7918 #     pragma omp parallel for
7919       for (cs_lnum_t c_id1 = 0; c_id1 < n_cells; c_id1++) {
7920         for (cs_lnum_t cidx = cell_cells_idx[c_id1];
7921              cidx < cell_cells_idx[c_id1+1];
7922              cidx++) {
7923 
7924           cs_lnum_t c_id2 = cell_cells_lst[cidx];
7925 
7926           cs_real_t dist[3];
7927           for (cs_lnum_t i = 0; i < 3; i++)
7928             dist[i] = cell_cen[c_id1][i] - cell_cen[c_id2][i];
7929 
7930           cs_real_t grad_dist1[6], var_dist[6];
7931 
7932           for (cs_lnum_t i = 0; i < 6; i++) {
7933             grad_dist1[i]
7934               = 0.5 * (  (gradt[c_id1][i][0]+gradt[c_id2][i][0])*dist[0]
7935                        + (gradt[c_id1][i][1]+gradt[c_id2][i][1])*dist[1]
7936                        + (gradt[c_id1][i][2]+gradt[c_id2][i][2])*dist[2]);
7937             var_dist[i] = pvar[c_id1][i] - pvar[c_id2][i];
7938           }
7939 
7940           cs_real_t dist_sq1 = _tensor_norm_2(grad_dist1);
7941           cs_real_t dvar_sq = _tensor_norm_2(var_dist);
7942 
7943           denum[c_id1] = CS_MAX(denum[c_id1], dist_sq1);
7944           denom[c_id1] = CS_MAX(denom[c_id1], dvar_sq);
7945 
7946         }
7947       }
7948 
7949     } /* End for extended neighborhood */
7950 
7951     /* Synchronize variable */
7952 
7953     if (halo != NULL) {
7954       cs_halo_sync_var(m->halo, halo_type, denom);
7955       cs_halo_sync_var(m->halo, halo_type, denum);
7956     }
7957 
7958   } /* End if clip_mode == CS_GRADIENT_LIMIT_FACE */
7959 
7960   /* Clipping of the gradient if denum/denom > climgp**2 */
7961 
7962   /* First clipping Algorithm: based on the cell gradient */
7963   /*------------------------------------------------------*/
7964 
7965   if (clip_mode == CS_GRADIENT_LIMIT_CELL) {
7966 
7967 #   pragma omp parallel
7968     {
7969       cs_gnum_t t_n_clip = 0;
7970       cs_real_t t_min_factor = min_factor, t_max_factor = max_factor;
7971 
7972 #     pragma omp for
7973       for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
7974 
7975         if (denum[c_id] > clipp_coef_sq * denom[c_id]) {
7976 
7977           cs_real_t factor1 = sqrt(clipp_coef_sq * denom[c_id]/denum[c_id]);
7978 
7979           for (cs_lnum_t i = 0; i < 3; i++) {
7980             for (cs_lnum_t j = 0; j < 3; j++)
7981               gradt[c_id][i][j] *= factor1;
7982           }
7983 
7984           t_min_factor = CS_MIN(factor1, t_min_factor);
7985           t_max_factor = CS_MAX(factor1, t_max_factor);
7986           t_n_clip++;
7987 
7988         } /* If clipping */
7989 
7990       } /* End of loop on cells */
7991 
7992 #     pragma omp critical
7993       {
7994         min_factor = CS_MIN(min_factor, t_min_factor);
7995         max_factor = CS_MAX(max_factor, t_max_factor);
7996         n_clip += t_n_clip;
7997       }
7998     } /* End of omp parallel construct */
7999 
8000   }
8001 
8002   /* Second clipping Algorithm: based on the face gradient */
8003   /*-------------------------------------------------------*/
8004 
8005   else if (clip_mode == CS_GRADIENT_LIMIT_FACE) {
8006 
8007     for (int g_id = 0; g_id < n_i_groups; g_id++) {
8008 
8009 #     pragma omp parallel for
8010       for (int t_id = 0; t_id < n_i_threads; t_id++) {
8011 
8012         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
8013              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
8014              f_id++) {
8015 
8016           cs_lnum_t c_id1 = i_face_cells[f_id][0];
8017           cs_lnum_t c_id2 = i_face_cells[f_id][1];
8018 
8019           cs_real_t factor1 = 1.0;
8020           if (denum[c_id1] > clipp_coef_sq * denom[c_id1])
8021             factor1 = sqrt(clipp_coef_sq * denom[c_id1]/denum[c_id1]);
8022 
8023           cs_real_t factor2 = 1.0;
8024           if (denum[c_id2] > clipp_coef_sq * denom[c_id2])
8025             factor2 = sqrt(clipp_coef_sq * denom[c_id2]/denum[c_id2]);
8026 
8027           cs_real_t t_min_factor = CS_MIN(factor1, factor2);
8028 
8029           clip_factor[c_id1] = CS_MIN(clip_factor[c_id1], t_min_factor);
8030           clip_factor[c_id2] = CS_MIN(clip_factor[c_id2], t_min_factor);
8031 
8032         } /* End of loop on faces */
8033 
8034       } /* End of loop on threads */
8035 
8036     } /* End of loop on thread groups */
8037 
8038     /* Complement for extended neighborhood */
8039 
8040     if (cell_cells_idx != NULL && halo_type == CS_HALO_EXTENDED) {
8041 
8042 #     pragma omp parallel for
8043       for (cs_lnum_t c_id1 = 0; c_id1 < n_cells; c_id1++) {
8044 
8045         cs_real_t t_min_factor = 1;
8046 
8047         for (cs_lnum_t cidx = cell_cells_idx[c_id1];
8048              cidx < cell_cells_idx[c_id1+1];
8049              cidx++) {
8050 
8051           cs_lnum_t c_id2 = cell_cells_lst[cidx];
8052           cs_real_t factor2 = 1.0;
8053 
8054           if (denum[c_id2] > clipp_coef_sq * denom[c_id2])
8055             factor2 = sqrt(clipp_coef_sq * denom[c_id2]/denum[c_id2]);
8056 
8057           t_min_factor = CS_MIN(min_factor, factor2);
8058 
8059         }
8060 
8061         clip_factor[c_id1] = CS_MIN(clip_factor[c_id1], t_min_factor);
8062 
8063       } /* End of loop on cells */
8064 
8065     } /* End for extended neighborhood */
8066 
8067 #   pragma omp parallel
8068     {
8069       cs_lnum_t t_n_clip = 0;
8070       cs_real_t t_min_factor = min_factor, t_max_factor = max_factor;
8071 
8072 #     pragma omp for
8073       for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
8074 
8075         for (cs_lnum_t i = 0; i < 3; i++) {
8076           for (cs_lnum_t j = 0; j < 3; j++)
8077             gradt[c_id][i][j] *= clip_factor[c_id];
8078         }
8079 
8080         if (clip_factor[c_id] < 0.99) {
8081           t_max_factor = CS_MAX(t_max_factor, clip_factor[c_id]);
8082           t_min_factor = CS_MIN(t_min_factor, clip_factor[c_id]);
8083           t_n_clip++;
8084         }
8085 
8086       } /* End of loop on cells */
8087 
8088 #     pragma omp critical
8089       {
8090         min_factor = CS_MIN(min_factor, t_min_factor);
8091         max_factor = CS_MAX(max_factor, t_max_factor);
8092         n_clip += t_n_clip;
8093       }
8094     } /* End of omp parallel construct */
8095 
8096   } /* End if clip_mode == CS_GRADIENT_LIMIT_FACE */
8097 
8098   /* Update min/max and n_clip in case of parallelism */
8099   /*--------------------------------------------------*/
8100 
8101 #if defined(HAVE_MPI)
8102 
8103   if (m->n_domains > 1) {
8104 
8105     assert(sizeof(cs_real_t) == sizeof(double));
8106 
8107     /* Global Max */
8108 
8109     MPI_Allreduce(&max_factor, &global_max_factor, 1, CS_MPI_REAL,
8110                   MPI_MAX, cs_glob_mpi_comm);
8111 
8112     max_factor = global_max_factor;
8113 
8114     /* Global min */
8115 
8116     MPI_Allreduce(&min_factor, &global_min_factor, 1, CS_MPI_REAL,
8117                   MPI_MIN, cs_glob_mpi_comm);
8118 
8119     min_factor = global_min_factor;
8120 
8121     /* Sum number of clippings */
8122 
8123     MPI_Allreduce(&n_clip, &n_g_clip, 1, CS_MPI_GNUM,
8124                   MPI_SUM, cs_glob_mpi_comm);
8125 
8126     n_clip = n_g_clip;
8127 
8128   } /* If n_domains > 1 */
8129 
8130 #endif /* defined(HAVE_MPI) */
8131 
8132   /* Output warning if necessary */
8133 
8134   if (verbosity > 1)
8135     bft_printf(_(" Variable: %s; Gradient of a vector limitation in %llu cells\n"
8136                  "   minimum factor = %14.5e; maximum factor = %14.5e\n"),
8137                var_name,
8138                (unsigned long long)n_clip, min_factor, max_factor);
8139 
8140   /* Synchronize the updated Gradient */
8141 
8142   if (m->halo != NULL) {
8143     cs_halo_sync_var_strided(m->halo, halo_type, (cs_real_t *)gradt, 9);
8144     if (cs_glob_mesh->have_rotation_perio)
8145       cs_halo_perio_sync_var_tens(m->halo, halo_type, (cs_real_t *)gradt);
8146   }
8147 
8148   BFT_FREE(buf);
8149 }
8150 
8151 /*----------------------------------------------------------------------------*/
8152 /*!
8153  * \brief  Compute cell gradient of tensor.
8154  *
8155  * \param[in]       var_name        variable name
8156  * \param[in]       gradient_info   performance logging structure, or NULL
8157  * \param[in]       gradient_type   gradient type
8158  * \param[in]       halo_type       halo type
8159  * \param[in]       inc             if 0, solve on increment; 1 otherwise
8160  * \param[in]       n_r_sweeps      if > 1, number of reconstruction sweeps
8161  * \param[in]       verbosity       verbosity level
8162  * \param[in]       clip_mode       clipping mode
8163  * \param[in]       epsilon         precision for iterative gradient calculation
8164  * \param[in]       clip_coeff      clipping coefficient
8165  * \param[in]       bc_coeff_a      boundary condition term a
8166  * \param[in]       bc_coeff_b      boundary condition term b
8167  * \param[in]       var             gradient's base variable
8168  * \param[out]      grad            gradient
8169                                       (\f$ \der{u_i}{x_j} \f$ is gradv[][i][j])
8170  */
8171 /*----------------------------------------------------------------------------*/
8172 
8173 static void
_gradient_tensor(const char * var_name,cs_gradient_info_t * gradient_info,cs_gradient_type_t gradient_type,cs_halo_type_t halo_type,int inc,int n_r_sweeps,int verbosity,int clip_mode,double epsilon,double clip_coeff,const cs_real_6_t bc_coeff_a[],const cs_real_66_t bc_coeff_b[],const cs_real_6_t * restrict var,cs_real_63_t * restrict grad)8174 _gradient_tensor(const char                *var_name,
8175                  cs_gradient_info_t        *gradient_info,
8176                  cs_gradient_type_t         gradient_type,
8177                  cs_halo_type_t             halo_type,
8178                  int                        inc,
8179                  int                        n_r_sweeps,
8180                  int                        verbosity,
8181                  int                        clip_mode,
8182                  double                     epsilon,
8183                  double                     clip_coeff,
8184                  const cs_real_6_t          bc_coeff_a[],
8185                  const cs_real_66_t         bc_coeff_b[],
8186                  const cs_real_6_t      *restrict var,
8187                  cs_real_63_t           *restrict grad)
8188 {
8189   const cs_mesh_t  *mesh = cs_glob_mesh;
8190   const cs_mesh_quantities_t *fvq = cs_glob_mesh_quantities;
8191 
8192   const cs_lnum_t n_b_faces = mesh->n_b_faces;
8193 
8194   /* Use Neumann BC's as default if not provided */
8195 
8196   cs_real_6_t *_bc_coeff_a = NULL;
8197   cs_real_66_t *_bc_coeff_b = NULL;
8198 
8199   if (bc_coeff_a == NULL) {
8200     BFT_MALLOC(_bc_coeff_a, n_b_faces, cs_real_6_t);
8201     for (cs_lnum_t i = 0; i < n_b_faces; i++) {
8202       for (cs_lnum_t j = 0; j < 6; j++)
8203         _bc_coeff_a[i][j] = 0;
8204     }
8205     bc_coeff_a = (const cs_real_6_t *)_bc_coeff_a;
8206   }
8207   if (bc_coeff_b == NULL) {
8208     BFT_MALLOC(_bc_coeff_b, n_b_faces, cs_real_66_t);
8209     for (cs_lnum_t i = 0; i < n_b_faces; i++) {
8210       for (cs_lnum_t j = 0; j < 6; j++) {
8211         for (cs_lnum_t k = 0; k < 6; k++)
8212           _bc_coeff_b[i][j][k] = 0;
8213         _bc_coeff_b[i][j][j] = 1;
8214       }
8215     }
8216     bc_coeff_b = (const cs_real_66_t *)_bc_coeff_b;
8217   }
8218 
8219   /* Compute gradient */
8220 
8221   switch (gradient_type) {
8222 
8223   case CS_GRADIENT_GREEN_ITER:
8224 
8225     _initialize_tensor_gradient(mesh,
8226                                 fvq,
8227                                 halo_type,
8228                                 inc,
8229                                 bc_coeff_a,
8230                                 bc_coeff_b,
8231                                 var,
8232                                 grad);
8233 
8234     /* If reconstructions are required */
8235 
8236     if (n_r_sweeps > 1)
8237       _iterative_tensor_gradient(mesh,
8238                                  fvq,
8239                                  var_name,
8240                                  gradient_info,
8241                                  halo_type,
8242                                  inc,
8243                                  n_r_sweeps,
8244                                  verbosity,
8245                                  epsilon,
8246                                  bc_coeff_a,
8247                                  bc_coeff_b,
8248                                  (const cs_real_6_t *)var,
8249                                  grad);
8250 
8251     break;
8252 
8253   case CS_GRADIENT_LSQ:
8254 
8255     _lsq_tensor_gradient(mesh,
8256                          cs_glob_mesh_adjacencies,
8257                          fvq,
8258                          halo_type,
8259                          inc,
8260                          bc_coeff_a,
8261                          bc_coeff_b,
8262                          (const cs_real_6_t *)var,
8263                          NULL, /* c_weight */
8264                          grad);
8265 
8266     _tensor_gradient_clipping(mesh,
8267                               fvq,
8268                               halo_type,
8269                               clip_mode,
8270                               verbosity,
8271                               clip_coeff,
8272                               var_name,
8273                               (const cs_real_6_t *)var,
8274                               grad);
8275 
8276     break;
8277 
8278   default:
8279     assert(0);  /* Should be set to available options by caller */
8280     break;
8281 
8282   }
8283 
8284   BFT_FREE(_bc_coeff_a);
8285   BFT_FREE(_bc_coeff_b);
8286 }
8287 
8288 /*============================================================================
8289  * Fortran wrapper function definitions
8290  *============================================================================*/
8291 
8292 /*----------------------------------------------------------------------------
8293  * Compute cell gradient of scalar field or component of vector or
8294  * tensor field.
8295  *----------------------------------------------------------------------------*/
8296 
8297 void
cs_f_gradient_s(int f_id,int imrgra,int inc,int iccocg,int n_r_sweeps,int iwarnp,int imligp,cs_real_t epsrgp,cs_real_t climgp,const cs_real_t coefap[],const cs_real_t coefbp[],cs_real_t pvar[],cs_real_3_t grad[])8298 cs_f_gradient_s(int               f_id,
8299                 int               imrgra,
8300                 int               inc,
8301                 int               iccocg,
8302                 int               n_r_sweeps,
8303                 int               iwarnp,
8304                 int               imligp,
8305                 cs_real_t         epsrgp,
8306                 cs_real_t         climgp,
8307                 const cs_real_t   coefap[],
8308                 const cs_real_t   coefbp[],
8309                 cs_real_t         pvar[],
8310                 cs_real_3_t       grad[])
8311 {
8312   bool recompute_cocg = (iccocg) ? true : false;
8313 
8314   cs_halo_type_t halo_type = CS_HALO_STANDARD;
8315   cs_gradient_type_t gradient_type = CS_GRADIENT_GREEN_ITER;
8316 
8317   char var_name[32];
8318   if (f_id > -1) {
8319     cs_field_t *f = cs_field_by_id(f_id);
8320     snprintf(var_name, 31, "%s", f->name);
8321   }
8322   else
8323     strcpy(var_name, "Work array");
8324   var_name[31] = '\0';
8325 
8326   /* Choose gradient type */
8327 
8328   cs_gradient_type_by_imrgra(imrgra,
8329                              &gradient_type,
8330                              &halo_type);
8331 
8332   /* Check if given field has internal coupling  */
8333   cs_internal_coupling_t  *cpl = NULL;
8334   if (f_id > -1) {
8335     const int key_id = cs_field_key_id_try("coupling_entity");
8336     if (key_id > -1) {
8337       const cs_field_t *f = cs_field_by_id(f_id);
8338       int coupl_id = cs_field_get_key_int(f, key_id);
8339       if (coupl_id > -1)
8340         cpl = cs_internal_coupling_by_id(coupl_id);
8341     }
8342   }
8343 
8344   /* Compute gradient */
8345 
8346   cs_gradient_scalar(var_name,
8347                      gradient_type,
8348                      halo_type,
8349                      inc,
8350                      recompute_cocg,
8351                      n_r_sweeps,
8352                      0,             /* ignored */
8353                      0,             /* iphydp */
8354                      1,             /* w_stride */
8355                      iwarnp,
8356                      imligp,
8357                      epsrgp,
8358                      climgp,
8359                      NULL,          /* f_ext */
8360                      coefap,
8361                      coefbp,
8362                      pvar,
8363                      NULL,          /* c_weight */
8364                      cpl,
8365                      grad);
8366 }
8367 
8368 /*----------------------------------------------------------------------------
8369  * Compute cell gradient of potential-type values.
8370  *----------------------------------------------------------------------------*/
8371 
8372 void
cs_f_gradient_potential(int f_id,int imrgra,int inc,int iccocg,int n_r_sweeps,int iphydp,int iwarnp,int imligp,cs_real_t epsrgp,cs_real_t climgp,cs_real_3_t f_ext[],const cs_real_t coefap[],const cs_real_t coefbp[],cs_real_t pvar[],cs_real_3_t grad[])8373 cs_f_gradient_potential(int               f_id,
8374                         int               imrgra,
8375                         int               inc,
8376                         int               iccocg,
8377                         int               n_r_sweeps,
8378                         int               iphydp,
8379                         int               iwarnp,
8380                         int               imligp,
8381                         cs_real_t         epsrgp,
8382                         cs_real_t         climgp,
8383                         cs_real_3_t       f_ext[],
8384                         const cs_real_t   coefap[],
8385                         const cs_real_t   coefbp[],
8386                         cs_real_t         pvar[],
8387                         cs_real_3_t       grad[])
8388 {
8389   bool recompute_cocg = (iccocg) ? true : false;
8390 
8391   cs_halo_type_t halo_type = CS_HALO_STANDARD;
8392   cs_gradient_type_t gradient_type = CS_GRADIENT_GREEN_ITER;
8393 
8394   char var_name[32];
8395   if (f_id > -1) {
8396     cs_field_t *f = cs_field_by_id(f_id);
8397     snprintf(var_name, 31, "%s", f->name);
8398   }
8399   else
8400     strcpy(var_name, "Work array");
8401   var_name[31] = '\0';
8402 
8403   /* Choose gradient type */
8404 
8405   cs_gradient_type_by_imrgra(imrgra,
8406                              &gradient_type,
8407                              &halo_type);
8408 
8409   /* Check if given field has internal coupling  */
8410   cs_internal_coupling_t  *cpl = NULL;
8411   if (f_id > -1) {
8412     const int key_id = cs_field_key_id_try("coupling_entity");
8413     if (key_id > -1) {
8414       const cs_field_t *f = cs_field_by_id(f_id);
8415       int coupl_id = cs_field_get_key_int(f, key_id);
8416       if (coupl_id > -1)
8417         cpl = cs_internal_coupling_by_id(coupl_id);
8418     }
8419   }
8420 
8421   /* Compute gradient */
8422 
8423   cs_gradient_scalar(var_name,
8424                      gradient_type,
8425                      halo_type,
8426                      inc,
8427                      recompute_cocg,
8428                      n_r_sweeps,
8429                      0,             /* ignored */
8430                      iphydp,
8431                      1,             /* w_stride */
8432                      iwarnp,
8433                      imligp,
8434                      epsrgp,
8435                      climgp,
8436                      f_ext,
8437                      coefap,
8438                      coefbp,
8439                      pvar,
8440                      NULL,          /* c_weight */
8441                      cpl,
8442                      grad);
8443 }
8444 
8445 /*----------------------------------------------------------------------------
8446  * Compute cell gradient of potential-type values.
8447  *----------------------------------------------------------------------------*/
8448 
8449 void
cs_f_gradient_weighted_s(int f_id,int imrgra,int inc,int iccocg,int n_r_sweeps,int iphydp,int iwarnp,int imligp,cs_real_t epsrgp,cs_real_t climgp,cs_real_3_t f_ext[],const cs_real_t coefap[],const cs_real_t coefbp[],cs_real_t pvar[],cs_real_t c_weight[],cs_real_3_t grad[])8450 cs_f_gradient_weighted_s(int               f_id,
8451                          int               imrgra,
8452                          int               inc,
8453                          int               iccocg,
8454                          int               n_r_sweeps,
8455                          int               iphydp,
8456                          int               iwarnp,
8457                          int               imligp,
8458                          cs_real_t         epsrgp,
8459                          cs_real_t         climgp,
8460                          cs_real_3_t       f_ext[],
8461                          const cs_real_t   coefap[],
8462                          const cs_real_t   coefbp[],
8463                          cs_real_t         pvar[],
8464                          cs_real_t         c_weight[],
8465                          cs_real_3_t       grad[])
8466 {
8467   bool recompute_cocg = (iccocg) ? true : false;
8468 
8469   cs_halo_type_t halo_type = CS_HALO_STANDARD;
8470   cs_gradient_type_t gradient_type = CS_GRADIENT_GREEN_ITER;
8471 
8472   char var_name[32];
8473   if (f_id > -1) {
8474     cs_field_t *f = cs_field_by_id(f_id);
8475     snprintf(var_name, 31, "%s", f->name);
8476   }
8477   else
8478     strcpy(var_name, "Work array");
8479   var_name[31] = '\0';
8480 
8481   /* Choose gradient type */
8482 
8483   cs_gradient_type_by_imrgra(imrgra,
8484                              &gradient_type,
8485                              &halo_type);
8486 
8487   /* Check if given field has internal coupling  */
8488   cs_internal_coupling_t  *cpl = NULL;
8489   if (f_id > -1) {
8490     const int key_id = cs_field_key_id_try("coupling_entity");
8491     if (key_id > -1) {
8492       const cs_field_t *f = cs_field_by_id(f_id);
8493       int coupl_id = cs_field_get_key_int(f, key_id);
8494       if (coupl_id > -1)
8495         cpl = cs_internal_coupling_by_id(coupl_id);
8496     }
8497   }
8498 
8499   /* Compute gradient */
8500 
8501   cs_gradient_scalar(var_name,
8502                      gradient_type,
8503                      halo_type,
8504                      inc,
8505                      recompute_cocg,
8506                      n_r_sweeps,
8507                      0,             /* ignored */
8508                      iphydp,
8509                      1,             /* w_stride */
8510                      iwarnp,
8511                      imligp,
8512                      epsrgp,
8513                      climgp,
8514                      f_ext,
8515                      coefap,
8516                      coefbp,
8517                      pvar,
8518                      c_weight,
8519                      cpl,
8520                      grad);
8521 }
8522 
8523 /*! (DOXYGEN_SHOULD_SKIP_THIS) \endcond */
8524 
8525 /*============================================================================
8526  * Public function definitions for Fortran API
8527  *============================================================================*/
8528 
8529 /*----------------------------------------------------------------------------
8530  * Compute the steady balance due to porous modelling for the pressure
8531  * gradient
8532  *----------------------------------------------------------------------------*/
8533 
CS_PROCF(grdpor,GRDPOR)8534 void CS_PROCF (grdpor, GRDPOR)
8535 (
8536  const int   *const inc          /* <-- 0 or 1: increment or not         */
8537 )
8538 {
8539   cs_gradient_porosity_balance(*inc);
8540 }
8541 
8542 /*----------------------------------------------------------------------------
8543  * Compute cell gradient of vector field.
8544  *----------------------------------------------------------------------------*/
8545 
CS_PROCF(cgdvec,CGDVEC)8546 void CS_PROCF (cgdvec, CGDVEC)
8547 (
8548  const int              *const f_id,      /* <-- field id, or -1              */
8549  const int              *const imrgra,    /* <-- gradient computation mode    */
8550  const int              *const inc,       /* <-- 0 or 1: increment or not     */
8551  const int              *const n_r_sweeps,/* <-- >1: with reconstruction      */
8552  const int              *const iwarnp,    /* <-- verbosity level              */
8553  const int              *const imligp,    /* <-- type of clipping             */
8554  const cs_real_t        *const epsrgp,    /* <-- precision for iterative
8555                                                  gradient calculation         */
8556  const cs_real_t        *const climgp,    /* <-- clipping coefficient         */
8557  const cs_real_3_t             coefav[],  /* <-- boundary condition term      */
8558  const cs_real_33_t            coefbv[],  /* <-- boundary condition term      */
8559        cs_real_3_t             pvar[],    /* <-- gradient's base variable     */
8560        cs_real_33_t            grad[]     /* <-> gradient of the variable
8561                                                  (du_i/dx_j : grad[][i][j])  */
8562 )
8563 {
8564   char var_name[32];
8565 
8566   cs_halo_type_t halo_type = CS_HALO_STANDARD;
8567   cs_gradient_type_t gradient_type = CS_GRADIENT_GREEN_ITER;
8568 
8569   cs_gradient_type_by_imrgra(*imrgra,
8570                              &gradient_type,
8571                              &halo_type);
8572 
8573   if (*f_id > -1)
8574     snprintf(var_name, 31, "Field %2d", *f_id);
8575   else
8576     strcpy(var_name, "Work array");
8577   var_name[31] = '\0';
8578 
8579   /* Check if given field has internal coupling  */
8580   cs_internal_coupling_t  *cpl = NULL;
8581   if (*f_id > -1) {
8582     const int key_id = cs_field_key_id_try("coupling_entity");
8583     if (key_id > -1) {
8584       const cs_field_t *f = cs_field_by_id(*f_id);
8585       int coupl_id = cs_field_get_key_int(f, key_id);
8586       if (coupl_id > -1)
8587         cpl = cs_internal_coupling_by_id(coupl_id);
8588     }
8589   }
8590 
8591   cs_gradient_vector(var_name,
8592                      gradient_type,
8593                      halo_type,
8594                      *inc,
8595                      *n_r_sweeps,
8596                      *iwarnp,
8597                      *imligp,
8598                      *epsrgp,
8599                      *climgp,
8600                      coefav,
8601                      coefbv,
8602                      pvar,
8603                      NULL, /* weighted gradient */
8604                      cpl,
8605                      grad);
8606 }
8607 
8608 /*============================================================================
8609  * Public function definitions
8610  *============================================================================*/
8611 
8612 /*----------------------------------------------------------------------------*/
8613 /*!
8614  * \brief  Initialize gradient computation API.
8615  */
8616 /*----------------------------------------------------------------------------*/
8617 
8618 void
cs_gradient_initialize(void)8619 cs_gradient_initialize(void)
8620 {
8621   assert(cs_glob_mesh != NULL);
8622 
8623   CS_TIMER_COUNTER_INIT(_gradient_t_tot);
8624 
8625   int stats_root = cs_timer_stats_id_by_name("operations");
8626   if (stats_root > -1) {
8627     _gradient_stat_id = cs_timer_stats_create("operations",
8628                                               "gradients",
8629                                               "gradients reconstruction");
8630   }
8631 }
8632 
8633 /*----------------------------------------------------------------------------*/
8634 /*!
8635  * \brief  Finalize gradient computation API.
8636  */
8637 /*----------------------------------------------------------------------------*/
8638 
8639 void
cs_gradient_finalize(void)8640 cs_gradient_finalize(void)
8641 {
8642   _gradient_quantities_destroy();
8643 
8644   cs_log_printf(CS_LOG_PERFORMANCE,
8645                 _("\n"
8646                   "Total elapsed time for all gradient computations:  %.3f s\n"),
8647                 _gradient_t_tot.nsec*1e-9);
8648 
8649   /* Free system info */
8650 
8651   for (int ii = 0; ii < cs_glob_gradient_n_systems; ii++) {
8652     _gradient_info_dump(cs_glob_gradient_systems[ii]);
8653     _gradient_info_destroy(&(cs_glob_gradient_systems[ii]));
8654   }
8655 
8656   cs_log_printf(CS_LOG_PERFORMANCE, "\n");
8657   cs_log_separator(CS_LOG_PERFORMANCE);
8658 
8659   BFT_FREE(cs_glob_gradient_systems);
8660 
8661   cs_glob_gradient_n_systems = 0;
8662   cs_glob_gradient_n_max_systems = 0;
8663 }
8664 
8665 /*----------------------------------------------------------------------------*/
8666 /*!
8667  * \brief  Free saved gradient quantities.
8668  *
8669  * This is required when the mesh changes, so that the on-demand computation
8670  * will be updated.
8671  */
8672 /*----------------------------------------------------------------------------*/
8673 
8674 void
cs_gradient_free_quantities(void)8675 cs_gradient_free_quantities(void)
8676 {
8677   for (int i = 0; i < _n_gradient_quantities; i++) {
8678 
8679     cs_gradient_quantities_t  *gq = _gradient_quantities + i;
8680 
8681     BFT_FREE(gq->cocg_it);
8682     BFT_FREE(gq->cocgb_s_lsq);
8683     BFT_FREE(gq->cocg_lsq);
8684     BFT_FREE(gq->cocgb_s_lsq_ext);
8685     BFT_FREE(gq->cocg_lsq_ext);
8686 
8687   }
8688 }
8689 
8690 /*----------------------------------------------------------------------------*/
8691 /*!
8692  * \brief  Compute cell gradient of scalar field or component of vector or
8693  *         tensor field.
8694  *
8695  * \param[in]       var_name       variable name
8696  * \param[in]       gradient_type  gradient type
8697  * \param[in]       halo_type      halo type
8698  * \param[in]       inc            if 0, solve on increment; 1 otherwise
8699  * \param[in]       recompute_cocg should COCG FV quantities be recomputed ?
8700  * \param[in]       n_r_sweeps     if > 1, number of reconstruction sweeps
8701  *                                 (only used by CS_GRADIENT_GREEN_ITER)
8702  * \param[in]       tr_dim         ignored
8703  * \param[in]       hyd_p_flag     flag for hydrostatic pressure
8704  * \param[in]       w_stride       stride for weighting coefficient
8705  * \param[in]       verbosity      verbosity level
8706  * \param[in]       clip_mode      clipping mode
8707  * \param[in]       epsilon        precision for iterative gradient calculation
8708  * \param[in]       clip_coeff     clipping coefficient
8709  * \param[in]       f_ext          exterior force generating the
8710  *                                 hydrostatic pressure
8711  * \param[in]       bc_coeff_a     boundary condition term a
8712  * \param[in]       bc_coeff_b     boundary condition term b
8713  * \param[in, out]  var            gradient's base variable
8714  * \param[in, out]  c_weight       cell variable weight, or NULL
8715  * \param[in]       cpl            associated internal coupling, or NULL
8716  * \param[out]      grad           gradient
8717  */
8718 /*----------------------------------------------------------------------------*/
8719 
8720 void
cs_gradient_scalar(const char * var_name,cs_gradient_type_t gradient_type,cs_halo_type_t halo_type,int inc,bool recompute_cocg,int n_r_sweeps,int tr_dim,int hyd_p_flag,int w_stride,int verbosity,cs_gradient_limit_t clip_mode,double epsilon,double clip_coeff,cs_real_3_t f_ext[],const cs_real_t bc_coeff_a[],const cs_real_t bc_coeff_b[],cs_real_t var[restrict],cs_real_t * restrict c_weight,const cs_internal_coupling_t * cpl,cs_real_t grad[restrict][3])8721 cs_gradient_scalar(const char                    *var_name,
8722                    cs_gradient_type_t             gradient_type,
8723                    cs_halo_type_t                 halo_type,
8724                    int                            inc,
8725                    bool                           recompute_cocg,
8726                    int                            n_r_sweeps,
8727                    int                            tr_dim,
8728                    int                            hyd_p_flag,
8729                    int                            w_stride,
8730                    int                            verbosity,
8731                    cs_gradient_limit_t            clip_mode,
8732                    double                         epsilon,
8733                    double                         clip_coeff,
8734                    cs_real_3_t                    f_ext[],
8735                    const cs_real_t                bc_coeff_a[],
8736                    const cs_real_t                bc_coeff_b[],
8737                    cs_real_t                      var[restrict],
8738                    cs_real_t            *restrict c_weight,
8739                    const cs_internal_coupling_t  *cpl,
8740                    cs_real_t                      grad[restrict][3])
8741 {
8742   CS_UNUSED(tr_dim);
8743 
8744   const cs_mesh_t  *mesh = cs_glob_mesh;
8745   cs_gradient_info_t *gradient_info = NULL;
8746   cs_timer_t t0, t1;
8747 
8748   bool update_stats = true;
8749 
8750   t0 = cs_timer_time();
8751 
8752   if (update_stats == true)
8753     gradient_info = _find_or_add_system(var_name, gradient_type);
8754 
8755   /* Synchronize variable */
8756 
8757   if (mesh->halo != NULL) {
8758 
8759     cs_halo_sync_var(mesh->halo, halo_type, var);
8760 
8761     if (c_weight != NULL) {
8762       if (w_stride == 6) {
8763         cs_halo_sync_var_strided(mesh->halo, halo_type, c_weight, 6);
8764         cs_halo_perio_sync_var_sym_tens(mesh->halo, halo_type, c_weight);
8765       }
8766       else
8767         cs_halo_sync_var(mesh->halo, halo_type, c_weight);
8768     }
8769 
8770     if (hyd_p_flag == 1) {
8771       cs_halo_sync_var_strided(mesh->halo, halo_type, (cs_real_t *)f_ext, 3);
8772       cs_halo_perio_sync_var_vect(mesh->halo, halo_type, (cs_real_t *)f_ext, 3);
8773     }
8774 
8775   }
8776 
8777   _gradient_scalar(var_name,
8778                    gradient_info,
8779                    gradient_type,
8780                    halo_type,
8781                    inc,
8782                    recompute_cocg,
8783                    n_r_sweeps,
8784                    hyd_p_flag,
8785                    w_stride,
8786                    verbosity,
8787                    clip_mode,
8788                    epsilon,
8789                    clip_coeff,
8790                    f_ext,
8791                    bc_coeff_a,
8792                    bc_coeff_b,
8793                    var,
8794                    c_weight,
8795                    cpl,
8796                    grad);
8797 
8798   t1 = cs_timer_time();
8799 
8800   cs_timer_counter_add_diff(&_gradient_t_tot, &t0, &t1);
8801 
8802   if (update_stats == true) {
8803     gradient_info->n_calls += 1;
8804     cs_timer_counter_add_diff(&(gradient_info->t_tot), &t0, &t1);
8805   }
8806 
8807   if (_gradient_stat_id > -1)
8808     cs_timer_stats_add_diff(_gradient_stat_id, &t0, &t1);
8809 }
8810 
8811 /*----------------------------------------------------------------------------*/
8812 /*!
8813  * \brief  Compute cell gradient of vector field.
8814  *
8815  * \param[in]       var_name        variable name
8816  * \param[in]       gradient_type   gradient type
8817  * \param[in]       halo_type       halo type
8818  * \param[in]       inc             if 0, solve on increment; 1 otherwise
8819  * \param[in]       n_r_sweeps      if > 1, number of reconstruction sweeps
8820  *                                  (only used by CS_GRADIENT_GREEN_ITER)
8821  * \param[in]       verbosity       verbosity level
8822  * \param[in]       clip_mode       clipping mode
8823  * \param[in]       epsilon         precision for iterative gradient calculation
8824  * \param[in]       clip_coeff      clipping coefficient
8825  * \param[in]       bc_coeff_a      boundary condition term a
8826  * \param[in]       bc_coeff_b      boundary condition term b
8827  * \param[in, out]  var             gradient's base variable
8828  * \param[in, out]  c_weight        cell variable weight, or NULL
8829  * \param[in]       cpl             associated internal coupling, or NULL
8830  * \param[out]      gradv           gradient
8831                                     (\f$ \der{u_i}{x_j} \f$ is gradv[][i][j])
8832  */
8833 /*----------------------------------------------------------------------------*/
8834 
8835 void
cs_gradient_vector(const char * var_name,cs_gradient_type_t gradient_type,cs_halo_type_t halo_type,int inc,int n_r_sweeps,int verbosity,cs_gradient_limit_t clip_mode,double epsilon,double clip_coeff,const cs_real_t bc_coeff_a[][3],const cs_real_t bc_coeff_b[][3][3],cs_real_t var[restrict][3],cs_real_t * restrict c_weight,const cs_internal_coupling_t * cpl,cs_real_t gradv[restrict][3][3])8836 cs_gradient_vector(const char                    *var_name,
8837                    cs_gradient_type_t             gradient_type,
8838                    cs_halo_type_t                 halo_type,
8839                    int                            inc,
8840                    int                            n_r_sweeps,
8841                    int                            verbosity,
8842                    cs_gradient_limit_t            clip_mode,
8843                    double                         epsilon,
8844                    double                         clip_coeff,
8845                    const cs_real_t                bc_coeff_a[][3],
8846                    const cs_real_t                bc_coeff_b[][3][3],
8847                    cs_real_t                      var[restrict][3],
8848                    cs_real_t        *restrict     c_weight,
8849                    const cs_internal_coupling_t  *cpl,
8850                    cs_real_t                      gradv[restrict][3][3])
8851 {
8852   const cs_mesh_t  *mesh = cs_glob_mesh;
8853 
8854   cs_gradient_info_t *gradient_info = NULL;
8855   cs_timer_t t0, t1;
8856 
8857   bool update_stats = true;
8858 
8859   t0 = cs_timer_time();
8860 
8861   if (update_stats == true) {
8862     gradient_info = _find_or_add_system(var_name, gradient_type);
8863   }
8864 
8865   /* By default, handle the gradient as a tensor
8866      (i.e. we assume it is the gradient of a vector field) */
8867 
8868   if (mesh->halo != NULL) {
8869 
8870     cs_halo_sync_var_strided(mesh->halo, halo_type, (cs_real_t *)var, 3);
8871     if (cs_glob_mesh->have_rotation_perio)
8872       cs_halo_perio_sync_var_vect(mesh->halo, halo_type, (cs_real_t *)var, 3);
8873 
8874     if (c_weight != NULL)
8875       cs_halo_sync_var(mesh->halo, halo_type, c_weight);
8876 
8877   }
8878 
8879   /* Compute gradient */
8880 
8881   _gradient_vector(var_name,
8882                    gradient_info,
8883                    gradient_type,
8884                    halo_type,
8885                    inc,
8886                    n_r_sweeps,
8887                    verbosity,
8888                    clip_mode,
8889                    epsilon,
8890                    clip_coeff,
8891                    bc_coeff_a,
8892                    bc_coeff_b,
8893                    (const cs_real_3_t *)var,
8894                    (const cs_real_t *)c_weight,
8895                    cpl,
8896                    gradv);
8897 
8898   t1 = cs_timer_time();
8899 
8900   cs_timer_counter_add_diff(&_gradient_t_tot, &t0, &t1);
8901 
8902   if (update_stats == true) {
8903     gradient_info->n_calls += 1;
8904     cs_timer_counter_add_diff(&(gradient_info->t_tot), &t0, &t1);
8905   }
8906 
8907   if (_gradient_stat_id > -1)
8908     cs_timer_stats_add_diff(_gradient_stat_id, &t0, &t1);
8909 }
8910 
8911 /*----------------------------------------------------------------------------*/
8912 /*!
8913  * \brief  Compute cell gradient of tensor.
8914  *
8915  * \param[in]       var_name        variable name
8916  * \param[in]       gradient_type   gradient type
8917  * \param[in]       halo_type       halo type
8918  * \param[in]       inc             if 0, solve on increment; 1 otherwise
8919  * \param[in]       n_r_sweeps      if > 1, number of reconstruction sweeps
8920  *                                  (only used by CS_GRADIENT_GREEN_ITER)
8921  * \param[in]       verbosity       verbosity level
8922  * \param[in]       clip_mode       clipping mode
8923  * \param[in]       epsilon         precision for iterative gradient calculation
8924  * \param[in]       clip_coeff      clipping coefficient
8925  * \param[in]       bc_coeff_a      boundary condition term a
8926  * \param[in]       bc_coeff_b      boundary condition term b
8927  * \param[in, out]  var             gradient's base variable
8928  * \param[out]      grad            gradient
8929                                     (\f$ \der{t_ij}{x_k} \f$ is grad[][ij][k])
8930  */
8931 /*----------------------------------------------------------------------------*/
8932 
8933 void
cs_gradient_tensor(const char * var_name,cs_gradient_type_t gradient_type,cs_halo_type_t halo_type,int inc,int n_r_sweeps,int verbosity,cs_gradient_limit_t clip_mode,double epsilon,double clip_coeff,const cs_real_6_t bc_coeff_a[],const cs_real_66_t bc_coeff_b[],cs_real_6_t * restrict var,cs_real_63_t * restrict grad)8934 cs_gradient_tensor(const char                *var_name,
8935                    cs_gradient_type_t         gradient_type,
8936                    cs_halo_type_t             halo_type,
8937                    int                        inc,
8938                    int                        n_r_sweeps,
8939                    int                        verbosity,
8940                    cs_gradient_limit_t        clip_mode,
8941                    double                     epsilon,
8942                    double                     clip_coeff,
8943                    const cs_real_6_t          bc_coeff_a[],
8944                    const cs_real_66_t         bc_coeff_b[],
8945                    cs_real_6_t      *restrict var,
8946                    cs_real_63_t     *restrict grad)
8947 {
8948   const cs_mesh_t  *mesh = cs_glob_mesh;
8949 
8950   cs_gradient_info_t *gradient_info = NULL;
8951   cs_timer_t t0, t1;
8952 
8953   bool update_stats = true;
8954 
8955   if (   gradient_type == CS_GRADIENT_GREEN_LSQ
8956       || gradient_type == CS_GRADIENT_GREEN_VTX)
8957     gradient_type = CS_GRADIENT_GREEN_ITER;
8958 
8959   t0 = cs_timer_time();
8960 
8961   if (update_stats == true) {
8962     gradient_info = _find_or_add_system(var_name, gradient_type);
8963   }
8964 
8965   /* By default, handle the gradient as a tensor
8966      (i.e. we assume it is the gradient of a vector field) */
8967 
8968   if (mesh->halo != NULL) {
8969     cs_halo_sync_var_strided(mesh->halo, halo_type, (cs_real_t *)var, 6);
8970     if (mesh->have_rotation_perio)
8971       cs_halo_perio_sync_var_sym_tens(mesh->halo, halo_type, (cs_real_t *)var);
8972   }
8973 
8974   /* Compute gradient */
8975 
8976   _gradient_tensor(var_name,
8977                    gradient_info,
8978                    gradient_type,
8979                    halo_type,
8980                    inc,
8981                    n_r_sweeps,
8982                    verbosity,
8983                    clip_mode,
8984                    epsilon,
8985                    clip_coeff,
8986                    bc_coeff_a,
8987                    bc_coeff_b,
8988                    (const cs_real_6_t *)var,
8989                    grad);
8990 
8991   t1 = cs_timer_time();
8992 
8993   cs_timer_counter_add_diff(&_gradient_t_tot, &t0, &t1);
8994 
8995   if (update_stats == true) {
8996     gradient_info->n_calls += 1;
8997     cs_timer_counter_add_diff(&(gradient_info->t_tot), &t0, &t1);
8998   }
8999 
9000   if (_gradient_stat_id > -1)
9001     cs_timer_stats_add_diff(_gradient_stat_id, &t0, &t1);
9002 }
9003 
9004 /*----------------------------------------------------------------------------*/
9005 /*!
9006  * \brief  Compute cell gradient of scalar field or component of vector or
9007  *         tensor field.
9008  *
9009  * This variant of the \ref cs_gradient_scalar function assumes ghost cell
9010  * values for input arrays (var and optionally c_weight)
9011  * have already been synchronized.
9012  *
9013  * \param[in]   var_name        variable name
9014  * \param[in]   gradient_type   gradient type
9015  * \param[in]   halo_type       halo type
9016  * \param[in]   inc             if 0, solve on increment; 1 otherwise
9017  * \param[in]   recompute_cocg  should COCG FV quantities be recomputed ?
9018  * \param[in]   n_r_sweeps      if > 1, number of reconstruction sweeps
9019  *                              (only used by CS_GRADIENT_GREEN_ITER)
9020  * \param[in]   hyd_p_flag      flag for hydrostatic pressure
9021  * \param[in]   w_stride        stride for weighting coefficient
9022  * \param[in]   verbosity       verbosity level
9023  * \param[in]   clip_mode       clipping mode
9024  * \param[in]   epsilon         precision for iterative gradient calculation
9025  * \param[in]   clip_coeff      clipping coefficient
9026  * \param[in]   f_ext           exterior force generating the
9027  *                              hydrostatic pressure
9028  * \param[in]   bc_coeff_a      boundary condition term a
9029  * \param[in]   bc_coeff_b      boundary condition term b
9030  * \param[in]   var             gradient's base variable
9031  * \param[in]   c_weight        cell variable weight, or NULL
9032  * \param[in]   cpl             associated internal coupling, or NULL
9033  * \param[out]  grad            gradient
9034  */
9035 /*----------------------------------------------------------------------------*/
9036 
9037 void
cs_gradient_scalar_synced_input(const char * var_name,cs_gradient_type_t gradient_type,cs_halo_type_t halo_type,int inc,bool recompute_cocg,int n_r_sweeps,int hyd_p_flag,int w_stride,int verbosity,cs_gradient_limit_t clip_mode,double epsilon,double clip_coeff,cs_real_t f_ext[][3],const cs_real_t bc_coeff_a[],const cs_real_t bc_coeff_b[],const cs_real_t var[restrict],const cs_real_t c_weight[restrict],const cs_internal_coupling_t * cpl,cs_real_t grad[restrict][3])9038 cs_gradient_scalar_synced_input(const char                 *var_name,
9039                                 cs_gradient_type_t          gradient_type,
9040                                 cs_halo_type_t              halo_type,
9041                                 int                         inc,
9042                                 bool                        recompute_cocg,
9043                                 int                         n_r_sweeps,
9044                                 int                         hyd_p_flag,
9045                                 int                         w_stride,
9046                                 int                         verbosity,
9047                                 cs_gradient_limit_t         clip_mode,
9048                                 double                      epsilon,
9049                                 double                      clip_coeff,
9050                                 cs_real_t                   f_ext[][3],
9051                                 const cs_real_t             bc_coeff_a[],
9052                                 const cs_real_t             bc_coeff_b[],
9053                                 const cs_real_t             var[restrict],
9054                                 const cs_real_t             c_weight[restrict],
9055                                 const cs_internal_coupling_t  *cpl,
9056                                 cs_real_t                   grad[restrict][3])
9057 {
9058   cs_gradient_info_t *gradient_info = NULL;
9059   cs_timer_t t0, t1;
9060 
9061   bool update_stats = true;
9062 
9063   if (hyd_p_flag == 1) {
9064     if (cs_glob_mesh->halo != NULL) {
9065       cs_halo_sync_var_strided(cs_glob_mesh->halo, halo_type,
9066                                (cs_real_t *)f_ext, 3);
9067       if (cs_glob_mesh->have_rotation_perio)
9068         cs_halo_perio_sync_var_vect(cs_glob_mesh->halo, halo_type,
9069                                     (cs_real_t *)f_ext, 3);
9070     }
9071   }
9072 
9073   t0 = cs_timer_time();
9074 
9075   if (update_stats == true)
9076     gradient_info = _find_or_add_system(var_name, gradient_type);
9077 
9078   _gradient_scalar(var_name,
9079                    gradient_info,
9080                    gradient_type,
9081                    halo_type,
9082                    inc,
9083                    recompute_cocg,
9084                    n_r_sweeps,
9085                    hyd_p_flag,
9086                    w_stride,
9087                    verbosity,
9088                    clip_mode,
9089                    epsilon,
9090                    clip_coeff,
9091                    f_ext,
9092                    bc_coeff_a,
9093                    bc_coeff_b,
9094                    var,
9095                    c_weight,
9096                    cpl,
9097                    grad);
9098 
9099   t1 = cs_timer_time();
9100 
9101   cs_timer_counter_add_diff(&_gradient_t_tot, &t0, &t1);
9102 
9103   if (update_stats == true) {
9104     gradient_info->n_calls += 1;
9105     cs_timer_counter_add_diff(&(gradient_info->t_tot), &t0, &t1);
9106   }
9107 
9108   if (_gradient_stat_id > -1)
9109     cs_timer_stats_add_diff(_gradient_stat_id, &t0, &t1);
9110 }
9111 
9112 /*----------------------------------------------------------------------------*/
9113 /*!
9114  * \brief  Compute cell gradient of vector field.
9115  *
9116  * This variant of the \ref cs_gradient_vector function assumes ghost cell
9117  * values for input arrays (var and optionally c_weight)
9118  * have already been synchronized.
9119  *
9120  * \param[in]   var_name        variable name
9121  * \param[in]   gradient_type   gradient type
9122  * \param[in]   halo_type       halo type
9123  * \param[in]   inc             if 0, solve on increment; 1 otherwise
9124  * \param[in]   n_r_sweeps      if > 1, number of reconstruction sweeps
9125  *                              (only used by CS_GRADIENT_GREEN_ITER)
9126  * \param[in]   verbosity       verbosity level
9127  * \param[in]   clip_mode       clipping mode
9128  * \param[in]   epsilon         precision for iterative gradient calculation
9129  * \param[in]   clip_coeff      clipping coefficient
9130  * \param[in]   bc_coeff_a      boundary condition term a
9131  * \param[in]   bc_coeff_b      boundary condition term b
9132  * \param[in]   var             gradient's base variable
9133  * \param[in]   c_weight        cell variable weight, or NULL
9134  * \param[in]   cpl             associated internal coupling, or NULL
9135  * \param[out]  grad            gradient
9136                                 (\f$ \der{u_i}{x_j} \f$ is gradv[][i][j])
9137  */
9138 /*----------------------------------------------------------------------------*/
9139 
9140 void
cs_gradient_vector_synced_input(const char * var_name,cs_gradient_type_t gradient_type,cs_halo_type_t halo_type,int inc,int n_r_sweeps,int verbosity,cs_gradient_limit_t clip_mode,double epsilon,double clip_coeff,const cs_real_t bc_coeff_a[][3],const cs_real_t bc_coeff_b[][3][3],const cs_real_t var[restrict][3],const cs_real_t c_weight[restrict],const cs_internal_coupling_t * cpl,cs_real_t grad[restrict][3][3])9141 cs_gradient_vector_synced_input(const char                *var_name,
9142                                 cs_gradient_type_t         gradient_type,
9143                                 cs_halo_type_t             halo_type,
9144                                 int                        inc,
9145                                 int                        n_r_sweeps,
9146                                 int                        verbosity,
9147                                 cs_gradient_limit_t        clip_mode,
9148                                 double                     epsilon,
9149                                 double                     clip_coeff,
9150                                 const cs_real_t            bc_coeff_a[][3],
9151                                 const cs_real_t            bc_coeff_b[][3][3],
9152                                 const cs_real_t            var[restrict][3],
9153                                 const cs_real_t            c_weight[restrict],
9154                                 const cs_internal_coupling_t  *cpl,
9155                                 cs_real_t                  grad[restrict][3][3])
9156 {
9157   cs_gradient_info_t *gradient_info = NULL;
9158   cs_timer_t t0, t1;
9159 
9160   bool update_stats = true;
9161 
9162   t0 = cs_timer_time();
9163 
9164   if (update_stats == true)
9165     gradient_info = _find_or_add_system(var_name, gradient_type);
9166 
9167   /* Compute gradient */
9168 
9169   _gradient_vector(var_name,
9170                    gradient_info,
9171                    gradient_type,
9172                    halo_type,
9173                    inc,
9174                    n_r_sweeps,
9175                    verbosity,
9176                    clip_mode,
9177                    epsilon,
9178                    clip_coeff,
9179                    bc_coeff_a,
9180                    bc_coeff_b,
9181                    var,
9182                    c_weight,
9183                    cpl,
9184                    grad);
9185 
9186   t1 = cs_timer_time();
9187 
9188   cs_timer_counter_add_diff(&_gradient_t_tot, &t0, &t1);
9189 
9190   if (update_stats == true) {
9191     gradient_info->n_calls += 1;
9192     cs_timer_counter_add_diff(&(gradient_info->t_tot), &t0, &t1);
9193   }
9194 
9195   if (_gradient_stat_id > -1)
9196     cs_timer_stats_add_diff(_gradient_stat_id, &t0, &t1);
9197 }
9198 
9199 /*----------------------------------------------------------------------------*/
9200 /*!
9201  * \brief  Compute cell gradient of tensor.
9202  *
9203  * This variant of the \ref cs_gradient_tensor function assumes ghost cell
9204  * values for input arrays (var and optionally c_weight)
9205  * have already been synchronized.
9206  *
9207  * \param[in]       var_name        variable name
9208  * \param[in]       gradient_type   gradient type
9209  * \param[in]       halo_type       halo type
9210  * \param[in]       inc             if 0, solve on increment; 1 otherwise
9211  * \param[in]       n_r_sweeps      if > 1, number of reconstruction sweeps
9212  *                                  (only used by CS_GRADIENT_GREEN_ITER)
9213  * \param[in]       verbosity       verbosity level
9214  * \param[in]       clip_mode       clipping mode
9215  * \param[in]       epsilon         precision for iterative gradient calculation
9216  * \param[in]       clip_coeff      clipping coefficient
9217  * \param[in]       bc_coeff_a      boundary condition term a
9218  * \param[in]       bc_coeff_b      boundary condition term b
9219  * \param[in, out]  var             gradient's base variable
9220  * \param[out]      grad            gradient
9221                                     (\f$ \der{t_ij}{x_k} \f$ is grad[][ij][k])
9222  */
9223 /*----------------------------------------------------------------------------*/
9224 
9225 void
cs_gradient_tensor_synced_input(const char * var_name,cs_gradient_type_t gradient_type,cs_halo_type_t halo_type,int inc,int n_r_sweeps,int verbosity,cs_gradient_limit_t clip_mode,double epsilon,double clip_coeff,const cs_real_t bc_coeff_a[][6],const cs_real_t bc_coeff_b[][6][6],const cs_real_t var[restrict][6],cs_real_63_t * restrict grad)9226 cs_gradient_tensor_synced_input(const char                *var_name,
9227                                 cs_gradient_type_t         gradient_type,
9228                                 cs_halo_type_t             halo_type,
9229                                 int                        inc,
9230                                 int                        n_r_sweeps,
9231                                 int                        verbosity,
9232                                 cs_gradient_limit_t        clip_mode,
9233                                 double                     epsilon,
9234                                 double                     clip_coeff,
9235                                 const cs_real_t            bc_coeff_a[][6],
9236                                 const cs_real_t            bc_coeff_b[][6][6],
9237                                 const cs_real_t            var[restrict][6],
9238                                 cs_real_63_t     *restrict grad)
9239 {
9240   cs_gradient_info_t *gradient_info = NULL;
9241   cs_timer_t t0, t1;
9242 
9243   bool update_stats = true;
9244 
9245   t0 = cs_timer_time();
9246 
9247   if (   gradient_type == CS_GRADIENT_GREEN_LSQ
9248       || gradient_type == CS_GRADIENT_GREEN_VTX)
9249     gradient_type = CS_GRADIENT_GREEN_ITER;
9250 
9251   if (update_stats == true)
9252     gradient_info = _find_or_add_system(var_name, gradient_type);
9253 
9254   /* Compute gradient */
9255 
9256   _gradient_tensor(var_name,
9257                    gradient_info,
9258                    gradient_type,
9259                    halo_type,
9260                    inc,
9261                    n_r_sweeps,
9262                    verbosity,
9263                    clip_mode,
9264                    epsilon,
9265                    clip_coeff,
9266                    bc_coeff_a,
9267                    bc_coeff_b,
9268                    var,
9269                    grad);
9270 
9271   t1 = cs_timer_time();
9272 
9273   cs_timer_counter_add_diff(&_gradient_t_tot, &t0, &t1);
9274 
9275   if (update_stats == true) {
9276     gradient_info->n_calls += 1;
9277     cs_timer_counter_add_diff(&(gradient_info->t_tot), &t0, &t1);
9278   }
9279 
9280   if (_gradient_stat_id > -1)
9281     cs_timer_stats_add_diff(_gradient_stat_id, &t0, &t1);
9282 }
9283 
9284 /*----------------------------------------------------------------------------*/
9285 /*!
9286  * \brief  Compute the gradient of a scalar field at a given cell
9287  *         using least-squares reconstruction.
9288  *
9289  * This assumes ghost cell values which might be used are already
9290  * synchronized.
9291  *
9292  * When boundary conditions are provided, both the bc_coeff_a and bc_coeff_b
9293  * arrays must be given. If boundary values are known, bc_coeff_a
9294  * can point to the boundary values array, and bc_coeff_b set to NULL.
9295  * If bc_coeff_a is NULL, bc_coeff_b is ignored.
9296  *
9297  * \param[in]   m               pointer to associated mesh structure
9298  * \param[in]   fvq             pointer to associated finite volume quantities
9299  * \param[in]   c_id            cell id
9300  * \param[in]   halo_type       halo type
9301  * \param[in]   bc_coeff_a      boundary condition term a, or NULL
9302  * \param[in]   bc_coeff_b      boundary condition term b, or NULL
9303  * \param[in]   var             gradient's base variable
9304  * \param[in]   c_weight        cell variable weight, or NULL
9305  * \param[out]  grad            gradient
9306  */
9307 /*----------------------------------------------------------------------------*/
9308 
9309 void
cs_gradient_scalar_cell(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,cs_lnum_t c_id,cs_halo_type_t halo_type,const cs_real_t bc_coeff_a[],const cs_real_t bc_coeff_b[],const cs_real_t var[],const cs_real_t c_weight[],cs_real_t grad[3])9310 cs_gradient_scalar_cell(const cs_mesh_t             *m,
9311                         const cs_mesh_quantities_t  *fvq,
9312                         cs_lnum_t                    c_id,
9313                         cs_halo_type_t               halo_type,
9314                         const cs_real_t              bc_coeff_a[],
9315                         const cs_real_t              bc_coeff_b[],
9316                         const cs_real_t              var[],
9317                         const cs_real_t              c_weight[],
9318                         cs_real_t                    grad[3])
9319 {
9320   CS_UNUSED(m);
9321 
9322   const cs_mesh_adjacencies_t *ma = cs_glob_mesh_adjacencies;
9323   const cs_lnum_t *restrict cell_cells_idx
9324     = (const cs_lnum_t *restrict) ma->cell_cells_idx;
9325   const cs_lnum_t *restrict cell_cells_e_idx
9326     = (const cs_lnum_t *restrict) ma->cell_cells_e_idx;
9327   const cs_lnum_t *restrict cell_b_faces_idx
9328     = (const cs_lnum_t *restrict) ma->cell_b_faces_idx;
9329   const cs_lnum_t *restrict cell_cells
9330     = (const cs_lnum_t *restrict) ma->cell_cells;
9331   const cs_lnum_t *restrict cell_cells_e
9332     = (const cs_lnum_t *restrict) ma->cell_cells_e;
9333   const cs_lnum_t *restrict cell_b_faces
9334     = (const cs_lnum_t *restrict) ma->cell_b_faces;
9335 
9336   const cs_real_3_t *restrict cell_cen
9337     = (const cs_real_3_t *restrict)fvq->cell_cen;
9338 
9339   /* Reconstruct gradients using least squares for non-orthogonal meshes */
9340 
9341   cs_real_t cocg[6] = {0., 0., 0., 0., 0., 0.};
9342   cs_real_t rhsv[3] = {0., 0., 0.};
9343 
9344   int n_adj = (halo_type == CS_HALO_EXTENDED) ? 2 : 1;
9345 
9346   for (int adj_id = 0; adj_id < n_adj; adj_id++) {
9347 
9348     const cs_lnum_t *restrict cell_cells_p;
9349     cs_lnum_t s_id, e_id;
9350 
9351     if (adj_id == 0){
9352       s_id = cell_cells_idx[c_id];
9353       e_id = cell_cells_idx[c_id+1];
9354       cell_cells_p = (const cs_lnum_t *restrict)(cell_cells);
9355     }
9356     else if (cell_cells_e_idx != NULL){
9357       s_id = cell_cells_e_idx[c_id];
9358       e_id = cell_cells_e_idx[c_id+1];
9359       cell_cells_p = (const cs_lnum_t *restrict)(cell_cells_e);
9360     }
9361     else
9362       break;
9363 
9364     if (c_weight == NULL) {
9365 
9366       for (cs_lnum_t i = s_id; i < e_id; i++) {
9367 
9368         cs_real_t dc[3];
9369         cs_lnum_t c_id1 = cell_cells_p[i];
9370         for (cs_lnum_t ii = 0; ii < 3; ii++)
9371           dc[ii] = cell_cen[c_id1][ii] - cell_cen[c_id][ii];
9372 
9373         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
9374 
9375         cs_real_t pfac = (var[c_id1] - var[c_id]) * ddc;
9376 
9377         for (cs_lnum_t ll = 0; ll < 3; ll++)
9378           rhsv[ll] += dc[ll] * pfac;
9379 
9380         cocg[0] += dc[0]*dc[0]*ddc;
9381         cocg[1] += dc[1]*dc[1]*ddc;
9382         cocg[2] += dc[2]*dc[2]*ddc;
9383         cocg[3] += dc[0]*dc[1]*ddc;
9384         cocg[4] += dc[1]*dc[2]*ddc;
9385         cocg[5] += dc[0]*dc[2]*ddc;
9386 
9387       }
9388 
9389     }
9390     else {
9391 
9392       for (cs_lnum_t i = s_id; i < e_id; i++) {
9393 
9394         cs_real_t dc[3];
9395         cs_lnum_t c_id1 = cell_cells_p[i];
9396         for (cs_lnum_t ii = 0; ii < 3; ii++)
9397           dc[ii] = cell_cen[c_id1][ii] - cell_cen[c_id][ii];
9398 
9399         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
9400 
9401         cs_real_t pfac = (var[c_id1] - var[c_id]) * ddc;
9402 
9403         cs_real_t _weight =   2. * c_weight[c_id1]
9404                             / (c_weight[c_id] + c_weight[c_id1]);
9405 
9406         for (cs_lnum_t ll = 0; ll < 3; ll++)
9407           rhsv[ll] += dc[ll] * pfac * _weight;
9408 
9409         cocg[0] += dc[0]*dc[0]*ddc;
9410         cocg[1] += dc[1]*dc[1]*ddc;
9411         cocg[2] += dc[2]*dc[2]*ddc;
9412         cocg[3] += dc[0]*dc[1]*ddc;
9413         cocg[4] += dc[1]*dc[2]*ddc;
9414         cocg[5] += dc[0]*dc[2]*ddc;
9415 
9416       }
9417     }
9418 
9419   } /* end of contribution from interior and extended cells */
9420 
9421   cs_lnum_t s_id = cell_b_faces_idx[c_id];
9422   cs_lnum_t e_id = cell_b_faces_idx[c_id+1];
9423 
9424   /* Contribution from boundary faces */
9425 
9426   for (cs_lnum_t i = s_id; i < e_id; i++) {
9427 
9428     const cs_real_3_t *restrict b_face_normal
9429       = (const cs_real_3_t *restrict)fvq->b_face_normal;
9430     const cs_real_t *restrict b_face_surf
9431       = (const cs_real_t *restrict)fvq->b_face_surf;
9432     const cs_real_t *restrict b_dist
9433       = (const cs_real_t *restrict)fvq->b_dist;
9434     const cs_real_3_t *restrict diipb
9435       = (const cs_real_3_t *restrict)fvq->diipb;
9436 
9437     cs_real_t  dsij[3];
9438 
9439     cs_lnum_t f_id = cell_b_faces[i];
9440 
9441     cs_real_t udbfs = 1. / b_face_surf[f_id];
9442 
9443     for (cs_lnum_t ll = 0; ll < 3; ll++)
9444       dsij[ll] = udbfs * b_face_normal[f_id][ll];
9445 
9446     if (bc_coeff_a != NULL && bc_coeff_b != NULL) { /* Known face BC's */
9447 
9448       cs_real_t unddij = 1. / b_dist[f_id];
9449       cs_real_t umcbdd = (1. -bc_coeff_b[f_id]) * unddij;
9450 
9451       cs_real_t pfac =  (  bc_coeff_a[f_id] + (bc_coeff_b[f_id] -1.)
9452                        * var[c_id]) * unddij;
9453 
9454       for (cs_lnum_t ll = 0; ll < 3; ll++)
9455         dsij[ll] += umcbdd*diipb[f_id][ll];
9456 
9457       for (cs_lnum_t ll = 0; ll < 3; ll++)
9458         rhsv[ll] += dsij[ll] * pfac;
9459 
9460       cocg[0] += dsij[0] * dsij[0];
9461       cocg[1] += dsij[1] * dsij[1];
9462       cocg[2] += dsij[2] * dsij[2];
9463       cocg[3] += dsij[0] * dsij[1];
9464       cocg[4] += dsij[1] * dsij[2];
9465       cocg[5] += dsij[0] * dsij[2];
9466 
9467     }
9468     else if (bc_coeff_a != NULL) { /* Known face values */
9469 
9470       const cs_real_3_t *restrict b_face_cog
9471         = (const cs_real_3_t *restrict)fvq->b_face_cog;
9472 
9473       cs_real_t dc[3];
9474       for (cs_lnum_t ii = 0; ii < 3; ii++)
9475         dc[ii] = b_face_cog[f_id][ii] - cell_cen[c_id][ii];
9476 
9477       cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
9478 
9479       cs_real_t pfac = (bc_coeff_a[f_id] - var[c_id]) * ddc;
9480 
9481       for (cs_lnum_t ll = 0; ll < 3; ll++)
9482         rhsv[ll] += dc[ll] * pfac;
9483 
9484       cocg[0] += dc[0]*dc[0]*ddc;
9485       cocg[1] += dc[1]*dc[1]*ddc;
9486       cocg[2] += dc[2]*dc[2]*ddc;
9487       cocg[3] += dc[0]*dc[1]*ddc;
9488       cocg[4] += dc[1]*dc[2]*ddc;
9489       cocg[5] += dc[0]*dc[2]*ddc;
9490 
9491     }
9492 
9493     else { /* Assign cell values as face values (homogeneous Neumann);
9494               as above, pfac cancels out, so does contribution to RHS */
9495 
9496       const cs_real_3_t *restrict b_face_cog
9497         = (const cs_real_3_t *restrict)fvq->b_face_cog;
9498 
9499       cs_real_t dc[3];
9500       for (cs_lnum_t ii = 0; ii < 3; ii++)
9501         dc[ii] = b_face_cog[f_id][ii] - cell_cen[c_id][ii];
9502 
9503       cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
9504 
9505       cocg[0] += dc[0]*dc[0]*ddc;
9506       cocg[1] += dc[1]*dc[1]*ddc;
9507       cocg[2] += dc[2]*dc[2]*ddc;
9508       cocg[3] += dc[0]*dc[1]*ddc;
9509       cocg[4] += dc[1]*dc[2]*ddc;
9510       cocg[5] += dc[0]*dc[2]*ddc;
9511 
9512     }
9513 
9514   } // end of contribution from boundary cells
9515 
9516   /* Invert */
9517 
9518   cs_real_t a00 = cocg[1]*cocg[2] - cocg[4]*cocg[4];
9519   cs_real_t a01 = cocg[4]*cocg[5] - cocg[3]*cocg[2];
9520   cs_real_t a02 = cocg[3]*cocg[4] - cocg[1]*cocg[5];
9521   cs_real_t a11 = cocg[0]*cocg[2] - cocg[5]*cocg[5];
9522   cs_real_t a12 = cocg[3]*cocg[5] - cocg[0]*cocg[4];
9523   cs_real_t a22 = cocg[0]*cocg[1] - cocg[3]*cocg[3];
9524 
9525   cs_real_t det_inv = 1. / (cocg[0]*a00 + cocg[3]*a01 + cocg[5]*a02);
9526 
9527   grad[0] = (  a00 * rhsv[0]
9528              + a01 * rhsv[1]
9529              + a02 * rhsv[2]) * det_inv;
9530   grad[1] = (  a01 * rhsv[0]
9531              + a11 * rhsv[1]
9532              + a12 * rhsv[2]) * det_inv;
9533   grad[2] = (  a02 * rhsv[0]
9534              + a12 * rhsv[1]
9535              + a22 * rhsv[2]) * det_inv;
9536 }
9537 
9538 /*----------------------------------------------------------------------------*/
9539 /*!
9540  * \brief  Compute the gradient of a vector field at a given cell
9541  *         using least-squares reconstruction.
9542  *
9543  * This assumes ghost cell values which might be used are already
9544  * synchronized.
9545  *
9546  * When boundary conditions are provided, both the bc_coeff_a and bc_coeff_b
9547  * arrays must be given. If boundary values are known, bc_coeff_a
9548  * can point to the boundary values array, and bc_coeff_b set to NULL.
9549  * If bc_coeff_a is NULL, bc_coeff_b is ignored.
9550  *
9551  * \param[in]   m               pointer to associated mesh structure
9552  * \param[in]   fvq             pointer to associated finite volume quantities
9553  * \param[in]   c_id            cell id
9554  * \param[in]   halo_type       halo type
9555  * \param[in]   bc_coeff_a      boundary condition term a, or NULL
9556  * \param[in]   bc_coeff_b      boundary condition term b, or NULL
9557  * \param[in]   var             gradient's base variable
9558  * \param[in]   c_weight        cell variable weight, or NULL
9559  * \param[out]  grad            gradient
9560  */
9561 /*----------------------------------------------------------------------------*/
9562 
9563 void
cs_gradient_vector_cell(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,cs_lnum_t c_id,cs_halo_type_t halo_type,const cs_real_t bc_coeff_a[][3],const cs_real_t bc_coeff_b[][3][3],const cs_real_t var[restrict][3],const cs_real_t c_weight[],cs_real_t grad[3][3])9564 cs_gradient_vector_cell(const cs_mesh_t             *m,
9565                         const cs_mesh_quantities_t  *fvq,
9566                         cs_lnum_t                    c_id,
9567                         cs_halo_type_t               halo_type,
9568                         const cs_real_t              bc_coeff_a[][3],
9569                         const cs_real_t              bc_coeff_b[][3][3],
9570                         const cs_real_t              var[restrict][3],
9571                         const cs_real_t              c_weight[],
9572                         cs_real_t                    grad[3][3])
9573 {
9574   CS_UNUSED(m);
9575 
9576   const cs_mesh_adjacencies_t *ma = cs_glob_mesh_adjacencies;
9577 
9578   const cs_lnum_t *restrict cell_cells_idx
9579     = (const cs_lnum_t *restrict) ma->cell_cells_idx;
9580   const cs_lnum_t *restrict cell_cells_e_idx
9581     = (const cs_lnum_t *restrict) ma->cell_cells_e_idx;
9582   const cs_lnum_t *restrict cell_b_faces_idx
9583     = (const cs_lnum_t *restrict) ma->cell_b_faces_idx;
9584   const cs_lnum_t *restrict cell_cells
9585     = (const cs_lnum_t *restrict) ma->cell_cells;
9586   const cs_lnum_t *restrict cell_cells_e
9587     = (const cs_lnum_t *restrict) ma->cell_cells_e;
9588   const cs_lnum_t *restrict cell_b_faces
9589     = (const cs_lnum_t *restrict) ma->cell_b_faces;
9590 
9591   const cs_real_3_t *restrict cell_cen
9592     = (const cs_real_3_t *restrict)fvq->cell_cen;
9593 
9594   /* Compute covariance matrix and Right-Hand Side */
9595 
9596   cs_real_t cocg[3][3] = {{0., 0., 0.},
9597                           {0., 0., 0.},
9598                           {0., 0., 0.}};
9599   cs_real_t rhs[3][3] = {{0., 0., 0.},
9600                          {0., 0., 0.},
9601                          {0., 0., 0.}};
9602 
9603   /* Contribution from interior and extended cells */
9604 
9605   int n_adj = (halo_type == CS_HALO_EXTENDED) ? 2 : 1;
9606 
9607   for (int adj_id = 0; adj_id < n_adj; adj_id++) {
9608 
9609     const cs_lnum_t *restrict cell_cells_p;
9610     cs_lnum_t s_id, e_id;
9611 
9612     if (adj_id == 0){
9613       s_id = cell_cells_idx[c_id];
9614       e_id = cell_cells_idx[c_id+1];
9615       cell_cells_p = (const cs_lnum_t *restrict)(cell_cells);
9616     }
9617     else if (cell_cells_e_idx != NULL){
9618       s_id = cell_cells_e_idx[c_id];
9619       e_id = cell_cells_e_idx[c_id+1];
9620       cell_cells_p = (const cs_lnum_t *restrict)(cell_cells_e);
9621     }
9622     else
9623       break;
9624 
9625     if (c_weight == NULL) {
9626 
9627       for (cs_lnum_t i = s_id; i < e_id; i++) {
9628 
9629         cs_real_t dc[3];
9630         cs_lnum_t c_id1 = cell_cells_p[i];
9631         for (cs_lnum_t ii = 0; ii < 3; ii++)
9632           dc[ii] = cell_cen[c_id1][ii] - cell_cen[c_id][ii];
9633 
9634         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
9635 
9636         for (cs_lnum_t kk = 0; kk < 3; kk++) {
9637 
9638           cs_real_t pfac = (var[c_id1][kk] - var[c_id][kk]) * ddc;
9639 
9640           for (cs_lnum_t ll = 0; ll < 3; ll++) {
9641             rhs[kk][ll] += dc[ll] * pfac;
9642             cocg[kk][ll] += dc[kk]*dc[ll]*ddc;
9643           }
9644 
9645         }
9646 
9647       }
9648 
9649     }
9650     else {
9651 
9652       for (cs_lnum_t i = s_id; i < e_id; i++) {
9653 
9654         cs_real_t dc[3];
9655         cs_lnum_t c_id1 = cell_cells_p[i];
9656         for (cs_lnum_t ii = 0; ii < 3; ii++)
9657           dc[ii] = cell_cen[c_id1][ii] - cell_cen[c_id][ii];
9658 
9659         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
9660 
9661         cs_real_t _weight =   2. * c_weight[c_id1]
9662                             / (c_weight[c_id] + c_weight[c_id1]);
9663 
9664         for (cs_lnum_t kk = 0; kk < 3; kk++) {
9665 
9666           cs_real_t pfac = (var[c_id1][kk] - var[c_id][kk]) * ddc;
9667 
9668           for (cs_lnum_t ll = 0; ll < 3; ll++) {
9669             rhs[kk][ll] += dc[ll] * pfac * _weight;
9670             cocg[kk][ll] += dc[kk]*dc[ll]*ddc;
9671           }
9672 
9673         }
9674 
9675       }
9676     }
9677 
9678   } /* end of contribution from interior and extended cells */
9679 
9680   /* Contribution from boundary conditions */
9681 
9682   cs_lnum_t s_id = cell_b_faces_idx[c_id];
9683   cs_lnum_t e_id = cell_b_faces_idx[c_id+1];
9684 
9685   if (e_id > s_id && bc_coeff_a != NULL && bc_coeff_b != NULL) {
9686 
9687     for (cs_lnum_t i = s_id; i < e_id; i++) {
9688 
9689       const cs_real_3_t *restrict b_face_normal
9690         = (const cs_real_3_t *restrict)fvq->b_face_normal;
9691       const cs_real_t *restrict b_dist
9692         = (const cs_real_t *restrict)fvq->b_dist;
9693 
9694       cs_lnum_t f_id = cell_b_faces[i];
9695 
9696       cs_real_t  n_d_dist[3];
9697       /* Normal is vector 0 if the b_face_normal norm is too small */
9698       cs_math_3_normalise(b_face_normal[f_id], n_d_dist);
9699 
9700       for (cs_lnum_t ii = 0; ii < 3; ii++) {
9701         for (cs_lnum_t jj = 0; jj < 3; jj++)
9702           cocg[ii][jj] += n_d_dist[ii] * n_d_dist[jj];
9703       }
9704 
9705       cs_real_t d_b_dist = 1. / b_dist[f_id];
9706 
9707       /* Normal divided by b_dist */
9708       for (cs_lnum_t j = 0; j < 3; j++)
9709         n_d_dist[j] *= d_b_dist;
9710 
9711       for (cs_lnum_t j = 0; j < 3; j++) {
9712         cs_real_t pfac =      bc_coeff_a[f_id][j]
9713                          + (  bc_coeff_b[f_id][0][j] * var[c_id][0]
9714                             + bc_coeff_b[f_id][1][j] * var[c_id][1]
9715                             + bc_coeff_b[f_id][2][j] * var[c_id][2]
9716                             -                          var[c_id][j]);
9717 
9718         for (cs_lnum_t k = 0; k < 3; k++)
9719           rhs[j][k] += n_d_dist[k] * pfac;
9720       }
9721 
9722     }
9723 
9724     /* Build indices bijection between [1-9] and [1-3]*[1-3] */
9725     cs_lnum_t _33_9_idx[9][2];
9726     int nn = 0;
9727     for (int ll = 0; ll < 3; ll++) {
9728       for (int mm = 0; mm < 3; mm++) {
9729         _33_9_idx[nn][0] = ll;
9730         _33_9_idx[nn][1] = mm;
9731         nn++;
9732       }
9733     }
9734 
9735     cs_real_t cocgb_v[45], rhsb_v[9], x[9];
9736 
9737     _compute_cocgb_rhsb_lsq_v(c_id,
9738                               1,
9739                               ma,
9740                               fvq,
9741                               _33_9_idx,
9742                               (const cs_real_3_t *)var,
9743                               (const cs_real_3_t *)bc_coeff_a,
9744                               (const cs_real_33_t *)bc_coeff_b,
9745                               (const cs_real_3_t *)cocg,
9746                               (const cs_real_3_t *)rhs,
9747                               cocgb_v,
9748                               rhsb_v);
9749 
9750     _fw_and_bw_ldtl_pp(cocgb_v,
9751                        9,
9752                        x,
9753                        rhsb_v);
9754 
9755     for (int kk = 0; kk < 9; kk++) {
9756       int ii = _33_9_idx[kk][0];
9757       int jj = _33_9_idx[kk][1];
9758       grad[ii][jj] = x[kk];
9759     }
9760   }
9761 
9762   /* Case with no boundary conditions or known face values */
9763 
9764   else {
9765 
9766     if (bc_coeff_a != NULL) {
9767 
9768       for (cs_lnum_t i = s_id; i < e_id; i++) {
9769 
9770         const cs_real_3_t *restrict b_face_cog
9771           = (const cs_real_3_t *restrict)fvq->b_face_cog;
9772 
9773         cs_lnum_t f_id = cell_b_faces[i];
9774 
9775         cs_real_t dc[3];
9776         for (cs_lnum_t ii = 0; ii < 3; ii++)
9777           dc[ii] = b_face_cog[f_id][ii] - cell_cen[c_id][ii];
9778 
9779         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
9780 
9781         for (cs_lnum_t kk = 0; kk < 3; kk++) {
9782 
9783           cs_real_t pfac = (bc_coeff_a[f_id][kk] - var[c_id][kk]) * ddc;
9784 
9785           for (cs_lnum_t ll = 0; ll < 3; ll++) {
9786             rhs[kk][ll] += dc[ll] * pfac;
9787             cocg[kk][ll] += dc[kk]*dc[ll]*ddc;
9788           }
9789 
9790         }
9791 
9792       }
9793 
9794     }
9795 
9796     else { /* if bc_coeff_a == NUL
9797               use homogeneous Neumann BC; as above, but
9798               pfac and RHS contribution become zero */
9799 
9800       for (cs_lnum_t i = s_id; i < e_id; i++) {
9801 
9802         const cs_real_3_t *restrict b_face_cog
9803           = (const cs_real_3_t *restrict)fvq->b_face_cog;
9804 
9805         cs_lnum_t f_id = cell_b_faces[i];
9806 
9807         cs_real_t dc[3];
9808         for (cs_lnum_t ii = 0; ii < 3; ii++)
9809           dc[ii] = b_face_cog[f_id][ii] - cell_cen[c_id][ii];
9810 
9811         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
9812 
9813         for (cs_lnum_t kk = 0; kk < 3; kk++) {
9814           for (cs_lnum_t ll = 0; ll < 3; ll++)
9815             cocg[kk][ll] += dc[kk]*dc[ll]*ddc;
9816         }
9817 
9818       }
9819 
9820     }
9821 
9822     /* Invert */
9823 
9824     cs_math_33_inv_cramer_in_place(cocg);
9825 
9826     for (cs_lnum_t jj = 0; jj < 3; jj++) {
9827       for (cs_lnum_t ii = 0; ii < 3; ii++) {
9828         grad[ii][jj] = 0.0;
9829         for (cs_lnum_t k = 0; k < 3; k++)
9830           grad[ii][jj] += rhs[ii][k] * cocg[k][jj];
9831 
9832       }
9833     }
9834   }
9835 
9836 }
9837 
9838 /*----------------------------------------------------------------------------*/
9839 /*!
9840  * \brief  Compute the gradient of a tensor field at a given cell
9841  *         using least-squares reconstruction.
9842  *
9843  * This assumes ghost cell values which might be used are already
9844  * synchronized.
9845  *
9846  * When boundary conditions are provided, both the bc_coeff_a and bc_coeff_b
9847  * arrays must be given. If boundary values are known, bc_coeff_a
9848  * can point to the boundary values array, and bc_coeff_b set to NULL.
9849  * If bc_coeff_a is NULL, bc_coeff_b is ignored.
9850  *
9851  * \param[in]   m               pointer to associated mesh structure
9852  * \param[in]   fvq             pointer to associated finite volume quantities
9853  * \param[in]   c_id            cell id
9854  * \param[in]   halo_type       halo type
9855  * \param[in]   bc_coeff_a      boundary condition term a, or NULL
9856  * \param[in]   bc_coeff_b      boundary condition term b, or NULL
9857  * \param[in]   var             gradient's base variable
9858  * \param[in]   c_weight        cell variable weight, or NULL
9859  * \param[out]  grad            gradient
9860  */
9861 /*----------------------------------------------------------------------------*/
9862 
9863 void
cs_gradient_tensor_cell(const cs_mesh_t * m,const cs_mesh_quantities_t * fvq,cs_lnum_t c_id,cs_halo_type_t halo_type,const cs_real_t bc_coeff_a[][6],const cs_real_t bc_coeff_b[][6][6],const cs_real_t var[restrict][6],const cs_real_t c_weight[],cs_real_t grad[6][3])9864 cs_gradient_tensor_cell(const cs_mesh_t             *m,
9865                         const cs_mesh_quantities_t  *fvq,
9866                         cs_lnum_t                    c_id,
9867                         cs_halo_type_t               halo_type,
9868                         const cs_real_t              bc_coeff_a[][6],
9869                         const cs_real_t              bc_coeff_b[][6][6],
9870                         const cs_real_t              var[restrict][6],
9871                         const cs_real_t              c_weight[],
9872                         cs_real_t                    grad[6][3])
9873 {
9874   CS_UNUSED(m);
9875 
9876   const cs_mesh_adjacencies_t *ma = cs_glob_mesh_adjacencies;
9877 
9878   const cs_lnum_t *restrict cell_cells_idx
9879     = (const cs_lnum_t *restrict) ma->cell_cells_idx;
9880   const cs_lnum_t *restrict cell_cells_e_idx
9881     = (const cs_lnum_t *restrict) ma->cell_cells_e_idx;
9882   const cs_lnum_t *restrict cell_b_faces_idx
9883     = (const cs_lnum_t *restrict) ma->cell_b_faces_idx;
9884   const cs_lnum_t *restrict cell_cells
9885     = (const cs_lnum_t *restrict) ma->cell_cells;
9886   const cs_lnum_t *restrict cell_cells_e
9887     = (const cs_lnum_t *restrict) ma->cell_cells_e;
9888   const cs_lnum_t *restrict cell_b_faces
9889     = (const cs_lnum_t *restrict) ma->cell_b_faces;
9890 
9891   const cs_real_3_t *restrict cell_cen
9892     = (const cs_real_3_t *restrict)fvq->cell_cen;
9893 
9894   /* Compute covariance matrix and Right-Hand Side */
9895 
9896   cs_real_t cocg[3][3] = {{0., 0., 0.},
9897                           {0., 0., 0.},
9898                           {0., 0., 0.}};
9899   cs_real_t rhs[6][3] = {{0., 0., 0.}, {0., 0., 0.},
9900                          {0., 0., 0.}, {0., 0., 0.},
9901                          {0., 0., 0.}, {0., 0., 0.}};
9902 
9903   /* Contribution from interior and extended cells */
9904 
9905   int n_adj = (halo_type == CS_HALO_EXTENDED) ? 2 : 1;
9906 
9907   for (int adj_id = 0; adj_id < n_adj; adj_id++) {
9908 
9909     const cs_lnum_t *restrict cell_cells_p;
9910     cs_lnum_t s_id, e_id;
9911 
9912     if (adj_id == 0){
9913       s_id = cell_cells_idx[c_id];
9914       e_id = cell_cells_idx[c_id+1];
9915       cell_cells_p = (const cs_lnum_t *restrict)(cell_cells);
9916     }
9917     else if (cell_cells_e_idx != NULL){
9918       s_id = cell_cells_e_idx[c_id];
9919       e_id = cell_cells_e_idx[c_id+1];
9920       cell_cells_p = (const cs_lnum_t *restrict)(cell_cells_e);
9921     }
9922     else
9923       break;
9924 
9925     if (c_weight == NULL) {
9926 
9927       for (cs_lnum_t i = s_id; i < e_id; i++) {
9928 
9929         cs_real_t dc[3];
9930         cs_lnum_t c_id1 = cell_cells_p[i];
9931         for (cs_lnum_t ii = 0; ii < 3; ii++)
9932           dc[ii] = cell_cen[c_id1][ii] - cell_cen[c_id][ii];
9933 
9934         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
9935 
9936         for (cs_lnum_t kk = 0; kk < 3; kk++) {
9937           for (cs_lnum_t ll = 0; ll < 3; ll++)
9938             cocg[kk][ll] += dc[kk]*dc[ll]*ddc;
9939         }
9940 
9941         for (cs_lnum_t kk = 0; kk < 6; kk++) {
9942           cs_real_t pfac = (var[c_id1][kk] - var[c_id][kk]) * ddc;
9943           for (cs_lnum_t ll = 0; ll < 3; ll++)
9944             rhs[kk][ll] += dc[ll] * pfac;
9945         }
9946 
9947       }
9948 
9949     }
9950     else {
9951 
9952       for (cs_lnum_t i = s_id; i < e_id; i++) {
9953 
9954         cs_real_t dc[3];
9955         cs_lnum_t c_id1 = cell_cells_p[i];
9956         for (cs_lnum_t ii = 0; ii < 3; ii++)
9957           dc[ii] = cell_cen[c_id1][ii] - cell_cen[c_id][ii];
9958 
9959         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
9960 
9961         cs_real_t _weight =   2. * c_weight[c_id1]
9962                             / (c_weight[c_id] + c_weight[c_id1]);
9963 
9964         for (cs_lnum_t kk = 0; kk < 3; kk++) {
9965           for (cs_lnum_t ll = 0; ll < 3; ll++)
9966             cocg[kk][ll] += dc[kk]*dc[ll]*ddc;
9967         }
9968 
9969         for (cs_lnum_t kk = 0; kk < 6; kk++) {
9970           cs_real_t pfac = (var[c_id1][kk] - var[c_id][kk]) * ddc;
9971           for (cs_lnum_t ll = 0; ll < 3; ll++)
9972             rhs[kk][ll] += dc[ll] * pfac * _weight;
9973         }
9974 
9975       }
9976     }
9977 
9978   } /* end of contribution from interior and extended cells */
9979 
9980   /* Contribution from boundary conditions */
9981 
9982   cs_lnum_t s_id = cell_b_faces_idx[c_id];
9983   cs_lnum_t e_id = cell_b_faces_idx[c_id+1];
9984 
9985   if (e_id > s_id && bc_coeff_a != NULL && bc_coeff_b != NULL) {
9986 
9987     for (cs_lnum_t i = s_id; i < e_id; i++) {
9988 
9989       const cs_real_3_t *restrict b_face_normal
9990         = (const cs_real_3_t *restrict)fvq->b_face_normal;
9991       const cs_real_t *restrict b_dist
9992         = (const cs_real_t *restrict)fvq->b_dist;
9993 
9994       cs_lnum_t f_id = cell_b_faces[i];
9995 
9996       cs_real_t  n_d_dist[3];
9997       /* Normal is vector 0 if the b_face_normal norm is too small */
9998       cs_math_3_normalise(b_face_normal[f_id], n_d_dist);
9999 
10000       for (cs_lnum_t ii = 0; ii < 3; ii++) {
10001         for (cs_lnum_t jj = 0; jj < 3; jj++)
10002           cocg[ii][jj] += n_d_dist[ii] * n_d_dist[jj];
10003       }
10004 
10005       cs_real_t d_b_dist = 1. / b_dist[f_id];
10006 
10007       /* Normal divided by b_dist */
10008       for (cs_lnum_t j = 0; j < 3; j++)
10009         n_d_dist[j] *= d_b_dist;
10010 
10011       for (cs_lnum_t k = 0; k < 6; k++) {
10012         cs_real_t pfac = bc_coeff_a[f_id][k] - var[c_id][k];
10013         for (cs_lnum_t j = 0; j < 6; j++)
10014           pfac += bc_coeff_b[f_id][j][k] * var[c_id][j];
10015 
10016         for (cs_lnum_t j = 0; j < 3; j++)
10017           rhs[k][j] += pfac * n_d_dist[j];
10018       }
10019 
10020     }
10021 
10022     /* Build indices bijection between [1-18] and [1-6]*[1-3] */
10023     cs_lnum_t _63_18_idx[18][2];
10024     cs_lnum_t nn = 0;
10025     for (cs_lnum_t ll = 0; ll < 6; ll++) {
10026       for (cs_lnum_t mm = 0; mm < 3; mm++) {
10027         _63_18_idx[nn][0] = ll;
10028         _63_18_idx[nn][1] = mm;
10029         nn++;
10030       }
10031     }
10032 
10033     cs_real_t cocgb_t[171], rhsb_t[18], x[18];
10034 
10035     _compute_cocgb_rhsb_lsq_t(c_id,
10036                               1,
10037                               ma,
10038                               fvq,
10039                               _63_18_idx,
10040                               (const cs_real_6_t *)var,
10041                               (const cs_real_6_t *)bc_coeff_a,
10042                               (const cs_real_66_t *)bc_coeff_b,
10043                               (const cs_real_3_t *)cocg,
10044                               (const cs_real_3_t *)rhs,
10045                               cocgb_t,
10046                               rhsb_t);
10047 
10048     _fw_and_bw_ldtl_pp(cocgb_t,
10049                        18,
10050                        x,
10051                        rhsb_t);
10052 
10053     for (int kk = 0; kk < 18; kk++) {
10054       int ii = _63_18_idx[kk][0];
10055       int jj = _63_18_idx[kk][1];
10056       grad[ii][jj] = x[kk];
10057     }
10058 
10059   }
10060 
10061   /* Case with no boundary conditions or known face values */
10062 
10063   else {
10064 
10065     if (bc_coeff_a != NULL) {
10066 
10067       for (cs_lnum_t i = s_id; i < e_id; i++) {
10068 
10069         const cs_real_3_t *restrict b_face_cog
10070           = (const cs_real_3_t *restrict)fvq->b_face_cog;
10071 
10072         cs_lnum_t f_id = cell_b_faces[i];
10073 
10074         cs_real_t dc[3];
10075         for (cs_lnum_t ii = 0; ii < 3; ii++)
10076           dc[ii] = b_face_cog[f_id][ii] - cell_cen[c_id][ii];
10077 
10078         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
10079 
10080         for (cs_lnum_t kk = 0; kk < 3; kk++) {
10081           for (cs_lnum_t ll = 0; ll < 3; ll++)
10082             cocg[kk][ll] += dc[kk]*dc[ll]*ddc;
10083         }
10084 
10085         for (cs_lnum_t kk = 0; kk < 6; kk++) {
10086           cs_real_t pfac = (bc_coeff_a[f_id][kk] - var[c_id][kk]) * ddc;
10087           for (cs_lnum_t ll = 0; ll < 3; ll++)
10088             rhs[kk][ll] += dc[ll] * pfac;
10089         }
10090 
10091       }
10092 
10093     }
10094 
10095     else { /* Use homogeneous Neumann;
10096               pfac and RHS contribution cancel out */
10097 
10098       for (cs_lnum_t i = s_id; i < e_id; i++) {
10099 
10100         const cs_real_3_t *restrict b_face_cog
10101           = (const cs_real_3_t *restrict)fvq->b_face_cog;
10102 
10103         cs_lnum_t f_id = cell_b_faces[i];
10104 
10105         cs_real_t dc[3];
10106         for (cs_lnum_t ii = 0; ii < 3; ii++)
10107           dc[ii] = b_face_cog[f_id][ii] - cell_cen[c_id][ii];
10108 
10109         cs_real_t ddc = 1. / (dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]);
10110 
10111         for (cs_lnum_t kk = 0; kk < 3; kk++) {
10112           for (cs_lnum_t ll = 0; ll < 3; ll++)
10113             cocg[kk][ll] += dc[kk]*dc[ll]*ddc;
10114         }
10115 
10116       }
10117 
10118     }
10119 
10120     /* Invert */
10121 
10122     cs_math_33_inv_cramer_in_place(cocg);
10123 
10124     for (cs_lnum_t jj = 0; jj < 3; jj++) {
10125       for (cs_lnum_t ii = 0; ii < 6; ii++) {
10126         grad[ii][jj] = 0.0;
10127         for (cs_lnum_t k = 0; k < 3; k++)
10128           grad[ii][jj] += rhs[ii][k] * cocg[k][jj];
10129 
10130       }
10131     }
10132   }
10133 
10134 }
10135 
10136 /*----------------------------------------------------------------------------
10137  * Determine gradient type by Fortran "imrgra" value
10138  *
10139  * parameters:
10140  *   imrgra         <-- Fortran gradient option
10141  *   gradient_type  --> gradient type
10142  *   halo_type      --> halo type
10143  *----------------------------------------------------------------------------*/
10144 
10145 void
cs_gradient_type_by_imrgra(int imrgra,cs_gradient_type_t * gradient_type,cs_halo_type_t * halo_type)10146 cs_gradient_type_by_imrgra(int                  imrgra,
10147                            cs_gradient_type_t  *gradient_type,
10148                            cs_halo_type_t      *halo_type)
10149 {
10150   *halo_type = CS_HALO_STANDARD;
10151   *gradient_type = CS_GRADIENT_GREEN_ITER;
10152 
10153   switch (imrgra) {
10154   case 0:
10155     *gradient_type = CS_GRADIENT_GREEN_ITER;
10156     break;
10157   case 1:
10158     *gradient_type = CS_GRADIENT_LSQ;
10159     break;
10160   case 2:
10161   case 3:
10162     *gradient_type = CS_GRADIENT_LSQ;
10163     *halo_type = CS_HALO_EXTENDED;
10164     break;
10165   case 4:
10166     *gradient_type = CS_GRADIENT_GREEN_LSQ;
10167     break;
10168   case 5:
10169   case 6:
10170     *gradient_type = CS_GRADIENT_GREEN_LSQ;
10171     *halo_type = CS_HALO_EXTENDED;
10172     break;
10173   case 7:
10174     *gradient_type = CS_GRADIENT_GREEN_VTX;
10175     break;
10176   default:
10177     *gradient_type = CS_GRADIENT_GREEN_ITER;
10178     break;
10179   }
10180 }
10181 
10182 /*----------------------------------------------------------------------------*/
10183 /*!
10184  * \brief compute the steady balance due to porous modelling for the pressure
10185  *        gradient.
10186  *
10187  * \param[in]  inc  if 0, solve on increment; 1 otherwise
10188  */
10189 /*----------------------------------------------------------------------------*/
10190 
10191 void
cs_gradient_porosity_balance(int inc)10192 cs_gradient_porosity_balance(int inc)
10193 {
10194   const cs_mesh_t  *m = cs_glob_mesh;
10195   cs_mesh_quantities_t  *mq = cs_glob_mesh_quantities;
10196   const cs_halo_t  *halo = m->halo;
10197 
10198   const cs_real_t *restrict cell_f_vol = mq->cell_f_vol;
10199   cs_real_2_t *i_f_face_factor = mq->i_f_face_factor;
10200   cs_real_t *b_f_face_factor = mq->b_f_face_factor;
10201   cs_real_t *i_massflux = cs_field_by_name("inner_mass_flux")->val;
10202   cs_real_t *b_massflux = cs_field_by_name("boundary_mass_flux")->val;
10203   const cs_real_3_t *restrict i_face_normal
10204     = (const cs_real_3_t *restrict)mq->i_face_normal;
10205   const cs_real_3_t *restrict i_f_face_normal
10206     = (const cs_real_3_t *restrict)mq->i_f_face_normal;
10207   const cs_real_3_t *restrict b_face_normal
10208     = (const cs_real_3_t *restrict)mq->b_face_normal;
10209   const cs_real_3_t *restrict b_f_face_normal
10210     = (const cs_real_3_t *restrict)mq->b_f_face_normal;
10211   const cs_lnum_2_t *restrict i_face_cells
10212     = (const cs_lnum_2_t *restrict)m->i_face_cells;
10213   const cs_lnum_t *restrict b_face_cells
10214     = (const cs_lnum_t *restrict)m->b_face_cells;
10215   const cs_real_t *restrict i_f_face_surf = mq->i_f_face_surf;
10216   const cs_real_t *restrict i_face_surf = mq->i_face_surf;
10217   const cs_real_t *restrict b_f_face_surf = mq->b_f_face_surf;
10218   const cs_real_t *restrict b_face_surf = mq->b_face_surf;
10219 
10220   const int *restrict c_disable_flag = mq->c_disable_flag;
10221   cs_lnum_t has_dc = mq->has_disable_flag; /* Has cells disabled? */
10222 
10223   const cs_lnum_t n_cells_ext = m->n_cells_with_ghosts;
10224   const cs_lnum_t n_cells = m->n_cells;
10225 
10226   const int n_i_groups = m->i_face_numbering->n_groups;
10227   const int n_i_threads = m->i_face_numbering->n_threads;
10228   const int n_b_groups = m->b_face_numbering->n_groups;
10229   const int n_b_threads = m->b_face_numbering->n_threads;
10230   const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
10231   const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
10232 
10233   /*Additional terms due to porosity */
10234   cs_field_t *f_i_poro_duq_0 = cs_field_by_name_try("i_poro_duq_0");
10235 
10236   if (f_i_poro_duq_0 == NULL)
10237     return;
10238 
10239   cs_real_t *i_poro_duq_0 = f_i_poro_duq_0->val;
10240   cs_real_t *i_poro_duq_1 = cs_field_by_name("i_poro_duq_1")->val;
10241   cs_real_t *b_poro_duq = cs_field_by_name("b_poro_duq")->val;
10242   cs_real_3_t *c_poro_div_duq
10243     = (cs_real_3_t *restrict)cs_field_by_name("poro_div_duq")->val;
10244 
10245 # pragma omp parallel for
10246   for (cs_lnum_t c_id = 0; c_id < n_cells_ext; c_id++) {
10247     for (cs_lnum_t i = 0; i < 3; i++)
10248       c_poro_div_duq[c_id][i] = 0.;
10249   }
10250 
10251   if (inc == 1) {
10252 
10253     /* Inner faces corrections */
10254     for (int g_id = 0; g_id < n_i_groups; g_id++) {
10255 
10256 #     pragma omp parallel for
10257       for (int t_id = 0; t_id < n_i_threads; t_id++) {
10258 
10259         for (cs_lnum_t f_id = i_group_index[(t_id*n_i_groups + g_id)*2];
10260              f_id < i_group_index[(t_id*n_i_groups + g_id)*2 + 1];
10261              f_id++) {
10262 
10263           cs_lnum_t ii = i_face_cells[f_id][0];
10264           cs_lnum_t jj = i_face_cells[f_id][1];
10265 
10266           cs_real_3_t normal;
10267 
10268           cs_math_3_normalise(i_face_normal[f_id], normal);
10269 
10270           cs_real_t *vel_i = &(CS_F_(vel)->val_pre[3*ii]);
10271           cs_real_t *vel_j = &(CS_F_(vel)->val_pre[3*jj]);
10272 
10273           cs_real_t veli_dot_n =    (1. - i_f_face_factor[f_id][0])
10274                                   * cs_math_3_dot_product(vel_i, normal);
10275           cs_real_t velj_dot_n =    (1. - i_f_face_factor[f_id][1])
10276                                   * cs_math_3_dot_product(vel_j, normal);
10277 
10278           cs_real_t d_f_surf = 0.;
10279           /* Is the cell disabled (for solid or porous)?
10280              Not the case if coupled */
10281           if (   has_dc * c_disable_flag[has_dc * ii] == 0
10282               && has_dc * c_disable_flag[has_dc * jj] == 0)
10283             d_f_surf = 1. / CS_MAX(i_f_face_surf[f_id],
10284                                    cs_math_epzero * i_face_surf[f_id]);
10285 
10286           i_poro_duq_0[f_id] = veli_dot_n * i_massflux[f_id] * d_f_surf;
10287           i_poro_duq_1[f_id] = velj_dot_n * i_massflux[f_id] * d_f_surf;
10288 
10289           for (cs_lnum_t i = 0; i < 3; i++) {
10290             c_poro_div_duq[ii][i] +=   i_poro_duq_0[f_id]
10291                                      * i_f_face_normal[f_id][i];
10292             c_poro_div_duq[jj][i] -=   i_poro_duq_1[f_id]
10293                                     * i_f_face_normal[f_id][i];
10294           }
10295         }
10296       }
10297 
10298     }
10299 
10300     /* Boundary faces corrections */
10301     for (int g_id = 0; g_id < n_b_groups; g_id++) {
10302 
10303 #     pragma omp parallel for
10304       for (int t_id = 0; t_id < n_b_threads; t_id++) {
10305 
10306         for (cs_lnum_t f_id = b_group_index[(t_id*n_b_groups + g_id)*2];
10307              f_id < b_group_index[(t_id*n_b_groups + g_id)*2 + 1];
10308              f_id++) {
10309 
10310           cs_lnum_t ii = b_face_cells[f_id];
10311 
10312           cs_real_3_t normal;
10313 
10314           cs_math_3_normalise(b_face_normal[f_id], normal);
10315 
10316           cs_real_t *vel_i = &(CS_F_(vel)->val_pre[3*ii]);
10317 
10318           cs_real_t veli_dot_n =   (1. - b_f_face_factor[f_id])
10319                                  * cs_math_3_dot_product(vel_i, normal);
10320 
10321           cs_real_t d_f_surf = 0.;
10322           /* Is the cell disabled (for solid or porous)?
10323              Not the case if coupled */
10324           if (has_dc * c_disable_flag[has_dc * ii] == 0)
10325             d_f_surf = 1. / CS_MAX(b_f_face_surf[f_id],
10326                                    cs_math_epzero * b_face_surf[f_id]);
10327 
10328           b_poro_duq[f_id] = veli_dot_n * b_massflux[f_id] * d_f_surf;
10329 
10330           for (cs_lnum_t i = 0; i < 3; i++)
10331             c_poro_div_duq[ii][i] +=   b_poro_duq[f_id]
10332                                      * b_f_face_normal[f_id][i];
10333         }
10334 
10335         /* Finalisation of cell terms */
10336         for (cs_lnum_t c_id = 0; c_id < n_cells; c_id++) {
10337           /* Is the cell disabled (for solid or porous)?
10338              Not the case if coupled */
10339           cs_real_t dvol = 0.;
10340           if (has_dc * c_disable_flag[has_dc * c_id] == 0)
10341             dvol = 1. / cell_f_vol[c_id];
10342 
10343           for (cs_lnum_t i = 0; i < 3; i++)
10344             c_poro_div_duq[c_id][i] *= dvol;
10345         }
10346       }
10347     }
10348 
10349     /* Handle parallelism and periodicity */
10350     if (halo != NULL)
10351       cs_halo_sync_var_strided(halo,
10352                                CS_HALO_STANDARD,
10353                                (cs_real_t *)c_poro_div_duq,
10354                                3);
10355 
10356   }
10357   else {
10358 #   pragma omp parallel for
10359     for (cs_lnum_t f_id = 0; f_id < m->n_i_faces; f_id++) {
10360       i_poro_duq_0[f_id] = 0.;
10361       i_poro_duq_1[f_id] = 0.;
10362     }
10363 
10364   }
10365 }
10366 
10367 /*----------------------------------------------------------------------------*/
10368 
10369 END_C_DECLS
10370