1 /*============================================================================
2  * Management of the post-processing
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 <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 
39 /*----------------------------------------------------------------------------
40  * Local headers
41  *----------------------------------------------------------------------------*/
42 
43 #include "bft_mem.h"
44 #include "bft_printf.h"
45 
46 #include "fvm_nodal.h"
47 #include "fvm_nodal_append.h"
48 #include "fvm_nodal_extract.h"
49 
50 #include "cs_base.h"
51 #include "cs_boundary_zone.h"
52 #include "cs_field.h"
53 #include "cs_file.h"
54 #include "cs_lagr_extract.h"
55 #include "cs_log.h"
56 #include "cs_lagr_query.h"
57 #include "cs_mesh.h"
58 #include "cs_mesh_connect.h"
59 #include "cs_mesh_location.h"
60 #include "cs_parall.h"
61 #include "cs_prototypes.h"
62 #include "cs_selector.h"
63 #include "cs_time_control.h"
64 #include "cs_timer.h"
65 #include "cs_timer_stats.h"
66 #include "cs_volume_zone.h"
67 
68 /*----------------------------------------------------------------------------
69  * Header for the current file
70  *----------------------------------------------------------------------------*/
71 
72 #include "cs_post.h"
73 
74 /*----------------------------------------------------------------------------*/
75 
76 BEGIN_C_DECLS
77 
78 /*=============================================================================
79  * Additional doxygen documentation
80  *============================================================================*/
81 
82 /*!
83   \file cs_post.c
84 
85   \brief Post-processing management.
86 
87   \var  CS_POST_ON_LOCATION
88         postprocess variables on their base location (volume for variables)
89   \var  CS_POST_BOUNDARY_NR
90         postprocess boundary without reconstruction
91 
92   \enum cs_post_type_t
93 
94   \brief Postprocessing input variable type
95 
96   \var CS_POST_TYPE_cs_real_t
97        Fortran double precision
98   \var CS_POST_TYPE_int
99        integer
100   \var CS_POST_TYPE_float
101        single precision floating-point value
102   \var CS_POST_TYPE_double
103        double precision floating-point value
104 
105   \typedef  cs_post_elt_select_t
106 
107   \brief  Function pointer to elements selection definition
108 
109   Each function of this sort may be used to select a given type of element,
110   usually cells, interior faces, or boundary faces.
111 
112   If non-empty and not containing all elements, a list of elements of the
113   main mesh should be allocated (using BFT_MALLOC) and defined by this
114   function when called. This list's lifecycle is then managed by the
115   postprocessing subsystem.
116 
117    Note: if the input pointer is non-NULL, it must point to valid data
118    when the selection function is called, so either:
119    - that value or structure should not be temporary (i.e. local);
120    - post-processing output must be ensured using cs_post_write_meshes()
121    with a fixed-mesh writer before the data pointed to goes out of scope;
122 
123   \param[in, out]  input     pointer to optional (untyped) value or structure
124   \param[out]      n_elts    number of selected elements
125   \param[out]      elt_list  list of selected elements (0 to n-1 numbering)
126 
127   \typedef  cs_post_time_dep_output_t
128 
129   Function pointer associated with a specific post-processing output.
130 
131   Such functions are registered using the \ref cs_post_add_time_dep_output,
132   and all registered functions are automatically called by
133   \ref cs_post_write_vars.
134 
135   Note: if the input pointer is non-NULL, it must point to valid data
136   when the output function is called, so either:
137   - that value or structure should not be temporary (i.e. local);
138   - post-processing output must be ensured using cs_post_write_var()
139   or similar before the data pointed to goes out of scope.
140 
141   \param[in, out]  input       pointer to optional (untyped) value or structure
142   \param[in]       nt_cur_abs  current time step number
143   \param[in]       t_cur_abs   absolute time at the current time step
144 
145   \typedef cs_post_time_mesh_dep_output_t
146 
147   Function pointer associated with a specific post-processing output
148   on multiple meshes.
149 
150   Such functions are registered using the cs_post_add_time_mesh_dep_vars(),
151   and all registered functions are automatically called by
152   cs_post_write_vars().
153 
154   Note: if the input pointer is non-NULL, it must point to valid data
155   when the output function is called, so either:
156   - that value or structure should not be temporary (i.e. local);
157   - post-processing output must be ensured using cs_post_write_var()
158   or similar before the data pointed to goes out of scope.
159 
160   \param[in, out]  input        pointer to optional (untyped) value or structure
161   \param[in]       mesh_id      id of the output mesh for the current call
162   \param[in]       cat_id       category id of the output mesh for the
163                                 current call
164   \param[in]       ent_flag     indicate global presence of cells
165                                 (ent_flag[0]), interior faces (ent_flag[1]),
166                                 boundary faces (ent_flag[2]), particles
167                                 (ent_flag[3]) or probes (ent_flag[4])
168   \param[in]       n_cells      local number of cells of post_mesh
169   \param[in]       n_i_faces    local number of interior faces of post_mesh
170   \param[in]       n_b_faces    local number of boundary faces of post_mesh
171   \param[in]       cell_list    list of cells (1 to n) of post-processing mesh
172   \param[in]       i_face_list  list of interior faces (1 to n) of
173                                 post-processing mesh
174   \param[in]       b_face_list  list of boundary faces (1 to n) of
175                                 post-processing mesh
176   \param[in]       nt_cur_abs   current time step number
177   \param[in]       t_cur_abs    current physical time
178   \param[in]       nt_cur_abs   current time step number
179   \param[in]       t_cur_abs    absolute time at the current time step
180 */
181 
182 /*! \cond DOXYGEN_SHOULD_SKIP_THIS */
183 
184 /*=============================================================================
185  * Local Macro Definitions
186  *============================================================================*/
187 
188 #define _MIN_RESERVED_MESH_ID    CS_POST_MESH_PROBES
189 #define _MIN_RESERVED_WRITER_ID  CS_POST_WRITER_HISTOGRAMS
190 
191 /*============================================================================
192  * Type definitions
193  *============================================================================*/
194 
195 /* Specific (forced) writer output times */
196 /*---------------------------------------*/
197 
198 typedef struct {
199 
200   int      n_t_steps_max ;   /* Max. number of forced time steps */
201   int      n_t_vals_max;     /* Max. number of forced time values */
202 
203   int      n_t_steps;        /* Number of forced time steps */
204   int      n_t_vals;         /* Number of forced time values */
205 
206   int     *t_steps;          /* Forced output time steps (unordered) */
207   double  *t_vals;           /* Forced output time values (unordered) */
208 
209 } cs_post_writer_times_t;
210 
211 /* Writer structure definition parameters */
212 /*----------------------------------------*/
213 
214 typedef struct {
215 
216   fvm_writer_time_dep_t   time_dep;     /* Time dependency */
217   int                     fmt_id;       /* format id */
218   char                   *case_name;    /* Case (writer) name */
219   char                   *dir_name;     /* Associated directory name */
220   char                   *fmt_opts;     /* Format options */
221 
222 } cs_post_writer_def_t;
223 
224 /* Mesh location type */
225 /*--------------------*/
226 
227 typedef enum {
228 
229   CS_POST_LOCATION_CELL,         /* Values located at cells */
230   CS_POST_LOCATION_I_FACE,       /* Values located at interior faces */
231   CS_POST_LOCATION_B_FACE,       /* Values located at boundary faces */
232   CS_POST_LOCATION_VERTEX,       /* Values located at vertices */
233   CS_POST_LOCATION_PARTICLE,     /* Values located at particles */
234 
235 } cs_post_location_t;
236 
237 /* Writer structure */
238 /*------------------*/
239 
240 /* This object is based on a choice of a case, directory, and format,
241    as well as a flag for associated mesh's time dependency, and the default
242    output interval for associated variables. */
243 
244 typedef struct {
245 
246   int            id;            /* Identifier (< 0 for "reservable" writer,
247                                  * > 0 for user writer */
248   int            active;        /* -1 if blocked at this stage,
249                                    0 if no output at current time step,
250                                    1 in case of output */
251   cs_time_control_t        tc;  /* Time control sub-structure */
252 
253 
254   cs_post_writer_times_t  *ot;  /* Specific output times */
255   cs_post_writer_def_t    *wd;  /* Associated writer definition */
256 
257   fvm_writer_t  *writer;        /* Associated FVM writer */
258 
259 } cs_post_writer_t;
260 
261 /* Post-processing mesh structure */
262 /*--------------------------------*/
263 
264 /* This object manages the link between an exportable mesh and
265    associated writers. */
266 
267 typedef struct {
268 
269   int                     id;            /* Identifier (< 0 for "reservable"
270                                             mesh, > 0 for user mesh */
271 
272   char                   *name;          /* Mesh name */
273   char                   *criteria[5];   /* Base selection criteria for
274                                             cells, interior faces,
275                                             boundary faces, and particles
276                                             respectively */
277   cs_post_elt_select_t   *sel_func[5];   /* Advanced selection functions for
278                                             cells, interior faces,
279                                             boundary faces, particles and
280                                             probes respectively */
281   void                   *sel_input[5];  /* Advanced selection input for
282                                             matching selection functions */
283   int                     ent_flag[5];   /* Presence of cells (ent_flag[0],
284                                             interior faces (ent_flag[1]),
285                                             boundary faces (ent_flag[2]),
286                                             or particles (ent_flag[3] = 1
287                                             for particles, 2 for trajectories),
288                                             probes (ent_flag[4] = 1 for
289                                             monitoring probes, 2 for profile)
290                                             on one processor at least */
291 
292   int                     cat_id;        /* Optional category id as regards
293                                             variables output (CS_POST_MESH_...,
294                                             0 by default) */
295 
296   int                     edges_ref;     /* Base mesh for edges mesh */
297   int                     locate_ref;    /* Base mesh for location mesh */
298 
299   bool                    add_groups;    /* Add group information if present */
300   bool                    post_domain;   /* Output domain number in parallel
301                                             if true */
302   bool                    time_varying;  /* Time varying if associated writers
303                                             allow it */
304 
305   int                     n_writers;     /* Number of associated writers */
306   int                    *writer_id;     /* Array of associated writer ids */
307   int                     nt_last;       /* Time step number for the last
308                                             output (-2 before first output,
309                                             -1 for time-indepedent output) */
310 
311   cs_lnum_t               n_i_faces;     /* N. associated interior faces */
312   cs_lnum_t               n_b_faces;     /* N. associated boundary faces */
313 
314   double                  density;       /* Particles density in case
315                                             of particle mesh */
316 
317   const fvm_nodal_t      *exp_mesh;      /* Associated exportable mesh */
318   fvm_nodal_t            *_exp_mesh;     /* Associated exportable mesh,
319                                             if owner */
320 
321   fvm_writer_time_dep_t   mod_flag_min;  /* Minimum mesh time dependency */
322   fvm_writer_time_dep_t   mod_flag_max;  /* Maximum mesh time dependency */
323 
324   int                     n_a_fields;    /* Number of additional fields
325                                             (in addition to those output
326                                             through "cat_id */
327   int                   *a_field_info;   /* For each additional field,
328                                             associated writer id, field id,
329                                             and component id */
330 
331 } cs_post_mesh_t;
332 
333 /*============================================================================
334  * Static global variables
335  *============================================================================*/
336 
337 /* Default output format and options */
338 
339 static int _cs_post_default_format_id = 0;
340 static char *_cs_post_default_format_options = NULL;
341 
342 /* Minimum global mesh time dependency */
343 
344 fvm_writer_time_dep_t  _cs_post_mod_flag_min = FVM_WRITER_FIXED_MESH;
345 
346 /* Flag for stable numbering of particles */
347 
348 static bool        _number_particles_by_coord = false;
349 
350 /* Array of exportable meshes associated with post-processing;
351    free ids start under the last CS_POST_MESH_* definition,
352    currently at -5) */
353 
354 static int              _cs_post_min_mesh_id = _MIN_RESERVED_MESH_ID;
355 static int              _cs_post_n_meshes = 0;
356 static int              _cs_post_n_meshes_max = 0;
357 static cs_post_mesh_t  *_cs_post_meshes = NULL;
358 
359 /* Array of writers for post-processing; */
360 /* writers CS_POST_WRITER_... are reserved */
361 
362 static int                _cs_post_min_writer_id = _MIN_RESERVED_WRITER_ID;
363 static int                _cs_post_n_writers = 0;
364 static int                _cs_post_n_writers_max = 0;
365 static cs_post_writer_t  *_cs_post_writers = NULL;
366 
367 /* Array of registered variable output functions and instances */
368 
369 static int                _cs_post_n_output_tp = 0;
370 static int                _cs_post_n_output_tp_max = 0;
371 
372 static int                _cs_post_n_output_mtp = 0;
373 static int                _cs_post_n_output_mtp_max = 0;
374 
375 static cs_post_time_dep_output_t  **_cs_post_f_output_tp = NULL;
376 static void                       **_cs_post_i_output_tp = NULL;
377 
378 static cs_post_time_mesh_dep_output_t  **_cs_post_f_output_mtp = NULL;
379 static void                            **_cs_post_i_output_mtp = NULL;
380 
381 /* Default directory name */
382 
383 static const char  _cs_post_dirname[] = "postprocessing";
384 
385 /* Timer statistics */
386 
387 static int  _post_out_stat_id = -1;
388 
389 /*============================================================================
390  * Prototypes for functions intended for use only by Fortran wrappers.
391  * (descriptions follow, with function bodies).
392  *============================================================================*/
393 
394 void
395 cs_f_post_activate_by_time_step(void);
396 
397 void
398 cs_f_post_write_var(int               mesh_id,
399                     const char       *var_name,
400                     int               var_dim,
401                     bool              interlace,
402                     bool              use_parent,
403                     int               nt_cur_abs,
404                     double            t_cur_abs,
405                     const cs_real_t  *cel_vals,
406                     const cs_real_t  *i_face_vals,
407                     const cs_real_t  *b_face_vals);
408 
409 /*============================================================================
410  * Prototypes for user functions called only by functions from this module.
411  *============================================================================*/
412 
413 /*----------------------------------------------------------------------------
414  * User function for output of values on a post-processing mesh.
415  *
416  * \param[in]       mesh_name    name of the output mesh for the current call
417  * \param[in]       mesh_id      id of the output mesh for the current call
418  * \param[in]       cat_id       category id of the output mesh for the
419  *                               current call
420  * \param[in]       probes       pointer to associated probe set structure if
421  *                               the mesh is a probe set, NULL otherwise
422  * \param[in]       n_cells      local number of cells of post_mesh
423  * \param[in]       n_i_faces    local number of interior faces of post_mesh
424  * \param[in]       n_b_faces    local number of boundary faces of post_mesh
425  * \param[in]       n_vertices   local number of vertices faces of post_mesh
426  * \param[in]       cell_list    list of cells (0 to n-1) of post-processing mesh
427  * \param[in]       i_face_list  list of interior faces (0 to n-1) of
428  *                               post-processing mesh
429  * \param[in]       b_face_list  list of boundary faces (0 to n-1) of
430  *                               post-processing mesh
431  * \param[in]       vertex_list  list of vertices (0 to n-1) of
432  *                               post-processing mesh
433  * \param[in]       ts           time step status structure, or NULL
434  *----------------------------------------------------------------------------*/
435 
436 void
437 cs_user_postprocess_values(const char            *mesh_name,
438                            int                    mesh_id,
439                            int                    cat_id,
440                            cs_probe_set_t        *probes,
441                            cs_lnum_t              n_cells,
442                            cs_lnum_t              n_i_faces,
443                            cs_lnum_t              n_b_faces,
444                            cs_lnum_t              n_vertice,
445                            const cs_lnum_t        cell_list[],
446                            const cs_lnum_t        i_face_list[],
447                            const cs_lnum_t        b_face_list[],
448                            const cs_lnum_t        vertex_list[],
449                            const cs_time_step_t  *ts);
450 
451 /*============================================================================
452  * Private function definitions
453  *============================================================================*/
454 
455 /*----------------------------------------------------------------------------
456  * Clear temporary writer definition information.
457  *
458  * parameters:
459  *   writer <-> pointer to writer structure
460  *----------------------------------------------------------------------------*/
461 
462 static void
_destroy_writer_def(cs_post_writer_t * writer)463 _destroy_writer_def(cs_post_writer_t  *writer)
464 {
465   assert(writer != NULL);
466   if (writer->wd != NULL) {
467     cs_post_writer_def_t  *wd = writer->wd;
468     BFT_FREE(wd->case_name);
469     BFT_FREE(wd->dir_name);
470     BFT_FREE(wd->fmt_opts);
471     BFT_FREE(writer->wd);
472   }
473 }
474 
475 /*----------------------------------------------------------------------------
476  * Print writer information to log file
477  *----------------------------------------------------------------------------*/
478 
479 static void
_writer_info(void)480 _writer_info(void)
481 {
482   if (cs_glob_rank_id < 1) {
483 
484     bft_printf(_("\n"
485                  "Postprocessing output writers:\n"
486                  "------------------------------\n\n"));
487 
488     for (int i = 0; i < _cs_post_n_writers; i++) {
489 
490       int fmt_id = 0, n_fmt_str = 0;
491       fvm_writer_time_dep_t   time_dep = FVM_WRITER_FIXED_MESH;
492       const char  *fmt_name, *fmt_opts = NULL;
493       const char  *case_name = NULL, *dir_name = NULL;
494       const char empty[] = "";
495       char interval_s[128] = "";
496 
497       const cs_post_writer_t  *writer = _cs_post_writers + i;
498 
499       if (writer->wd != NULL) {
500         const cs_post_writer_def_t *wd = writer->wd;
501         fmt_id = wd->fmt_id;
502         time_dep = wd->time_dep;
503         fmt_opts = wd->fmt_opts;
504         case_name = wd->case_name;
505         dir_name = wd->dir_name;
506       }
507       else if (writer->writer != NULL) {
508         const fvm_writer_t *w = writer->writer;
509         fmt_id = fvm_writer_get_format_id(fvm_writer_get_format(w));
510         time_dep = fvm_writer_get_time_dep(w);
511         case_name = fvm_writer_get_name(w);
512         fmt_opts = fvm_writer_get_options(w);
513         dir_name = fvm_writer_get_path(w);
514       }
515       if (fmt_opts == NULL)
516         fmt_opts = empty;
517 
518       n_fmt_str = fvm_writer_n_version_strings(fmt_id);
519       if (n_fmt_str == 0)
520         fmt_name = fvm_writer_format_name(fmt_id);
521       else
522         fmt_name = fvm_writer_version_string(fmt_id, 0, 0);
523 
524       cs_time_control_get_description(&(writer->tc), interval_s, 128);
525 
526       bft_printf(_("  %2d: name: %s\n"
527                    "      directory: %s\n"
528                    "      format: %s\n"
529                    "      options: %s\n"
530                    "      time dependency: %s\n"
531                    "      output: %s\n\n"),
532                  writer->id, case_name, dir_name, fmt_name, fmt_opts,
533                  _(fvm_writer_time_dep_name[time_dep]), interval_s);
534     }
535   }
536 }
537 
538 /*----------------------------------------------------------------------------
539  * Initialize a writer; this creates the FVM writer structure, and
540  * clears the temporary writer definition information.
541  *
542  * parameters:
543  *   writer <-> pointer to writer structure
544  *----------------------------------------------------------------------------*/
545 
546 static void
_init_writer(cs_post_writer_t * writer)547 _init_writer(cs_post_writer_t  *writer)
548 {
549   assert(writer != NULL);
550 
551   if (writer->writer == NULL) {
552 
553     cs_post_writer_def_t  *wd = writer->wd;
554 
555     /* Sanity checks */
556     assert(writer->wd != NULL);
557     if (wd->fmt_id >= fvm_writer_n_formats())
558       bft_error(__FILE__, __LINE__, 0,
559                 _(" Invalid format name for writer (case: %s, dirname: %s)."),
560                 wd->case_name, wd->dir_name);
561 
562     writer->writer = fvm_writer_init(wd->case_name,
563                                      wd->dir_name,
564                                      fvm_writer_format_name(wd->fmt_id),
565                                      wd->fmt_opts,
566                                      wd->time_dep);
567     _destroy_writer_def(writer);
568 
569   }
570 
571 }
572 
573 /*----------------------------------------------------------------------------
574  * Free a writer's forced output time values.
575  *
576  * parameters:
577  *   w <-> pointer to writer structure
578  *----------------------------------------------------------------------------*/
579 
580 static void
_free_writer_times(cs_post_writer_t * w)581 _free_writer_times(cs_post_writer_t  *w)
582 {
583   assert(w != NULL);
584 
585   if (w->ot == NULL) {
586     BFT_FREE(w->ot->t_vals);
587     BFT_FREE(w->ot->t_steps);
588     BFT_FREE(w->ot);
589   }
590 }
591 
592 /*----------------------------------------------------------------------------
593  * Create a specific writer output times structure.
594  *
595  * returns:
596  *   structure for handling of specific output times
597  *----------------------------------------------------------------------------*/
598 
599 static cs_post_writer_times_t *
_writer_times_create(void)600 _writer_times_create(void)
601 {
602   cs_post_writer_times_t  *ot;
603   BFT_MALLOC(ot, 1, cs_post_writer_times_t);
604 
605   ot->n_t_steps_max = 0;
606   ot->n_t_vals_max = 0;
607 
608   ot->n_t_steps = 0;
609   ot->n_t_vals = 0;
610 
611   ot->t_steps = NULL;
612   ot->t_vals = NULL;
613 
614   return ot;
615 }
616 
617 /*----------------------------------------------------------------------------
618  * Add an activation time step for a specific writer.
619  *
620  * If a negative value is provided, a previously added activation time
621  * step matching that absolute value will be removed, if present.
622  *
623  * parameters:
624  *   writer_id <-- writer id, or 0 for all writers
625  *   nt        <-- time step value to add (or remove)
626  *----------------------------------------------------------------------------*/
627 
628 static void
_add_writer_ts(cs_post_writer_t * w,int nt)629 _add_writer_ts(cs_post_writer_t  *w,
630                int                nt)
631 {
632   int prev_id;
633   int nt_abs = CS_ABS(nt);
634 
635   if (w->ot == NULL)
636     w->ot = _writer_times_create();
637 
638   /* Search for previous value */
639 
640   for (prev_id = 0; prev_id < w->ot->n_t_steps; prev_id++) {
641     if (w->ot->t_steps[prev_id] == nt_abs)
642       break;
643   }
644 
645   /* If value already present */
646 
647   if (prev_id < w->ot->n_t_steps) {
648 
649     /* Remove previous value from unsorted list (swap with last, remove last) */
650 
651     if (nt < 0) {
652       w->ot->t_steps[prev_id] = w->ot->t_steps[w->ot->n_t_steps - 1];
653       w->ot->n_t_steps -= 1;
654     }
655 
656   }
657 
658   /* If values not already present */
659 
660   else if (nt > -1) {
661 
662     if (w->ot->n_t_steps_max < w->ot->n_t_steps + 1) {
663       if (w->ot->n_t_steps_max == 0)
664         w->ot->n_t_steps_max = 1;
665       else
666         w->ot->n_t_steps_max *= 2;
667       BFT_REALLOC(w->ot->t_steps, w->ot->n_t_steps_max, int);
668     }
669 
670     w->ot->t_steps[w->ot->n_t_steps] = nt;
671     w->ot->n_t_steps += 1;
672 
673   }
674 }
675 
676 /*----------------------------------------------------------------------------
677  * Add an activation time value for a specific writer.
678  *
679  * If a negative value is provided, a previously added activation time
680  * step matching that absolute value will be removed, if present.
681  *
682  * parameters:
683  *   writer_id <-- writer id, or 0 for all writers
684  *   t         <-- time value to add (or remove)
685  *----------------------------------------------------------------------------*/
686 
687 static void
_add_writer_tv(cs_post_writer_t * w,double t)688 _add_writer_tv(cs_post_writer_t  *w,
689                double             t)
690 {
691   int prev_id;
692   double t_abs = CS_ABS(t);
693 
694   if (w->ot == NULL)
695     w->ot = _writer_times_create();
696 
697   /* Search for previous value */
698 
699   for (prev_id = 0; prev_id < w->ot->n_t_steps; prev_id++) {
700     double td = w->ot->t_vals[prev_id] - t_abs;
701     if (td > -1.e-35 && td < 1.e-35)
702       break;
703   }
704 
705   /* If value already present */
706 
707   if (prev_id < w->ot->n_t_vals) {
708 
709     /* Remove previous value from unsorted list (swap with last, remove last) */
710 
711     if (t < 0.) {
712       w->ot->t_vals[prev_id] = w->ot->t_vals[w->ot->n_t_vals - 1];
713       w->ot->n_t_vals -= 1;
714     }
715 
716   }
717 
718   /* If values not already present */
719 
720   else if (t >= 0.) {
721 
722     if (w->ot->n_t_vals_max < w->ot->n_t_vals + 1) {
723       if (w->ot->n_t_vals_max == 0)
724         w->ot->n_t_vals_max = 1;
725       else
726         w->ot->n_t_vals_max *= 2;
727       BFT_REALLOC(w->ot->t_vals, w->ot->n_t_vals_max, double);
728     }
729 
730     w->ot->t_vals[w->ot->n_t_vals] = t;
731     w->ot->n_t_vals += 1;
732 
733   }
734 }
735 
736 /*----------------------------------------------------------------------------
737  * Update "active" or "inactive" flag of a writer based on specified
738  * output lists.
739  *
740  * parameters:
741  *   w  <-> pointer to writer structure
742  *   ts <-- time step status structure
743  *----------------------------------------------------------------------------*/
744 
745 static void
_activate_if_listed(cs_post_writer_t * w,const cs_time_step_t * ts)746 _activate_if_listed(cs_post_writer_t      *w,
747                     const cs_time_step_t  *ts)
748 {
749   int  i;
750   bool force_status = false;
751   int prev_status = w->active;
752 
753   cs_post_writer_times_t *ot = w->ot;
754 
755   /* If no output times list is provided, nothing to do */
756 
757   if (ot == NULL)
758     return;
759 
760   /* In case of previous calls for a given time step,
761      do not change status (which must have been forced otherwise),
762      but update lists so as not to provoke an output at the next
763      time step (so as to be consistent with the forcing that must have
764      been done prior to entering here for this situation to exist). */
765 
766   if (w->tc.last_nt == ts->nt_cur)
767     force_status = true;
768 
769   /* Test for listed time steps */
770 
771   i = 0;
772   while (i < ot->n_t_steps) {
773     /* Activate, then remove current or previous time steps from list */
774     if (ot->t_steps[i] <= ts->nt_cur) {
775       if (w->active > -1)
776         w->active = 1;
777       ot->t_steps[i] = ot->t_steps[ot->n_t_steps - 1];
778       ot->n_t_steps -= 1;
779     }
780     else
781       i++;
782   }
783 
784   /* Test for listed time values */
785 
786   i = 0;
787   while (i < ot->n_t_vals) {
788     /* Activate, then remove current or previous time values from list */
789     if (ot->t_vals[i] <= ts->t_cur) {
790       if (w->active > -1)
791         w->active = 1;
792       ot->t_vals[i] = ot->t_vals[ot->n_t_steps - 1];
793       ot->n_t_vals -= 1;
794     }
795     else
796       i++;
797   }
798 
799   if (force_status)
800     w->active = prev_status;
801 }
802 
803 /*----------------------------------------------------------------------------
804  * Convert cs_post_type_t datatype to cs_datatype_t.
805  *
806  * parameters:
807  *   type_cs <-- Code_Saturne data type
808  *
809  * returns
810  *   corresponding FVM datatype
811  *----------------------------------------------------------------------------*/
812 
813 static cs_datatype_t
_cs_post_cnv_datatype(cs_post_type_t type_cs)814 _cs_post_cnv_datatype(cs_post_type_t  type_cs)
815 {
816   cs_datatype_t type_fvm = CS_DATATYPE_NULL;
817 
818   switch(type_cs) {
819 
820   case CS_POST_TYPE_cs_real_t:
821     if (sizeof(cs_real_t) == sizeof(double))
822       type_fvm = CS_DOUBLE;
823     else if (sizeof(cs_real_t) == sizeof(float))
824       type_fvm = CS_FLOAT;
825     break;
826 
827   case CS_POST_TYPE_int:
828     if (sizeof(int) == 4)
829       type_fvm = CS_INT32;
830     else if (sizeof(int) == 8)
831       type_fvm = CS_INT64;
832     break;
833 
834   case CS_POST_TYPE_float:
835     type_fvm = CS_FLOAT;
836     break;
837 
838   case CS_POST_TYPE_double:
839     type_fvm = CS_DOUBLE;
840     break;
841 
842   default:
843     assert(0);
844   }
845 
846   return type_fvm;
847 }
848 
849 /*----------------------------------------------------------------------------
850  * Search for position in the array of writers of a writer with a given id.
851  *
852  * parameters:
853  *   writer_id <-- id of writer
854  *
855  * returns:
856  *   position in the array of writers
857  *----------------------------------------------------------------------------*/
858 
859 static int
_cs_post_writer_id(const int writer_id)860 _cs_post_writer_id(const int  writer_id)
861 {
862   int  id;
863 
864   cs_post_writer_t  *writer = NULL;
865 
866   /* Search for requested writer */
867 
868   for (id = 0; id < _cs_post_n_writers; id++) {
869     writer = _cs_post_writers + id;
870     if (writer->id == writer_id)
871       break;
872   }
873   if (id >= _cs_post_n_writers)
874     bft_error(__FILE__, __LINE__, 0,
875               _("The requested post-processing writer number\n"
876                 "%d is not defined.\n"), (int)(writer_id));
877 
878   return id;
879 }
880 
881 /*----------------------------------------------------------------------------
882  * Search for position in the array of writers of a writer with a given id,
883  * allowing the writer not to be present.
884  *
885  * parameters:
886  *   writer_id <-- id of writer
887  *
888  * returns:
889  *   position in the array of writers, or -1
890  *----------------------------------------------------------------------------*/
891 
892 static int
_cs_post_writer_id_try(const int writer_id)893 _cs_post_writer_id_try(const int  writer_id)
894 {
895   int  id;
896 
897   cs_post_writer_t  *writer = NULL;
898 
899   /* Search for requested writer */
900 
901   for (id = 0; id < _cs_post_n_writers; id++) {
902     writer = _cs_post_writers + id;
903     if (writer->id == writer_id)
904       break;
905   }
906   if (id >= _cs_post_n_writers)
907     id = -1;
908 
909   return id;
910 }
911 
912 /*----------------------------------------------------------------------------
913  * Search for position in the array of meshes of a mesh with a given id.
914  *
915  * parameters:
916  *   mesh_id <-- id of mesh
917  *
918  * returns:
919  *   position in the array of meshes
920  *----------------------------------------------------------------------------*/
921 
922 static int
_cs_post_mesh_id(int mesh_id)923 _cs_post_mesh_id(int  mesh_id)
924 {
925   int id;
926   cs_post_mesh_t  *post_mesh = NULL;
927 
928   /* Search for requested mesh */
929 
930   for (id = 0; id < _cs_post_n_meshes; id++) {
931     post_mesh = _cs_post_meshes + id;
932     if (post_mesh->id == mesh_id)
933       break;
934   }
935   if (id >= _cs_post_n_meshes)
936     bft_error(__FILE__, __LINE__, 0,
937               _("The requested post-processing mesh number\n"
938                 "%d is not defined.\n"), (int)mesh_id);
939 
940   return id;
941 }
942 
943 /*----------------------------------------------------------------------------
944  * Search for position in the array of meshes of a mesh with a given id,
945  * allowing the id not to be present
946  *
947  * parameters:
948  *   mesh_id <-- id of mesh
949  *
950  * returns:
951  *   position in the array of meshes, or -1
952  *----------------------------------------------------------------------------*/
953 
954 static int
_cs_post_mesh_id_try(int mesh_id)955 _cs_post_mesh_id_try(int  mesh_id)
956 {
957   int id;
958   cs_post_mesh_t  *post_mesh = NULL;
959 
960   /* Search for requested mesh */
961 
962   for (id = 0; id < _cs_post_n_meshes; id++) {
963     post_mesh = _cs_post_meshes + id;
964     if (post_mesh->id == mesh_id)
965       break;
966   }
967   if (id >= _cs_post_n_meshes)
968     id = -1;
969 
970   return id;
971 }
972 
973 /*----------------------------------------------------------------------------
974  * Return indicator base on Lagrangian calculation status:
975  *
976  * parameters:
977  *   ts            <-- time step structure, or NULL
978  *
979  * returns:
980  *   0 if Lagrangian model is not active
981  *   1 if Lagrangian model is active but no particle data is ready
982  *   2 if current but not previous particle data is present
983  *   3 if current and previous particle data is present
984  *----------------------------------------------------------------------------*/
985 
986 static int
_lagrangian_needed(const cs_time_step_t * ts)987 _lagrangian_needed(const cs_time_step_t  *ts)
988 {
989   int retval = 0;
990 
991   int _model = cs_lagr_model_type();
992 
993   if (_model != 0) {
994 
995     retval = 1;
996 
997     if (ts != NULL) {
998       int _restart = cs_lagr_particle_restart();
999       int _nt_start = (_restart) ? ts->nt_prev : ts->nt_prev + 1;
1000       if (ts->nt_cur == _nt_start)
1001         retval = 2;
1002       else if (ts->nt_cur > _nt_start)
1003         retval = 3;
1004     }
1005 
1006   }
1007 
1008   return retval;
1009 }
1010 
1011 /*----------------------------------------------------------------------------
1012  * Update mesh attributes related to writer association.
1013  *
1014  * parameters:
1015  *   post_mesh  <-> pointer to postprocessing mesh
1016  *----------------------------------------------------------------------------*/
1017 
1018 static void
_update_mesh_writer_associations(cs_post_mesh_t * post_mesh)1019 _update_mesh_writer_associations(cs_post_mesh_t  *post_mesh)
1020 {
1021   int i, j;
1022 
1023   /* Minimum and maximum time dependency flags initially inverted,
1024      will be recalculated after mesh - writer associations */
1025 
1026   if (post_mesh->time_varying)
1027     post_mesh->mod_flag_min = FVM_WRITER_TRANSIENT_CONNECT;
1028   else
1029     post_mesh->mod_flag_min = _cs_post_mod_flag_min;
1030   post_mesh->mod_flag_max = FVM_WRITER_FIXED_MESH;
1031 
1032   int   n_writers = post_mesh->n_writers;
1033 
1034   if (post_mesh->ent_flag[3] == 0) { /* Non-Lagrangian mesh */
1035 
1036     for (i = 0; i < n_writers; i++) {
1037 
1038       fvm_writer_time_dep_t mod_flag;
1039       const int _writer_id = post_mesh->writer_id[i];
1040       cs_post_writer_t  *writer = _cs_post_writers + _writer_id;
1041 
1042       if (writer->wd != NULL)
1043         mod_flag = writer->wd->time_dep;
1044       else
1045         mod_flag = fvm_writer_get_time_dep(writer->writer);
1046 
1047       if (mod_flag < post_mesh->mod_flag_min)
1048         post_mesh->mod_flag_min = mod_flag;
1049       if (mod_flag > post_mesh->mod_flag_max)
1050         post_mesh->mod_flag_max = mod_flag;
1051 
1052     }
1053 
1054   }
1055   else { /* Lagrangian mesh: post_mesh->ent_flag[3] != 0 */
1056 
1057     int mode = post_mesh->ent_flag[3];
1058     fvm_writer_time_dep_t mod_type = (mode == 2) ?
1059       FVM_WRITER_FIXED_MESH : FVM_WRITER_TRANSIENT_CONNECT;
1060 
1061     post_mesh->mod_flag_min = FVM_WRITER_TRANSIENT_CONNECT;
1062     post_mesh->mod_flag_max = FVM_WRITER_TRANSIENT_CONNECT;
1063 
1064     for (i = 0, j = 0; i < n_writers; i++) {
1065 
1066       fvm_writer_time_dep_t mod_flag;
1067       const int _writer_id = post_mesh->writer_id[i];
1068       cs_post_writer_t  *writer = _cs_post_writers + _writer_id;
1069 
1070       if (writer->wd != NULL)
1071         mod_flag = writer->wd->time_dep;
1072       else
1073         mod_flag = fvm_writer_get_time_dep(writer->writer);
1074 
1075       if (mod_flag == mod_type)
1076         post_mesh->writer_id[j++] = _writer_id;
1077 
1078     }
1079 
1080     if (j < n_writers) {
1081       post_mesh->n_writers = j;
1082       BFT_REALLOC(post_mesh->writer_id, j, int);
1083     }
1084 
1085   }
1086 }
1087 
1088 /*----------------------------------------------------------------------------
1089  * Add or select a post-processing mesh, do basic initialization, and return
1090  * a pointer to the associated structure.
1091  *
1092  * parameters:
1093  *   mesh_id      <-- requested mesh id
1094  *   time_varying <-- if true, mesh may be redefined over time if associated
1095  *                    writers allow it
1096  *   mode         <-- 0 for standard mesh, 1 for particles, 2 for trajectories,
1097  *                    3 for probes, 4 for profiles
1098  *   n_writers    <-- number of associated writers
1099  *   writer_ids   <-- ids of associated writers
1100  *
1101  * returns:
1102  *   pointer to associated structure
1103  *----------------------------------------------------------------------------*/
1104 
1105 static cs_post_mesh_t *
_predefine_mesh(int mesh_id,bool time_varying,int mode,int n_writers,const int writer_ids[])1106 _predefine_mesh(int        mesh_id,
1107                 bool       time_varying,
1108                 int        mode,
1109                 int        n_writers,
1110                 const int  writer_ids[])
1111 {
1112   /* local variables */
1113 
1114   int  i, j;
1115 
1116   cs_post_mesh_t  *post_mesh = NULL;
1117 
1118   /* Check that the requested mesh is available */
1119 
1120   if (mesh_id == 0)
1121       bft_error(__FILE__, __LINE__, 0,
1122                 _("The requested post-processing mesh number\n"
1123                   "must be < 0 (reserved) or > 0 (user).\n"));
1124 
1125   for (i = 0; i < _cs_post_n_meshes; i++) {
1126     if ((_cs_post_meshes + i)->id == mesh_id) {
1127 
1128       post_mesh = _cs_post_meshes + i;
1129 
1130       BFT_FREE(post_mesh->name);
1131       for (j = 0; j < 5; j++)
1132         BFT_FREE(post_mesh->criteria[j]);
1133       BFT_FREE(post_mesh->writer_id);
1134 
1135       post_mesh->exp_mesh = NULL;
1136       if (post_mesh->_exp_mesh != NULL)
1137         post_mesh->_exp_mesh = fvm_nodal_destroy(post_mesh->_exp_mesh);
1138 
1139       break;
1140 
1141     }
1142   }
1143 
1144   if (i == _cs_post_n_meshes) {
1145 
1146     /* Resize global array of exportable meshes */
1147 
1148     if (_cs_post_n_meshes == _cs_post_n_meshes_max) {
1149       if (_cs_post_n_meshes_max == 0)
1150         _cs_post_n_meshes_max = 8;
1151       else
1152         _cs_post_n_meshes_max *= 2;
1153       BFT_REALLOC(_cs_post_meshes,
1154                   _cs_post_n_meshes_max,
1155                   cs_post_mesh_t);
1156     }
1157 
1158     post_mesh = _cs_post_meshes + i;
1159 
1160     _cs_post_n_meshes += 1;
1161   }
1162 
1163   if (mesh_id < _cs_post_min_mesh_id)
1164     _cs_post_min_mesh_id = mesh_id;
1165 
1166   /* Assign newly created mesh to the structure */
1167 
1168   post_mesh->id = mesh_id;
1169   post_mesh->name = NULL;
1170   post_mesh->cat_id = mesh_id;
1171   post_mesh->edges_ref = -1;
1172   post_mesh->locate_ref = -1;
1173 
1174   post_mesh->n_writers = 0;
1175   post_mesh->writer_id = NULL;
1176 
1177   post_mesh->nt_last = -2;
1178 
1179   post_mesh->add_groups = false;
1180   post_mesh->post_domain = false;
1181 
1182   post_mesh->time_varying = time_varying;
1183 
1184   for (j = 0; j < 5; j++) {
1185     post_mesh->criteria[j] = NULL;
1186     post_mesh->sel_func[j] = NULL;
1187     post_mesh->sel_input[j] = NULL;
1188     post_mesh->ent_flag[j] = 0;
1189   }
1190 
1191   post_mesh->n_i_faces = 0;
1192   post_mesh->n_b_faces = 0;
1193 
1194   post_mesh->density = 1.;
1195 
1196   post_mesh->exp_mesh = NULL;
1197   post_mesh->_exp_mesh = NULL;
1198 
1199   /* Minimum and maximum time dependency flags initially inverted,
1200      will be recalculated after mesh - writer associations */
1201 
1202   if (post_mesh->time_varying)
1203     post_mesh->mod_flag_min = FVM_WRITER_TRANSIENT_CONNECT;
1204   else
1205     post_mesh->mod_flag_min = _cs_post_mod_flag_min;
1206   post_mesh->mod_flag_max = FVM_WRITER_FIXED_MESH;
1207 
1208   /* Associate mesh with writers */
1209 
1210   post_mesh->n_writers = n_writers;
1211   BFT_MALLOC(post_mesh->writer_id, n_writers, int);
1212 
1213   for (i = 0; i < n_writers; i++)
1214     post_mesh->writer_id[i] = _cs_post_writer_id(writer_ids[i]);
1215 
1216   if (mode == 1 || mode == 2)          /* Lagrangian mesh */
1217     post_mesh->ent_flag[3] = mode;
1218 
1219   else if (mode == 3 || mode == 4)     /* Probe or profile mesh */
1220     post_mesh->ent_flag[4] = mode - 2; /* 1 = probe monitoring,
1221                                           2 = profile */
1222 
1223   _update_mesh_writer_associations(post_mesh);
1224 
1225   /* Additional field output */
1226 
1227   post_mesh->n_a_fields = 0;
1228   post_mesh->a_field_info = NULL;
1229 
1230   return post_mesh;
1231 }
1232 
1233 /*----------------------------------------------------------------------------
1234  * Free a postprocessing mesh's data.
1235  *
1236  * parameters:
1237  *   _mesh_id <-- local id of mesh to remove
1238  *----------------------------------------------------------------------------*/
1239 
1240 static void
_free_mesh(int _mesh_id)1241 _free_mesh(int _mesh_id)
1242 {
1243   int i;
1244   cs_post_mesh_t  *post_mesh = _cs_post_meshes + _mesh_id;
1245 
1246   if (post_mesh->_exp_mesh != NULL)
1247     post_mesh->_exp_mesh = fvm_nodal_destroy(post_mesh->_exp_mesh);
1248 
1249   BFT_FREE(post_mesh->writer_id);
1250   post_mesh->n_writers = 0;
1251 
1252   for (i = 0; i < 5; i++)
1253     BFT_FREE(post_mesh->criteria[i]);
1254 
1255   BFT_FREE(post_mesh->name);
1256   BFT_FREE(post_mesh->a_field_info);
1257 
1258   /* Shift remaining meshes */
1259 
1260   for (i = 0; i < _cs_post_n_meshes; i++) {
1261     post_mesh = _cs_post_meshes + i;
1262     if (post_mesh->locate_ref > _mesh_id)
1263       post_mesh->locate_ref -= 1;
1264     else if (post_mesh->locate_ref == _mesh_id)
1265       post_mesh->locate_ref = -1;
1266     if (post_mesh->edges_ref >= _mesh_id) {
1267       assert(post_mesh->edges_ref != _mesh_id);
1268       post_mesh->edges_ref -= 1;
1269     }
1270   }
1271 
1272   for (i = _mesh_id + 1; i < _cs_post_n_meshes; i++) {
1273     post_mesh = _cs_post_meshes + i;
1274     _cs_post_meshes[i-1] = _cs_post_meshes[i];
1275   }
1276   _cs_post_n_meshes -= 1;
1277 }
1278 
1279 /*----------------------------------------------------------------------------
1280  * Check and possibly fix postprocessing mesh category id once we have
1281  * knowledge of entity counts.
1282  *
1283  * parameters:
1284  *   post_mesh <-> pointer to partially initialized post-processing mesh
1285  *----------------------------------------------------------------------------*/
1286 
1287 static void
_check_mesh_cat_id(cs_post_mesh_t * post_mesh)1288 _check_mesh_cat_id(cs_post_mesh_t  *post_mesh)
1289 {
1290   if (   post_mesh->cat_id == CS_POST_MESH_VOLUME
1291       || post_mesh->cat_id == CS_POST_MESH_BOUNDARY
1292       || post_mesh->cat_id == CS_POST_MESH_SURFACE) {
1293     const int *ef = post_mesh->ent_flag;
1294     if (ef[0] == 1 && ef[1] == 0 && ef[2] == 0)
1295       post_mesh->cat_id = CS_POST_MESH_VOLUME;
1296     else if (ef[0] == 0 && ef[1] == 0 && ef[2] == 1)
1297       post_mesh->cat_id = CS_POST_MESH_BOUNDARY;
1298     else if (ef[0] == 0 && (ef[1] == 1 || ef[2] == 1))
1299       post_mesh->cat_id = CS_POST_MESH_SURFACE;
1300   }
1301 }
1302 
1303 /*----------------------------------------------------------------------------
1304  * Create a post-processing mesh; lists of cells or faces to extract are
1305  * sorted upon exit, whether they were sorted upon calling or not.
1306  *
1307  * The list of associated cells is only necessary if the number of cells
1308  * to extract is strictly greater than 0 and less than the number of cells
1309  * of the computational mesh.
1310  *
1311  * Lists of faces are ignored if the number of extracted cells is nonzero;
1312  * otherwise, if the number of boundary faces to extract is equal to the
1313  * number of boundary faces in the computational mesh, and the number of
1314  * interior faces to extract is zero, then we extract by default the boundary
1315  * mesh, and the list of associated boundary faces is thus not necessary.
1316  *
1317  * parameters:
1318  *   post_mesh   <-> pointer to partially initialized post-processing mesh
1319  *   n_cells     <-- number of associated cells
1320  *   n_i_faces   <-- number of associated interior faces
1321  *   n_b_faces   <-- number of associated boundary faces
1322  *   cell_list   <-> list of associated cells
1323  *   i_face_list <-> list of associated interior faces
1324  *   b_face_list <-> list of associated boundary faces
1325  *----------------------------------------------------------------------------*/
1326 
1327 static void
_define_export_mesh(cs_post_mesh_t * post_mesh,cs_lnum_t n_cells,cs_lnum_t n_i_faces,cs_lnum_t n_b_faces,cs_lnum_t cell_list[],cs_lnum_t i_face_list[],cs_lnum_t b_face_list[])1328 _define_export_mesh(cs_post_mesh_t  *post_mesh,
1329                     cs_lnum_t        n_cells,
1330                     cs_lnum_t        n_i_faces,
1331                     cs_lnum_t        n_b_faces,
1332                     cs_lnum_t        cell_list[],
1333                     cs_lnum_t        i_face_list[],
1334                     cs_lnum_t        b_face_list[])
1335 {
1336   /* local variables */
1337 
1338   fvm_nodal_t  *exp_mesh = NULL;
1339 
1340   /* Create associated structure */
1341 
1342   if (post_mesh->ent_flag[0] == 1) {
1343 
1344     if (n_cells >= cs_glob_mesh->n_cells)
1345       exp_mesh = cs_mesh_connect_cells_to_nodal(cs_glob_mesh,
1346                                                 post_mesh->name,
1347                                                 post_mesh->add_groups,
1348                                                 cs_glob_mesh->n_cells,
1349                                                 NULL);
1350     else
1351       exp_mesh = cs_mesh_connect_cells_to_nodal(cs_glob_mesh,
1352                                                 post_mesh->name,
1353                                                 post_mesh->add_groups,
1354                                                 n_cells,
1355                                                 cell_list);
1356 
1357   }
1358   else {
1359 
1360     if (   n_b_faces >= cs_glob_mesh->n_b_faces
1361         && n_i_faces == 0)
1362       exp_mesh = cs_mesh_connect_faces_to_nodal(cs_glob_mesh,
1363                                                 post_mesh->name,
1364                                                 post_mesh->add_groups,
1365                                                 0,
1366                                                 cs_glob_mesh->n_b_faces,
1367                                                 NULL,
1368                                                 NULL);
1369     else
1370       exp_mesh = cs_mesh_connect_faces_to_nodal(cs_glob_mesh,
1371                                                 post_mesh->name,
1372                                                 post_mesh->add_groups,
1373                                                 n_i_faces,
1374                                                 n_b_faces,
1375                                                 i_face_list,
1376                                                 b_face_list);
1377 
1378   }
1379 
1380   /* Fix category id now that we have knowledge of entity counts */
1381 
1382   _check_mesh_cat_id(post_mesh);
1383 
1384   /* Local dimensions */
1385 
1386   post_mesh->n_i_faces = n_i_faces;
1387   post_mesh->n_b_faces = n_b_faces;
1388 
1389   /* As faces might be split, ensure the number of faces is correct */
1390 
1391   /* Link to newly created mesh */
1392 
1393   post_mesh->exp_mesh = exp_mesh;
1394   post_mesh->_exp_mesh = exp_mesh;
1395 }
1396 
1397 /*----------------------------------------------------------------------------
1398  * Create a particles post-processing mesh;
1399  *
1400  * parameters:
1401  *   post_mesh     <-> pointer to partially initialized post-processing mesh
1402  *   n_particles   <-- number of associated particles
1403  *   particle_list <-> list of associated particles
1404  *   ts            <-- time step structure
1405  *----------------------------------------------------------------------------*/
1406 
1407 static void
_define_particle_export_mesh(cs_post_mesh_t * post_mesh,cs_lnum_t n_particles,cs_lnum_t particle_list[],const cs_time_step_t * ts)1408 _define_particle_export_mesh(cs_post_mesh_t        *post_mesh,
1409                              cs_lnum_t              n_particles,
1410                              cs_lnum_t              particle_list[],
1411                              const cs_time_step_t  *ts)
1412 {
1413   /* local variables */
1414 
1415   fvm_nodal_t  *exp_mesh = NULL;
1416 
1417   assert(ts != NULL);
1418 
1419   /* Create associated structure */
1420 
1421   {
1422     cs_gnum_t *global_num = NULL;
1423     cs_coord_3_t *coords = NULL;
1424     fvm_io_num_t  *io_num = NULL;
1425 
1426     cs_lagr_particle_set_t  *p_set = cs_lagr_get_particle_set();
1427 
1428     if (p_set == NULL)
1429       return;
1430 
1431     /* Particle positions */
1432 
1433     if (post_mesh->ent_flag[3] == 1) {
1434 
1435       assert(ts->nt_cur > -1);
1436 
1437       exp_mesh = fvm_nodal_create(post_mesh->name, 3);
1438 
1439       BFT_MALLOC(coords, n_particles, cs_coord_3_t);
1440 
1441       cs_lagr_get_particle_values(p_set,
1442                                   CS_LAGR_COORDS,
1443                                   CS_REAL_TYPE,
1444                                   3,
1445                                   -1,
1446                                   n_particles,
1447                                   particle_list,
1448                                   coords);
1449 
1450       fvm_nodal_define_vertex_list(exp_mesh, n_particles, NULL);
1451       fvm_nodal_transfer_vertices(exp_mesh, (cs_coord_t *)coords);
1452 
1453     }
1454 
1455     /* Particle trajectories */
1456 
1457     else if (post_mesh->ent_flag[3] == 2) {
1458 
1459       cs_lnum_t i;
1460       cs_lnum_t  *vertex_num;
1461       char *mesh_name;
1462 
1463       assert(ts->nt_cur > 0);
1464 
1465       BFT_MALLOC(mesh_name, strlen(post_mesh->name) + 32, char);
1466       sprintf(mesh_name, "%s_%05d", post_mesh->name, ts->nt_cur);
1467 
1468       exp_mesh = fvm_nodal_create(mesh_name, 3);
1469 
1470       BFT_FREE(mesh_name);
1471 
1472       BFT_MALLOC(vertex_num, n_particles*2, cs_lnum_t);
1473 
1474       for (i = 0; i < n_particles*2; i++)
1475         vertex_num[i] = i+1;
1476 
1477       BFT_MALLOC(coords, n_particles*2, cs_coord_3_t);
1478 
1479       cs_lagr_get_trajectory_values(p_set,
1480                                     CS_LAGR_COORDS,
1481                                     CS_REAL_TYPE,
1482                                     3,
1483                                     -1,
1484                                     n_particles,
1485                                     particle_list,
1486                                     coords);
1487 
1488       fvm_nodal_append_by_transfer(exp_mesh,
1489                                    n_particles,
1490                                    FVM_EDGE,
1491                                    NULL,
1492                                    NULL,
1493                                    NULL,
1494                                    vertex_num,
1495                                    NULL);
1496 
1497       fvm_nodal_transfer_vertices(exp_mesh, (cs_coord_t *)coords);
1498 
1499       if (post_mesh->nt_last < ts->nt_cur)
1500         post_mesh->nt_last = -2;
1501     }
1502 
1503     /* Build global numbering if required */
1504 
1505     if (_number_particles_by_coord)
1506       io_num = fvm_io_num_create_from_sfc((const cs_coord_t *)coords,
1507                                           3,
1508                                           n_particles,
1509                                           FVM_IO_NUM_SFC_MORTON_BOX);
1510     else if (cs_glob_n_ranks > 1)
1511       io_num = fvm_io_num_create_from_scan(n_particles);
1512 
1513     if (io_num != NULL) {
1514 
1515       global_num = fvm_io_num_transfer_global_num(io_num);
1516       fvm_io_num_destroy(io_num);
1517 
1518       if (post_mesh->ent_flag[3] == 1) {
1519 
1520         fvm_nodal_init_io_num(exp_mesh, global_num, 0);
1521         BFT_FREE(global_num);
1522 
1523       }
1524       else if (post_mesh->ent_flag[3] == 2) {
1525 
1526         cs_lnum_t i;
1527         cs_gnum_t *g_coord_num;
1528 
1529         fvm_nodal_init_io_num(exp_mesh, global_num, 1);
1530 
1531         BFT_MALLOC(g_coord_num, n_particles*2, cs_gnum_t);
1532         for (i = 0; i < n_particles; i++) {
1533           g_coord_num[i*2] = global_num[i]*2 - 1;
1534           g_coord_num[i*2+1] = global_num[i]*2;
1535         }
1536         BFT_FREE(global_num);
1537 
1538         fvm_nodal_init_io_num(exp_mesh, g_coord_num, 0);
1539 
1540         BFT_FREE(g_coord_num);
1541 
1542       }
1543 
1544     }
1545 
1546     /* Drop trajectory sub-mesh if it is empty
1547        (otherwise, EnSight is OK, but ParaView can't display variables) */
1548 
1549     if (   post_mesh->ent_flag[3] == 2
1550         && fvm_nodal_get_n_g_elements(exp_mesh, FVM_EDGE) == 0)
1551       exp_mesh = fvm_nodal_destroy(exp_mesh);
1552 
1553   }
1554 
1555   /* Fix category id */
1556 
1557   if (post_mesh->cat_id < 0)
1558     post_mesh->cat_id = CS_POST_MESH_PARTICLES;
1559 
1560   /* Link to newly created mesh */
1561 
1562   post_mesh->exp_mesh = exp_mesh;
1563   post_mesh->_exp_mesh = exp_mesh;
1564 }
1565 
1566 /*----------------------------------------------------------------------------
1567  * Initialize a volume or surface post-processing mesh based on its
1568  * selection criteria or selection functions.
1569  *
1570  * parameters:
1571  *   post_mesh <-> pointer to partially initialized post-processing mesh
1572  *   ts        <-- time step structure
1573  *----------------------------------------------------------------------------*/
1574 
1575 static void
_define_regular_mesh(cs_post_mesh_t * post_mesh)1576 _define_regular_mesh(cs_post_mesh_t  *post_mesh)
1577 {
1578   const cs_mesh_t *mesh = cs_glob_mesh;
1579 
1580   assert(post_mesh != NULL);
1581 
1582   assert(post_mesh->exp_mesh == NULL);
1583 
1584   cs_lnum_t i;
1585   cs_lnum_t n_cells = 0, n_i_faces = 0, n_b_faces = 0;
1586   cs_lnum_t *cell_list = NULL, *i_face_list = NULL, *b_face_list = NULL;
1587 
1588   /* Define element lists based on selection criteria */
1589 
1590   if (post_mesh->criteria[0] != NULL) {
1591     const char *criteria = post_mesh->criteria[0];
1592     if (!strcmp(criteria, "all[]"))
1593       n_cells = mesh->n_cells;
1594     else {
1595       BFT_MALLOC(cell_list, mesh->n_cells, cs_lnum_t);
1596       cs_selector_get_cell_num_list(criteria, &n_cells, cell_list);
1597     }
1598   }
1599   else if (post_mesh->sel_func[0] != NULL) {
1600     cs_post_elt_select_t *sel_func = post_mesh->sel_func[0];
1601     sel_func(post_mesh->sel_input[0], &n_cells, &cell_list);
1602     for (i = 0; i < n_cells; i++)
1603       cell_list[i] += 1;
1604   }
1605 
1606   if (post_mesh->criteria[1] != NULL) {
1607     const char *criteria = post_mesh->criteria[1];
1608     if (!strcmp(criteria, "all[]"))
1609       n_i_faces = mesh->n_i_faces;
1610     else {
1611       BFT_MALLOC(i_face_list, mesh->n_i_faces, cs_lnum_t);
1612       cs_selector_get_i_face_num_list(criteria, &n_i_faces, i_face_list);
1613     }
1614   }
1615   else if (post_mesh->sel_func[1] != NULL) {
1616     cs_post_elt_select_t *sel_func = post_mesh->sel_func[1];
1617     sel_func(post_mesh->sel_input[1], &n_i_faces, &i_face_list);
1618     for (i = 0; i < n_i_faces; i++)
1619       i_face_list[i] += 1;
1620   }
1621 
1622   if (post_mesh->criteria[2] != NULL) {
1623     const char *criteria = post_mesh->criteria[2];
1624     if (!strcmp(criteria, "all[]"))
1625       n_b_faces = mesh->n_b_faces;
1626     else {
1627       BFT_MALLOC(b_face_list, mesh->n_b_faces, cs_lnum_t);
1628       cs_selector_get_b_face_num_list(criteria, &n_b_faces, b_face_list);
1629     }
1630   }
1631   else if (post_mesh->sel_func[2] != NULL) {
1632     cs_post_elt_select_t *sel_func = post_mesh->sel_func[2];
1633     sel_func(post_mesh->sel_input[2], &n_b_faces, &b_face_list);
1634     for (i = 0; i < n_b_faces; i++)
1635       b_face_list[i] += 1;
1636   }
1637 
1638   /* Define mesh based on current arguments */
1639 
1640   _define_export_mesh(post_mesh,
1641                       n_cells,
1642                       n_i_faces,
1643                       n_b_faces,
1644                       cell_list,
1645                       i_face_list,
1646                       b_face_list);
1647 
1648   BFT_FREE(cell_list);
1649   BFT_FREE(i_face_list);
1650   BFT_FREE(b_face_list);
1651 }
1652 
1653 /*----------------------------------------------------------------------------
1654  * Create a post-processing mesh for probes
1655  *
1656  * parameters:
1657  *   post_mesh     <-> pointer to partially initialized post-processing mesh
1658  *----------------------------------------------------------------------------*/
1659 
1660 static void
_define_probe_export_mesh(cs_post_mesh_t * post_mesh)1661 _define_probe_export_mesh(cs_post_mesh_t  *post_mesh)
1662 {
1663   /* Sanity checks */
1664   assert(post_mesh != NULL);
1665 
1666   cs_probe_set_t     *pset = (cs_probe_set_t *)post_mesh->sel_input[4];
1667   cs_post_mesh_t     *post_mesh_loc = NULL;
1668   const fvm_nodal_t  *location_mesh = NULL;
1669 
1670   /* First step: locate probes and update their coordinates */
1671 
1672   if (post_mesh->locate_ref > -1) {
1673     post_mesh_loc = _cs_post_meshes + post_mesh->locate_ref;
1674     if (post_mesh_loc->exp_mesh == NULL)
1675       _define_regular_mesh(post_mesh_loc);
1676     location_mesh = post_mesh_loc->exp_mesh;
1677   }
1678 
1679   cs_probe_set_locate(pset, location_mesh);
1680 
1681   /* Create associated structure */
1682 
1683   fvm_nodal_t *exp_mesh
1684     = cs_probe_set_export_mesh(pset, cs_probe_set_get_name(pset));
1685 
1686   /* Link to newly created mesh */
1687 
1688   post_mesh->exp_mesh = exp_mesh;
1689   post_mesh->_exp_mesh = exp_mesh;
1690 
1691   /* Unassign matching location mesh ids for non-transient probe sets
1692      to allow freeing them */
1693 
1694   bool time_varying;
1695 
1696   int  n_writers = 0;
1697   int  *writer_id = NULL;
1698 
1699   cs_probe_set_get_post_info(pset,
1700                              &time_varying,
1701                              NULL,
1702                              NULL,
1703                              NULL,
1704                              NULL,
1705                              NULL,
1706                              &n_writers,
1707                              &writer_id);
1708 
1709   if (time_varying == false)
1710     post_mesh->locate_ref = -1;
1711   else if (post_mesh_loc->mod_flag_max < FVM_WRITER_TRANSIENT_COORDS)
1712     post_mesh_loc->mod_flag_max = FVM_WRITER_TRANSIENT_COORDS;
1713 }
1714 
1715 /*----------------------------------------------------------------------------
1716  * Initialize a post-processing mesh based on its selection criteria
1717  * or selection functions.
1718  *
1719  * parameters:
1720  *   post_mesh <-> pointer to partially initialized post-processing mesh
1721  *   ts        <-- time step structure
1722  *----------------------------------------------------------------------------*/
1723 
1724 static void
_define_mesh(cs_post_mesh_t * post_mesh,const cs_time_step_t * ts)1725 _define_mesh(cs_post_mesh_t        *post_mesh,
1726              const cs_time_step_t  *ts)
1727 
1728 {
1729   const cs_mesh_t *mesh = cs_glob_mesh;
1730 
1731   assert(post_mesh != NULL);
1732 
1733   assert(post_mesh->exp_mesh == NULL);
1734 
1735   /* Edges mesh */
1736 
1737   if (post_mesh->edges_ref > -1) {
1738 
1739     fvm_nodal_t *exp_edges = NULL;
1740     cs_post_mesh_t *post_base
1741       = _cs_post_meshes + _cs_post_mesh_id(post_mesh->edges_ref);
1742 
1743     /* if base mesh structure is not built yet, force its build now */
1744 
1745     if (post_base->exp_mesh == NULL)
1746       _define_mesh(post_base, ts);
1747 
1748     /* Copy mesh edges to new mesh structure */
1749 
1750     exp_edges = fvm_nodal_copy_edges(post_mesh->name, post_mesh->exp_mesh);
1751 
1752     /* Create mesh and assign to structure */
1753 
1754     post_mesh->exp_mesh = exp_edges;
1755     post_mesh->_exp_mesh = exp_edges;
1756   }
1757 
1758   /* Particle (Lagrangian) mesh */
1759 
1760   else if (post_mesh->ent_flag[3] != 0 && ts != NULL) {
1761 
1762     cs_lnum_t n_post_particles = 0, n_particles = cs_lagr_get_n_particles();
1763     cs_lnum_t *particle_list = NULL;
1764 
1765     if (post_mesh->criteria[3] != NULL) {
1766 
1767       cs_lnum_t n_cells = 0;
1768       cs_lnum_t *cell_list = NULL;
1769       const char *criteria = post_mesh->criteria[3];
1770 
1771       if (!strcmp(criteria, "all[]"))
1772         n_cells = mesh->n_cells;
1773       else {
1774         BFT_MALLOC(cell_list, mesh->n_cells, cs_lnum_t);
1775         cs_selector_get_cell_num_list(criteria, &n_cells, cell_list);
1776       }
1777       if (n_cells < mesh->n_cells || post_mesh->density < 1.) {
1778         BFT_MALLOC(particle_list, n_particles, cs_lnum_t);
1779         cs_lagr_get_particle_list(n_cells,
1780                                   cell_list,
1781                                   post_mesh->density,
1782                                   &n_post_particles,
1783                                   particle_list);
1784         BFT_REALLOC(particle_list, n_post_particles, cs_lnum_t);
1785       }
1786       else
1787         n_post_particles = n_particles;
1788       BFT_FREE(cell_list);
1789     }
1790 
1791     else if (post_mesh->sel_func[3] != NULL) {
1792       cs_post_elt_select_t *sel_func = post_mesh->sel_func[3];
1793       sel_func(post_mesh->sel_input[0], &n_post_particles, &particle_list);
1794     }
1795 
1796     _define_particle_export_mesh(post_mesh,
1797                                  n_post_particles,
1798                                  particle_list,
1799                                  ts);
1800 
1801     BFT_FREE(particle_list);
1802   }
1803 
1804   /* Probe mesh */
1805 
1806   else if (post_mesh->ent_flag[4] != 0) {
1807 
1808     _define_probe_export_mesh(post_mesh);
1809 
1810   }
1811 
1812   /* Standard (non-particle) meshes */
1813 
1814   else
1815     _define_regular_mesh(post_mesh);
1816 }
1817 
1818 /*----------------------------------------------------------------------------
1819  * Modify an existing post-processing mesh.
1820  *
1821  * It is not necessary to use this function if a mesh is simply deformed.
1822  *
1823  * parameters:
1824  *   post_mesh <-- pointer to postprocessing mesh structure
1825  *   ts        <-- time step structure
1826  *----------------------------------------------------------------------------*/
1827 
1828 static void
_redefine_mesh(cs_post_mesh_t * post_mesh,const cs_time_step_t * ts)1829 _redefine_mesh(cs_post_mesh_t        *post_mesh,
1830                const cs_time_step_t  *ts)
1831 {
1832   /* Remove previous base structure (return if we do not own the mesh) */
1833 
1834   if (post_mesh->exp_mesh != NULL) {
1835     if (post_mesh->_exp_mesh == NULL)
1836       return;
1837     else
1838       post_mesh->_exp_mesh = fvm_nodal_destroy(post_mesh->_exp_mesh);
1839   }
1840   post_mesh->exp_mesh = NULL;
1841 
1842   /* Define new mesh */
1843 
1844   _define_mesh(post_mesh, ts);
1845 }
1846 
1847 /*----------------------------------------------------------------------------
1848  * Remove meshes which are associated with no writer
1849  *----------------------------------------------------------------------------*/
1850 
1851 static void
_clear_unused_meshes(void)1852 _clear_unused_meshes(void)
1853 {
1854   int  i;
1855   int *discard = NULL;
1856 
1857   cs_post_mesh_t  *post_mesh;
1858 
1859   /* Mark used meshes */
1860 
1861   BFT_MALLOC(discard, _cs_post_n_meshes, int);
1862 
1863   for (i = 0; i < _cs_post_n_meshes; i++) {
1864     post_mesh = _cs_post_meshes + i;
1865     if (post_mesh->n_writers == 0)
1866       discard[i] = 1;
1867     else
1868       discard[i] = 0;
1869   }
1870 
1871   for (i = 0; i < _cs_post_n_meshes; i++) {
1872     post_mesh = _cs_post_meshes + i;
1873     if (post_mesh->locate_ref > -1) {
1874       if (post_mesh->n_writers > 0)
1875         discard[post_mesh->locate_ref] = 0;
1876     }
1877   }
1878 
1879   /* Discard meshes not required, compacting array */
1880 
1881   for (i = _cs_post_n_meshes - 1; i >= 0; i--) {
1882     if (discard[i] == 1)
1883       _free_mesh(i);  /* shifts other meshes and reduces _cs_post_n_meshes */
1884   }
1885 
1886   BFT_FREE(discard);
1887 }
1888 
1889 /*----------------------------------------------------------------------------
1890  * Divide polygons or polyhedra in simpler elements if necessary.
1891  *
1892  * parameters:
1893  *   post_mesh <-> pointer to post-processing mesh
1894  *   writer    <-- pointer to associated writer
1895  *----------------------------------------------------------------------------*/
1896 
1897 static void
_divide_poly(cs_post_mesh_t * post_mesh,cs_post_writer_t * writer)1898 _divide_poly(cs_post_mesh_t    *post_mesh,
1899              cs_post_writer_t  *writer)
1900 {
1901   if (fvm_writer_needs_tesselation(writer->writer,
1902                                    post_mesh->exp_mesh,
1903                                    FVM_CELL_POLY) > 0)
1904     fvm_nodal_tesselate(post_mesh->_exp_mesh, FVM_CELL_POLY, NULL);
1905 
1906   if (fvm_writer_needs_tesselation(writer->writer,
1907                                    post_mesh->exp_mesh,
1908                                    FVM_FACE_POLY) > 0)
1909     fvm_nodal_tesselate(post_mesh->_exp_mesh, FVM_FACE_POLY, NULL);
1910 }
1911 
1912 /*----------------------------------------------------------------------------
1913  * Write parallel domain (rank) number to post-processing mesh
1914  *
1915  * parameters:
1916  *   writer     <-- FVM writer
1917  *   exp_mesh   <-- exportable mesh
1918  *   nt_cur_abs <-- current time step number
1919  *   t_cur_abs  <-- current physical time
1920  *---------------------------------------------------------------------------*/
1921 
1922 static void
_cs_post_write_domain(fvm_writer_t * writer,const fvm_nodal_t * exp_mesh,int nt_cur_abs,double t_cur_abs)1923 _cs_post_write_domain(fvm_writer_t       *writer,
1924                       const fvm_nodal_t  *exp_mesh,
1925                       int                 nt_cur_abs,
1926                       double              t_cur_abs)
1927 {
1928   int  dim_ent;
1929   cs_lnum_t  i, n_elts;
1930 
1931   cs_lnum_t  parent_num_shift[1]  = {0};
1932   int32_t  *domain = NULL;
1933 
1934   int _nt_cur_abs = -1;
1935   double _t_cur_abs = 0.;
1936 
1937   const int32_t   *var_ptr[1] = {NULL};
1938 
1939   if (cs_glob_n_ranks < 2)
1940     return;
1941 
1942   dim_ent = fvm_nodal_get_max_entity_dim(exp_mesh);
1943   n_elts = fvm_nodal_get_n_entities(exp_mesh, dim_ent);
1944 
1945   /* Prepare domain number */
1946 
1947   BFT_MALLOC(domain, n_elts, int32_t);
1948 
1949   for (i = 0; i < n_elts; i++)
1950     domain[i] = cs_glob_rank_id;
1951 
1952   /* Prepare post-processing output */
1953 
1954   var_ptr[0] = domain;
1955 
1956   if (fvm_writer_get_time_dep(writer) != FVM_WRITER_FIXED_MESH) {
1957     _nt_cur_abs = nt_cur_abs;
1958     _t_cur_abs = t_cur_abs;
1959   }
1960 
1961   fvm_writer_export_field(writer,
1962                           exp_mesh,
1963                           "mpi_rank_id",
1964                           FVM_WRITER_PER_ELEMENT,
1965                           1,
1966                           CS_INTERLACE,
1967                           0,
1968                           parent_num_shift,
1969                           CS_INT32,
1970                           _nt_cur_abs,
1971                           _t_cur_abs,
1972                           (const void * *)var_ptr);
1973 
1974   /* Free memory */
1975 
1976   BFT_FREE(domain);
1977 }
1978 
1979 /*----------------------------------------------------------------------------
1980  * Output fixed zone information if needed.
1981  *
1982  * This function is called when we know some writers are active
1983  *
1984  * parameters:
1985  *   writer     <-- FVM writer
1986  *   post_mesh  <-- postprocessing mesh
1987  *   nt_cur_abs <-- current time step number
1988  *   t_cur_abs  <-- current physical time
1989  *----------------------------------------------------------------------------*/
1990 
1991 static void
_cs_post_write_fixed_zone_info(fvm_writer_t * writer,const cs_post_mesh_t * post_mesh,int nt_cur_abs,double t_cur_abs)1992 _cs_post_write_fixed_zone_info(fvm_writer_t          *writer,
1993                                const cs_post_mesh_t  *post_mesh,
1994                                int                    nt_cur_abs,
1995                                double                 t_cur_abs)
1996 {
1997   assert(post_mesh->exp_mesh != NULL);
1998 
1999   bool output = false;
2000   const int   *var_ptr[1] = {NULL};
2001   const char  *name = NULL;
2002 
2003   if (post_mesh->id == CS_POST_MESH_VOLUME) {
2004 
2005     /* Ignore cases where all zones include all cells */
2006 
2007     int n_zones = cs_volume_zone_n_zones();
2008     int z_id = 0;
2009 
2010     for (z_id = 0; z_id < n_zones; z_id++) {
2011       const cs_zone_t  *z = cs_volume_zone_by_id(z_id);
2012       if (z->location_id != CS_MESH_LOCATION_CELLS)
2013         break;
2014     }
2015     if (z_id >= n_zones)
2016       return;
2017 
2018     const int *zone_id = cs_volume_zone_cell_zone_id();
2019     name = "volume zone id";
2020 
2021     if (cs_volume_zone_n_zones_time_varying() == 0) {
2022       output = true;
2023       var_ptr[0] = zone_id;
2024     }
2025 
2026   }
2027 
2028   else if (post_mesh->id == CS_POST_MESH_BOUNDARY) {
2029 
2030     /* Ignore cases where all zones include all boundary faces */
2031 
2032     int n_zones = cs_boundary_zone_n_zones();
2033     int z_id = 0;
2034 
2035     for (z_id = 0; z_id < n_zones; z_id++) {
2036       const cs_zone_t  *z = cs_boundary_zone_by_id(z_id);
2037       if (z->location_id != CS_MESH_LOCATION_BOUNDARY_FACES)
2038         break;
2039     }
2040     if (z_id >= n_zones)
2041       return;
2042 
2043     const int *zone_id = cs_boundary_zone_face_zone_id();
2044     name = "boundary zone id";
2045 
2046     if (cs_boundary_zone_n_zones_time_varying() == 0) {
2047       output = true;
2048       var_ptr[0] = zone_id;
2049     }
2050 
2051   }
2052 
2053   if (output) {
2054 
2055     cs_lnum_t  parent_num_shift[1]  = {0};
2056     int _nt_cur_abs = -1;
2057     double _t_cur_abs = 0.;
2058 
2059     if (fvm_writer_get_time_dep(writer) != FVM_WRITER_FIXED_MESH) {
2060       _nt_cur_abs = nt_cur_abs;
2061       _t_cur_abs = t_cur_abs;
2062     }
2063 
2064     fvm_writer_export_field(writer,
2065                             post_mesh->exp_mesh,
2066                             name,
2067                             FVM_WRITER_PER_ELEMENT,
2068                             1,
2069                             CS_INTERLACE,
2070                             1,
2071                             parent_num_shift,
2072                             CS_INT_TYPE,
2073                             _nt_cur_abs,
2074                             _t_cur_abs,
2075                             (const void * *)var_ptr);
2076 
2077   }
2078 }
2079 
2080 /*----------------------------------------------------------------------------
2081  * Output varying zone information if needed.
2082  *
2083  * This function is called when we know some writers are active
2084  *
2085  * parameters:
2086  *   ts <-- time step structure, or NULL
2087  *----------------------------------------------------------------------------*/
2088 
2089 static void
_cs_post_write_transient_zone_info(const cs_post_mesh_t * post_mesh,const cs_time_step_t * ts)2090 _cs_post_write_transient_zone_info(const cs_post_mesh_t  *post_mesh,
2091                                    const cs_time_step_t  *ts)
2092 {
2093   if (post_mesh->id == CS_POST_MESH_VOLUME) {
2094     if (cs_volume_zone_n_zones_time_varying() > 0) {
2095       cs_post_write_var(post_mesh->id,
2096                         CS_POST_WRITER_ALL_ASSOCIATED,
2097                         "volume zone id",
2098                         1,       /* var_dim */
2099                         true,    /* interlace */
2100                         true,    /* use_parent */
2101                         CS_POST_TYPE_int,
2102                         cs_volume_zone_cell_zone_id(),
2103                         NULL,
2104                         NULL,
2105                         ts);
2106     }
2107   }
2108 
2109   else if (post_mesh->id == CS_POST_MESH_BOUNDARY) {
2110     if (cs_boundary_zone_n_zones_time_varying() > 0)
2111       cs_post_write_var(post_mesh->id,
2112                         CS_POST_WRITER_ALL_ASSOCIATED,
2113                         "boundary zone id",
2114                         1,       /* var_dim */
2115                         true,    /* interlace */
2116                         true,    /* use_parent */
2117                         CS_POST_TYPE_int,
2118                         NULL,
2119                         NULL,
2120                         cs_boundary_zone_face_zone_id(),
2121                         ts);
2122   }
2123 }
2124 
2125 /*----------------------------------------------------------------------------
2126  * Output a post-processing mesh using associated writers.
2127  *
2128  * If the time step structure argument passed is NULL, a time-independent
2129  * output will be assumed.
2130  *
2131  * parameters:
2132  *   post_mesh  <-> pointer to post-processing mesh
2133  *   ts         <-- time step structure, or NULL
2134  *----------------------------------------------------------------------------*/
2135 
2136 static void
_cs_post_write_mesh(cs_post_mesh_t * post_mesh,const cs_time_step_t * ts)2137 _cs_post_write_mesh(cs_post_mesh_t        *post_mesh,
2138                     const cs_time_step_t  *ts)
2139 {
2140   int  j;
2141   fvm_writer_time_dep_t  time_dep;
2142   bool  write_mesh = false;
2143 
2144   cs_post_writer_t *writer = NULL;
2145 
2146   const int nt_cur = (ts != NULL) ? ts->nt_cur : -1;
2147   const double t_cur = (ts != NULL) ? ts->t_cur : 0.;
2148 
2149   /* Special case: particle trajectories can not be
2150      output before time stepping starts */
2151 
2152   if (post_mesh->ent_flag[3] == 2 && nt_cur < 1)
2153     return;
2154 
2155   /* Loop on writers */
2156 
2157   for (j = 0; j < post_mesh->n_writers; j++) {
2158 
2159     writer = _cs_post_writers + post_mesh->writer_id[j];
2160 
2161     if (writer->wd != NULL)
2162       time_dep = writer->wd->time_dep;
2163     else
2164       time_dep = fvm_writer_get_time_dep(writer->writer);
2165 
2166     write_mesh = false;
2167 
2168     if (   time_dep == FVM_WRITER_FIXED_MESH
2169         && writer->active > -1
2170         && post_mesh->ent_flag[3] != 2) {
2171       if (post_mesh->nt_last < -1)
2172         write_mesh = true;
2173     }
2174     else {
2175       if (post_mesh->nt_last < nt_cur && writer->active == 1)
2176         write_mesh = true;
2177     }
2178 
2179     if (write_mesh == true) {
2180 
2181       if (writer->writer == NULL)
2182         _init_writer(writer);
2183 
2184       if (post_mesh->exp_mesh == NULL)
2185         _define_mesh(post_mesh, ts);
2186 
2187       if (post_mesh->exp_mesh == NULL)
2188         continue;
2189 
2190       _divide_poly(post_mesh, writer);
2191 
2192       fvm_writer_set_mesh_time(writer->writer, nt_cur, t_cur);
2193       fvm_writer_export_nodal(writer->writer, post_mesh->exp_mesh);
2194 
2195       if (nt_cur >= 0) {
2196         writer->tc.last_nt = nt_cur;
2197         writer->tc.last_t = t_cur;
2198       }
2199 
2200     }
2201 
2202     if (write_mesh == true) {
2203 
2204       if (post_mesh->post_domain)
2205         _cs_post_write_domain(writer->writer,
2206                               post_mesh->exp_mesh,
2207                               nt_cur,
2208                               t_cur);
2209 
2210       _cs_post_write_fixed_zone_info(writer->writer,
2211                                      post_mesh,
2212                                      nt_cur,
2213                                      t_cur);
2214 
2215       if (nt_cur >= 0) {
2216         writer->tc.last_nt = nt_cur;
2217         writer->tc.last_t = t_cur;
2218       }
2219 
2220     }
2221 
2222   }
2223 
2224   if (write_mesh == true)
2225     post_mesh->nt_last = nt_cur;
2226 }
2227 
2228 /*----------------------------------------------------------------------------
2229  * Assemble variable values defined on a mix of interior and boundary
2230  * faces (with no indirection) into an array defined on a single faces set.
2231  *
2232  * The resulting variable is not interlaced.
2233  *
2234  * parameters:
2235  *   exp_mesh    <-- exportable mesh
2236  *   n_i_faces   <-- number of interior faces
2237  *   n_b_faces   <-- number of boundary faces
2238  *   var_dim     <-- variable dimension
2239  *   interlace   <-- for vector, interlace if 1, no interlace if 0
2240  *   i_face_vals <-- values at interior faces
2241  *   b_face_vals <-- values at boundary faces
2242  *   var_tmp[]   --> assembled values
2243  *----------------------------------------------------------------------------*/
2244 
2245 static void
_cs_post_assmb_var_faces(const fvm_nodal_t * exp_mesh,cs_lnum_t n_i_faces,cs_lnum_t n_b_faces,int var_dim,cs_interlace_t interlace,const cs_real_t i_face_vals[],const cs_real_t b_face_vals[],cs_real_t var_tmp[])2246 _cs_post_assmb_var_faces(const fvm_nodal_t  *exp_mesh,
2247                          cs_lnum_t           n_i_faces,
2248                          cs_lnum_t           n_b_faces,
2249                          int                 var_dim,
2250                          cs_interlace_t      interlace,
2251                          const cs_real_t     i_face_vals[],
2252                          const cs_real_t     b_face_vals[],
2253                          cs_real_t           var_tmp[])
2254 {
2255   cs_lnum_t  i, j, stride_1, stride_2;
2256 
2257   cs_lnum_t  n_elts = n_i_faces + n_b_faces;
2258 
2259   assert(exp_mesh != NULL);
2260 
2261   /* The variable is defined on interior and boundary faces of the
2262      post-processing mesh, and has been built using values
2263      at the corresponding interior and boundary faces */
2264 
2265   /* Boundary faces contribution */
2266 
2267   if (interlace == CS_INTERLACE) {
2268     stride_1 = var_dim;
2269     stride_2 = 1;
2270   }
2271   else {
2272     stride_1 = 1;
2273     stride_2 = n_b_faces;
2274   }
2275 
2276   for (i = 0; i < n_b_faces; i++) {
2277     for (j = 0; j < var_dim; j++)
2278       var_tmp[i + j*n_elts] = b_face_vals[i*stride_1 + j*stride_2];
2279   }
2280 
2281   /* Interior faces contribution */
2282 
2283   if (interlace == CS_INTERLACE) {
2284     stride_1 = var_dim;
2285     stride_2 = 1;
2286   }
2287   else {
2288     stride_1 = 1;
2289     stride_2 = n_i_faces;
2290   }
2291 
2292   for (i = 0; i < n_i_faces; i++) {
2293     for (j = 0; j < var_dim; j++)
2294       var_tmp[i + n_b_faces + j*n_elts] = i_face_vals[i*stride_1 + j*stride_2];
2295   }
2296 
2297 }
2298 
2299 /*----------------------------------------------------------------------------*/
2300 /*!
2301  * \brief Check if post-processing is activated and then update post-processing
2302  *        of meshes if there is a time-dependent mesh
2303  *
2304  * \param[in]  ts  time step status structure, or NULL
2305  */
2306 /*----------------------------------------------------------------------------*/
2307 
2308 static void
_update_meshes(const cs_time_step_t * ts)2309 _update_meshes(const cs_time_step_t  *ts)
2310 {
2311   bool  active;
2312 
2313   /* Loop on writers to check if something must be done */
2314   /*----------------------------------------------------*/
2315 
2316   int  w;
2317   for (w = 0; w < _cs_post_n_writers; w++) {
2318     cs_post_writer_t  *writer = _cs_post_writers + w;
2319     if (writer->active == 1)
2320       break;
2321   }
2322   if (w == _cs_post_n_writers)
2323     return;
2324 
2325   int t_top_id = cs_timer_stats_switch(_post_out_stat_id);
2326 
2327   /* Possible modification of post-processing meshes */
2328   /*-------------------------------------------------*/
2329 
2330   for (int i = 0; i < _cs_post_n_meshes; i++) {
2331 
2332     cs_post_mesh_t  *post_mesh = _cs_post_meshes + i;
2333 
2334     active = false;
2335 
2336     for (int j = 0; j < post_mesh->n_writers; j++) {
2337       cs_post_writer_t  *writer = _cs_post_writers + post_mesh->writer_id[j];
2338       if (writer->active == 1)
2339         active = true;
2340     }
2341 
2342     if (active == false)
2343       continue;
2344 
2345     /* Modifiable user mesh, active at this time step */
2346 
2347     if (post_mesh->mod_flag_min == FVM_WRITER_TRANSIENT_CONNECT)
2348       _redefine_mesh(post_mesh, ts);
2349 
2350     else if (post_mesh->ent_flag[4] != 0) {
2351       bool time_varying;
2352       cs_probe_set_t  *pset = (cs_probe_set_t *)post_mesh->sel_input[4];
2353       cs_probe_set_get_post_info(pset,
2354                                  &time_varying,
2355                                  NULL,
2356                                  NULL,
2357                                  NULL,
2358                                  NULL,
2359                                  NULL,
2360                                  NULL,
2361                                  NULL);
2362       if (time_varying) {
2363         cs_post_mesh_t *post_mesh_loc = _cs_post_meshes + post_mesh->locate_ref;
2364         cs_probe_set_locate(pset, post_mesh_loc->exp_mesh);
2365 
2366         /* Update associated mesh */
2367         fvm_nodal_t *exp_mesh
2368           = cs_probe_set_export_mesh(pset, cs_probe_set_get_name(pset));
2369         if (post_mesh->_exp_mesh != NULL)
2370           post_mesh->_exp_mesh = fvm_nodal_destroy(post_mesh->_exp_mesh);
2371         post_mesh->_exp_mesh = exp_mesh;
2372         post_mesh->exp_mesh = exp_mesh;
2373       }
2374     }
2375 
2376   }
2377 
2378   /* Output of meshes or vertex displacement field if necessary */
2379   /*------------------------------------------------------------*/
2380 
2381   cs_post_write_meshes(ts);
2382 
2383   cs_timer_stats_switch(t_top_id);
2384 }
2385 
2386 /*----------------------------------------------------------------------------
2387  * Generate global group flags array from local family flags
2388  *
2389  * The caller should free the returned array once it is no longer needed.
2390  *
2391  * parameters:
2392  *   mesh     <-- pointer to mesh structure
2393  *   fam_flag <-- flag values (size: mesh->n_families + 1)
2394  *
2395  * returns:
2396  *   group flag (size: mesh->n_groups)
2397  *----------------------------------------------------------------------------*/
2398 
2399 static char *
_build_group_flag(const cs_mesh_t * mesh,int * fam_flag)2400 _build_group_flag(const cs_mesh_t  *mesh,
2401                   int              *fam_flag)
2402 {
2403   int i, j;
2404 
2405   char *group_flag = NULL;
2406 
2407   BFT_MALLOC(group_flag, mesh->n_groups, char);
2408   memset(group_flag, 0, mesh->n_groups);
2409 
2410 #if defined(HAVE_MPI)
2411   if (cs_glob_n_ranks > 1) {
2412     int *_fam_flag = NULL;
2413     BFT_MALLOC(_fam_flag, mesh->n_families + 1, int);
2414     MPI_Allreduce(fam_flag, _fam_flag, mesh->n_families + 1,
2415                   MPI_INT, MPI_MAX, cs_glob_mpi_comm);
2416     memcpy(fam_flag, _fam_flag, (mesh->n_families + 1)*sizeof(int));
2417     BFT_FREE(_fam_flag);
2418   }
2419 #endif /* defined(HAVE_MPI) */
2420 
2421   for (i = 0; i < mesh->n_families; i++) {
2422     if (fam_flag[(i+1)] != 0) {
2423       char mask = fam_flag[i+1];
2424       for (j = 0; j < mesh->n_max_family_items; j++) {
2425         int g_id = - mesh->family_item[mesh->n_families*j + i] - 1;
2426         if (g_id >= 0)
2427           group_flag[g_id] = group_flag[g_id] | mask;
2428       }
2429     }
2430   }
2431 
2432   return group_flag;
2433 }
2434 
2435 /*----------------------------------------------------------------------------
2436  * Set a family flags array to 1 for families containg a given group,
2437  * and to 0 for others.
2438  *
2439  * parameters:
2440  *   mesh     <-- pointer to mesh structure
2441  *   g_id     <-- group id
2442  *   fam_flag --> flag values (size: mesh->n_families)
2443  *----------------------------------------------------------------------------*/
2444 
2445 static void
_set_fam_flags(const cs_mesh_t * mesh,int g_id,int * fam_flag)2446 _set_fam_flags(const cs_mesh_t  *mesh,
2447                int               g_id,
2448                int              *fam_flag)
2449 {
2450   int j, k;
2451   memset(fam_flag, 0, mesh->n_families*sizeof(int));
2452   for (j = 0; j < mesh->n_families; j++) {
2453     for (k = 0; k < mesh->n_max_family_items; k++) {
2454       int _g_id = - mesh->family_item[mesh->n_families*k + j] - 1;
2455       if (_g_id == g_id)
2456         fam_flag[j] = 1;
2457     }
2458   }
2459 }
2460 
2461 /*----------------------------------------------------------------------------
2462  * Output volume sub-meshes by group
2463  *
2464  * parameters:
2465  *   mesh      <-- base mesh
2466  *   fmt_name  <-- format name
2467  *   fmt_opts  <-- format options
2468  *---------------------------------------------------------------------------*/
2469 
2470 static void
_vol_submeshes_by_group(const cs_mesh_t * mesh,const char * fmt_name,const char * fmt_opts)2471 _vol_submeshes_by_group(const cs_mesh_t  *mesh,
2472                         const char       *fmt_name,
2473                         const char       *fmt_opts)
2474 {
2475   cs_lnum_t i, j;
2476   cs_lnum_t n_cells, n_i_faces, n_b_faces;
2477   char part_name[81];
2478   int max_null_family = 0;
2479   int *fam_flag = NULL;
2480   char *group_flag = NULL;
2481   cs_lnum_t *cell_list = NULL, *i_face_list = NULL, *b_face_list = NULL;
2482   fvm_writer_t *writer = NULL;
2483   fvm_nodal_t *exp_mesh = NULL;
2484 
2485   if (mesh->n_families == 0)
2486     return;
2487 
2488   /* Families should be sorted, so if a nonzero family is empty,
2489      it is family 1 */
2490 
2491   if (mesh->family_item[0] == 0)
2492     max_null_family = 1;
2493 
2494   if (mesh->n_families <= max_null_family)
2495     return;
2496 
2497   /* Get writer info */
2498 
2499   /* Default values */
2500 
2501   /* Create default writer */
2502 
2503   writer = fvm_writer_init("mesh_groups",
2504                            _cs_post_dirname,
2505                            fmt_name,
2506                            fmt_opts,
2507                            FVM_WRITER_FIXED_MESH);
2508 
2509   /* Now detect which groups may be referenced */
2510 
2511   BFT_MALLOC(fam_flag, (mesh->n_families + 1), int);
2512   memset(fam_flag, 0, (mesh->n_families + 1) * sizeof(int));
2513 
2514   if (mesh->cell_family != NULL) {
2515     for (i = 0; i < mesh->n_cells; i++)
2516       fam_flag[mesh->cell_family[i]]
2517         = fam_flag[mesh->cell_family[i]] | 1;
2518   }
2519   if (mesh->i_face_family != NULL) {
2520     for (i = 0; i < mesh->n_i_faces; i++)
2521       fam_flag[mesh->i_face_family[i]]
2522         = fam_flag[mesh->i_face_family[i]] | 2;
2523   }
2524   if (mesh->b_face_family != NULL) {
2525     for (i = 0; i < mesh->n_b_faces; i++)
2526       fam_flag[mesh->b_face_family[i]]
2527         = fam_flag[mesh->b_face_family[i]] | 4;
2528   }
2529 
2530   group_flag = _build_group_flag(mesh, fam_flag);
2531 
2532   /* Now extract volume elements by groups.
2533      Note that selector structures may not have been initialized yet,
2534      so to avoid issue, we use a direct selection here. */
2535 
2536   BFT_REALLOC(fam_flag, mesh->n_families, int);
2537 
2538   BFT_MALLOC(cell_list, mesh->n_cells, cs_lnum_t);
2539 
2540   for (i = 0; i < mesh->n_groups; i++) {
2541 
2542     if (group_flag[i] & '\1') {
2543 
2544       const char *g_name = mesh->group + mesh->group_idx[i];
2545 
2546       _set_fam_flags(mesh, i, fam_flag);
2547 
2548       for (j = 0, n_cells = 0; j < mesh->n_cells; j++) {
2549         int f_id = mesh->cell_family[j];
2550         if (f_id > 0 && fam_flag[f_id - 1])
2551           cell_list[n_cells++] = j + 1;
2552       }
2553       strcpy(part_name, "vol: ");
2554       strncat(part_name, g_name, 80 - strlen(part_name));
2555       exp_mesh = cs_mesh_connect_cells_to_nodal(mesh,
2556                                                 part_name,
2557                                                 false,
2558                                                 n_cells,
2559                                                 cell_list);
2560 
2561       if (fvm_writer_needs_tesselation(writer, exp_mesh, FVM_CELL_POLY) > 0)
2562         fvm_nodal_tesselate(exp_mesh, FVM_CELL_POLY, NULL);
2563 
2564       fvm_writer_set_mesh_time(writer, -1, 0);
2565       fvm_writer_export_nodal(writer, exp_mesh);
2566 
2567       exp_mesh = fvm_nodal_destroy(exp_mesh);
2568     }
2569 
2570   }
2571 
2572   /* Now export cells with no groups */
2573 
2574   if (mesh->cell_family != NULL) {
2575     for (j = 0, n_cells = 0; j < mesh->n_cells; j++) {
2576       if (mesh->cell_family[j] <= max_null_family)
2577         cell_list[n_cells++] = j + 1;
2578     }
2579   }
2580   else {
2581     for (j = 0, n_cells = 0; j < mesh->n_cells; j++)
2582       cell_list[n_cells++] = j + 1;
2583   }
2584 
2585   i = n_cells;
2586   cs_parall_counter_max(&i, 1);
2587 
2588   if (i > 0) {
2589     exp_mesh = cs_mesh_connect_cells_to_nodal(mesh,
2590                                               "vol: no_group",
2591                                               false,
2592                                               n_cells,
2593                                               cell_list);
2594 
2595     if (fvm_writer_needs_tesselation(writer, exp_mesh, FVM_CELL_POLY) > 0)
2596       fvm_nodal_tesselate(exp_mesh, FVM_CELL_POLY, NULL);
2597 
2598     fvm_writer_set_mesh_time(writer, -1, 0);
2599     fvm_writer_export_nodal(writer, exp_mesh);
2600 
2601     exp_mesh = fvm_nodal_destroy(exp_mesh);
2602   }
2603 
2604   BFT_FREE(cell_list);
2605 
2606   /* Now extract faces by groups */
2607 
2608   BFT_MALLOC(i_face_list, mesh->n_i_faces, cs_lnum_t);
2609   BFT_MALLOC(b_face_list, mesh->n_b_faces, cs_lnum_t);
2610 
2611   for (i = 0; i < mesh->n_groups; i++) {
2612 
2613     if ((group_flag[i] & '\2') || (group_flag[i] & '\4')) {
2614 
2615       const char *g_name = mesh->group + mesh->group_idx[i];
2616 
2617       _set_fam_flags(mesh, i, fam_flag);
2618 
2619       n_i_faces = 0;
2620       if (mesh->i_face_family != NULL) {
2621         for (j = 0; j < mesh->n_i_faces; j++) {
2622           int f_id = mesh->i_face_family[j];
2623           if (f_id > 0 && fam_flag[f_id - 1])
2624             i_face_list[n_i_faces++] = j + 1;
2625         }
2626       }
2627       n_b_faces = 0;
2628       if (mesh->b_face_family != NULL) {
2629         for (j = 0; j < mesh->n_b_faces; j++) {
2630           int f_id = mesh->b_face_family[j];
2631           if (f_id > 0 && fam_flag[f_id - 1])
2632             b_face_list[n_b_faces++] = j + 1;
2633         }
2634       }
2635 
2636       strcpy(part_name, "surf: ");
2637       strncat(part_name, g_name, 80 - strlen(part_name));
2638       exp_mesh = cs_mesh_connect_faces_to_nodal(cs_glob_mesh,
2639                                                 part_name,
2640                                                 false,
2641                                                 n_i_faces,
2642                                                 n_b_faces,
2643                                                 i_face_list,
2644                                                 b_face_list);
2645 
2646       if (fvm_writer_needs_tesselation(writer, exp_mesh, FVM_FACE_POLY) > 0)
2647         fvm_nodal_tesselate(exp_mesh, FVM_FACE_POLY, NULL);
2648 
2649       fvm_writer_set_mesh_time(writer, -1, 0);
2650       fvm_writer_export_nodal(writer, exp_mesh);
2651 
2652       exp_mesh = fvm_nodal_destroy(exp_mesh);
2653     }
2654 
2655   }
2656 
2657   writer = fvm_writer_finalize(writer);
2658 
2659   BFT_FREE(b_face_list);
2660   BFT_FREE(i_face_list);
2661 
2662   BFT_FREE(fam_flag);
2663   BFT_FREE(group_flag);
2664 }
2665 
2666 /*----------------------------------------------------------------------------
2667  * Output boundary sub-meshes by group, if it contains multiple groups.
2668  *
2669  * parameters:
2670  *   mesh        <-- base mesh
2671  *   fmt_name    <-- format name
2672  *   fmt_opts    <-- format options
2673  *---------------------------------------------------------------------------*/
2674 
2675 static void
_boundary_submeshes_by_group(const cs_mesh_t * mesh,const char * fmt_name,const char * fmt_opts)2676 _boundary_submeshes_by_group(const cs_mesh_t   *mesh,
2677                              const char        *fmt_name,
2678                              const char        *fmt_opts)
2679 {
2680   cs_lnum_t i, j;
2681   cs_lnum_t n_b_faces;
2682   cs_gnum_t n_no_group = 0;
2683   int max_null_family = 0;
2684   int *fam_flag = NULL;
2685   char *group_flag = NULL;
2686   cs_lnum_t *b_face_list = NULL;
2687   fvm_writer_t *writer = NULL;
2688   fvm_nodal_t *exp_mesh = NULL;
2689 
2690   if (mesh->n_families == 0)
2691     return;
2692 
2693   /* Families should be sorted, so if a nonzero family is empty,
2694      it is family 1 */
2695 
2696   if (mesh->family_item[0] == 0)
2697     max_null_family = 1;
2698 
2699   if (mesh->n_families <= max_null_family)
2700     return;
2701 
2702   /* Check how many boundary faces belong to no group */
2703 
2704   if (mesh->b_face_family != NULL) {
2705     for (j = 0, n_b_faces = 0; j < mesh->n_b_faces; j++) {
2706       if (mesh->b_face_family[j] <= max_null_family)
2707         n_no_group += 1;
2708     }
2709   }
2710   else
2711     n_no_group = mesh->n_b_faces;
2712 
2713   cs_parall_counter(&n_no_group, 1);
2714 
2715   if (n_no_group == mesh->n_g_b_faces)
2716     return;
2717 
2718   /* Get writer info */
2719 
2720   /* Default values */
2721 
2722   /* Create default writer */
2723 
2724   writer = fvm_writer_init("boundary_groups",
2725                            _cs_post_dirname,
2726                            fmt_name,
2727                            fmt_opts,
2728                            FVM_WRITER_FIXED_MESH);
2729 
2730   /* Now detect which groups may be referenced */
2731 
2732   BFT_MALLOC(fam_flag, mesh->n_families + 1, int);
2733   memset(fam_flag, 0, (mesh->n_families + 1)*sizeof(int));
2734 
2735   if (mesh->b_face_family != NULL) {
2736     for (i = 0; i < mesh->n_b_faces; i++)
2737       fam_flag[mesh->b_face_family[i]] = 1;
2738   }
2739 
2740   group_flag = _build_group_flag(mesh, fam_flag);
2741 
2742   /* Now extract boundary faces by groups.
2743      Note that selector structures may not have been initialized yet,
2744      so to avoid issue, we use a direct selection here. */
2745 
2746   BFT_REALLOC(fam_flag, mesh->n_families, int);
2747 
2748   BFT_MALLOC(b_face_list, mesh->n_b_faces, cs_lnum_t);
2749 
2750   for (i = 0; i < mesh->n_groups; i++) {
2751 
2752     if (group_flag[i] != 0) {
2753 
2754       const char *g_name = mesh->group + mesh->group_idx[i];
2755 
2756       _set_fam_flags(mesh, i, fam_flag);
2757 
2758       n_b_faces = 0;
2759       if (mesh->b_face_family != NULL) {
2760         for (j = 0; j < mesh->n_b_faces; j++) {
2761           int f_id = mesh->b_face_family[j];
2762           if (f_id > 0 && fam_flag[f_id - 1])
2763             b_face_list[n_b_faces++] = j + 1;
2764         }
2765       }
2766 
2767       exp_mesh = cs_mesh_connect_faces_to_nodal(cs_glob_mesh,
2768                                                 g_name,
2769                                                 false,
2770                                                 0,
2771                                                 n_b_faces,
2772                                                 NULL,
2773                                                 b_face_list);
2774 
2775       if (fvm_writer_needs_tesselation(writer, exp_mesh, FVM_FACE_POLY) > 0)
2776         fvm_nodal_tesselate(exp_mesh, FVM_FACE_POLY, NULL);
2777 
2778       fvm_writer_set_mesh_time(writer, -1, 0);
2779       fvm_writer_export_nodal(writer, exp_mesh);
2780 
2781       exp_mesh = fvm_nodal_destroy(exp_mesh);
2782     }
2783 
2784   }
2785 
2786   /* Output boundary faces belonging to no group */
2787 
2788   if (n_no_group > 0) {
2789 
2790     if (mesh->b_face_family != NULL) {
2791       for (j = 0, n_b_faces = 0; j < mesh->n_b_faces; j++) {
2792         if (mesh->b_face_family[j] <= max_null_family)
2793           b_face_list[n_b_faces++] = j + 1;
2794       }
2795     }
2796     else {
2797       for (j = 0, n_b_faces = 0; j < mesh->n_b_faces; j++)
2798         b_face_list[n_b_faces++] = j + 1;
2799     }
2800 
2801     exp_mesh = cs_mesh_connect_faces_to_nodal(cs_glob_mesh,
2802                                               "no_group",
2803                                               false,
2804                                               0,
2805                                               n_b_faces,
2806                                               NULL,
2807                                               b_face_list);
2808 
2809     if (fvm_writer_needs_tesselation(writer, exp_mesh, FVM_FACE_POLY) > 0)
2810       fvm_nodal_tesselate(exp_mesh, FVM_FACE_POLY, NULL);
2811 
2812     fvm_writer_set_mesh_time(writer, -1, 0);
2813     fvm_writer_export_nodal(writer, exp_mesh);
2814 
2815     exp_mesh = fvm_nodal_destroy(exp_mesh);
2816   }
2817 
2818   BFT_FREE(b_face_list);
2819 
2820   writer = fvm_writer_finalize(writer);
2821 
2822   BFT_FREE(fam_flag);
2823   BFT_FREE(group_flag);
2824 }
2825 
2826 /*----------------------------------------------------------------------------
2827  * Check if a given field matches the profile for post_write_var.
2828  *
2829  * parameters:
2830  *   post_mesh      <-- postprocessing mesh
2831  *   field_loc_type <-- associated field location type
2832  *
2833  * return:
2834  *   true if the field matches the profile for post_writer_var
2835  *----------------------------------------------------------------------------*/
2836 
2837 static bool
_cs_post_match_post_write_var(const cs_post_mesh_t * post_mesh,cs_mesh_location_type_t field_loc_type)2838 _cs_post_match_post_write_var(const cs_post_mesh_t     *post_mesh,
2839                               cs_mesh_location_type_t   field_loc_type)
2840 {
2841   bool match = false;
2842 
2843   if (post_mesh->ent_flag[CS_POST_LOCATION_CELL] == 1) {
2844     if (   field_loc_type == CS_MESH_LOCATION_CELLS
2845         || field_loc_type == CS_MESH_LOCATION_VERTICES)
2846       match = true;
2847   }
2848 
2849   else if (post_mesh->ent_flag[CS_POST_LOCATION_B_FACE] == 1) {
2850     if (field_loc_type == CS_MESH_LOCATION_VERTICES)
2851       match = true;
2852     else if (   field_loc_type == CS_MESH_LOCATION_BOUNDARY_FACES
2853              && post_mesh->ent_flag[CS_POST_LOCATION_I_FACE] == 0)
2854       match = true;
2855   }
2856 
2857   else if (post_mesh->ent_flag[CS_POST_LOCATION_I_FACE] == 1) {
2858     if (field_loc_type == CS_MESH_LOCATION_VERTICES)
2859       match = true;
2860     else if (field_loc_type == CS_MESH_LOCATION_INTERIOR_FACES)
2861       match = true;
2862   }
2863 
2864   return match;
2865 }
2866 
2867 /*----------------------------------------------------------------------------
2868  * Extract component from field values.
2869  *
2870  * The caller is responsible for freeing the returned array.
2871  *
2872  * parameters:
2873  *   f            <-- pointer to associated field
2874  *   comp_id      <-- id of component to extract
2875  *   name         <-- base name
2876  *   name_buf     --> name with dimension extension added
2877  *
2878  * return:
2879  *   array restricted to selected component
2880  *----------------------------------------------------------------------------*/
2881 
2882 static cs_real_t *
_extract_field_component(const cs_field_t * f,cs_lnum_t comp_id,const char * name,char name_buf[96])2883 _extract_field_component(const cs_field_t  *f,
2884                          cs_lnum_t          comp_id,
2885                          const char        *name,
2886                          char               name_buf[96])
2887 {
2888   strncpy(name_buf, name, 90);
2889   name_buf[90] = '\0';
2890   switch (f->dim) {
2891   case 3:
2892     strncat(name_buf, cs_glob_field_comp_name_3[comp_id], 5);
2893     break;
2894   case 6:
2895     strncat(name_buf, cs_glob_field_comp_name_6[comp_id], 5);
2896     break;
2897   case 9:
2898     strncat(name_buf, cs_glob_field_comp_name_9[comp_id], 5);
2899     break;
2900   default:
2901     snprintf(name_buf + strlen(name_buf), 5, "[%ld]", (long)comp_id);
2902   }
2903   name_buf[95] = '\0';
2904 
2905   const cs_lnum_t dim = f->dim;
2906   const cs_lnum_t n_elts = cs_mesh_location_get_n_elts(f->location_id)[0];
2907   const cs_real_t *src_val = f->val;
2908 
2909   cs_real_t *val;
2910   BFT_MALLOC(val, n_elts, cs_real_t);
2911 
2912   for (cs_lnum_t i = 0; i < n_elts; i++)
2913     val[i] = src_val[i*dim + comp_id];
2914 
2915   return val;
2916 }
2917 
2918 /*----------------------------------------------------------------------------
2919  * Output coordinates for profiles if requested.
2920  *
2921  * parameters:
2922  *   post_mesh   <-- pointer to post-processing mesh structure
2923  *   ts          <-- time step status structure, or NULL
2924  *----------------------------------------------------------------------------*/
2925 
2926 static void
_cs_post_output_profile_coords(cs_post_mesh_t * post_mesh,const cs_time_step_t * ts)2927 _cs_post_output_profile_coords(cs_post_mesh_t        *post_mesh,
2928                                const cs_time_step_t  *ts)
2929 {
2930   assert(post_mesh != NULL);
2931 
2932   const cs_probe_set_t  *pset = (cs_probe_set_t *)post_mesh->sel_input[4];
2933 
2934   bool auto_curve_coo, auto_cart_coo;
2935 
2936   cs_probe_set_get_post_info(pset,
2937                              NULL,
2938                              NULL,
2939                              NULL,
2940                              NULL,
2941                              &auto_curve_coo,
2942                              &auto_cart_coo,
2943                              NULL,
2944                              NULL);
2945 
2946   if (auto_curve_coo) {
2947     cs_real_t *s = cs_probe_set_get_loc_curvilinear_abscissa(pset);
2948     cs_post_write_probe_values(post_mesh->id,
2949                                CS_POST_WRITER_ALL_ASSOCIATED,
2950                                "s",
2951                                1,
2952                                CS_POST_TYPE_cs_real_t,
2953                                0,
2954                                NULL,
2955                                NULL,
2956                                s,
2957                                ts);
2958     BFT_FREE(s);
2959   }
2960 
2961   if (auto_cart_coo) {
2962 
2963     int nt_cur = (ts != NULL) ? ts->nt_cur : -1;
2964     double t_cur = (ts != NULL) ? ts->t_cur : 0.;
2965 
2966     const cs_lnum_t  n_points = fvm_nodal_get_n_entities(post_mesh->exp_mesh, 0);
2967     cs_coord_t  *point_coords;
2968     BFT_MALLOC(point_coords, n_points*3, cs_coord_t);
2969     fvm_nodal_get_vertex_coords(post_mesh->exp_mesh,
2970                                 CS_INTERLACE,
2971                                 point_coords);
2972 
2973     for (int i = 0; i < post_mesh->n_writers; i++) {
2974 
2975       cs_post_writer_t *writer = _cs_post_writers + post_mesh->writer_id[i];
2976 
2977       if (writer->active == 1 && writer->writer != NULL) {
2978         const char *fmt = fvm_writer_get_format(writer->writer);
2979         if (strcmp(fmt, "plot"))
2980           continue;
2981 
2982         cs_lnum_t  parent_num_shift[1] = {0};
2983         const void  *var_ptr[1] = {point_coords};
2984 
2985         fvm_writer_export_field(writer->writer,
2986                                 post_mesh->exp_mesh,
2987                                 "",  /* var_name */
2988                                 FVM_WRITER_PER_NODE,
2989                                 3,
2990                                 CS_INTERLACE,
2991                                 0, /* n_parent_lists */
2992                                 parent_num_shift,
2993                                 CS_COORD_TYPE,
2994                                 nt_cur,
2995                                 t_cur,
2996                                 (const void **)var_ptr);
2997 
2998         if (nt_cur >= 0) {
2999           writer->tc.last_nt = nt_cur;
3000           writer->tc.last_t = t_cur;
3001         }
3002       }
3003 
3004     } /* End of loop on writers */
3005 
3006     BFT_FREE(point_coords);
3007 
3008   }
3009 }
3010 
3011 /*----------------------------------------------------------------------------
3012  * Main post-processing output of variables.
3013  *
3014  * parameters:
3015  *   post_mesh   <-- pointer to post-processing mesh structure
3016  *   ts          <-- time step status structure, or NULL
3017  *----------------------------------------------------------------------------*/
3018 
3019 static void
_cs_post_output_fields(cs_post_mesh_t * post_mesh,const cs_time_step_t * ts)3020 _cs_post_output_fields(cs_post_mesh_t        *post_mesh,
3021                        const cs_time_step_t  *ts)
3022 {
3023   int  pset_interpolation = 0;
3024   bool pset_on_boundary = false;
3025   cs_probe_set_t  *pset = (cs_probe_set_t *)post_mesh->sel_input[4];
3026 
3027   if (pset != NULL) {
3028     cs_probe_set_get_post_info(pset,
3029                                NULL,
3030                                &pset_on_boundary,
3031                                NULL,
3032                                NULL,
3033                                NULL,
3034                                NULL,
3035                                NULL,
3036                                NULL);
3037     if (pset_on_boundary == false && cs_probe_set_get_interpolation(pset) == 1)
3038       pset_interpolation = 1;
3039   }
3040 
3041   /* Base output for cell and boundary meshes */
3042   /*------------------------------------------*/
3043 
3044   if (   post_mesh->cat_id == CS_POST_MESH_VOLUME
3045       || post_mesh->cat_id == CS_POST_MESH_BOUNDARY
3046       || post_mesh->cat_id == CS_POST_MESH_SURFACE) {
3047 
3048     const int n_fields = cs_field_n_fields();
3049     const int vis_key_id = cs_field_key_id("post_vis");
3050     const int label_key_id = cs_field_key_id("label");
3051 
3052     /* Loop on fields */
3053 
3054     for (int f_id = 0; f_id < n_fields; f_id++) {
3055 
3056       const bool  use_parent = true;
3057 
3058       const cs_field_t  *f = cs_field_by_id(f_id);
3059 
3060       const cs_mesh_location_type_t field_loc_type
3061          = cs_mesh_location_get_type(f->location_id);
3062 
3063       if (_cs_post_match_post_write_var(post_mesh, field_loc_type) == false)
3064         continue;
3065 
3066       if (! (cs_field_get_key_int(f, vis_key_id) & CS_POST_ON_LOCATION))
3067         continue;
3068 
3069       const char *name = cs_field_get_key_str(f, label_key_id);
3070       if (name == NULL)
3071         name = f->name;
3072 
3073       if (pset != NULL) {
3074         char interpolate_input[96];
3075         strncpy(interpolate_input, f->name, 95); interpolate_input[95] = '\0';
3076 
3077         cs_interpolate_from_location_t
3078           *interpolate_func = cs_interpolate_from_location_p0;
3079         if (   field_loc_type == CS_MESH_LOCATION_CELLS
3080             && pset_interpolation == 1)
3081           interpolate_func = cs_interpolate_from_location_p1;
3082 
3083         cs_post_write_probe_values(post_mesh->id,
3084                                    CS_POST_WRITER_ALL_ASSOCIATED,
3085                                    name,
3086                                    f->dim,
3087                                    CS_POST_TYPE_cs_real_t,
3088                                    f->location_id,
3089                                    interpolate_func,
3090                                    interpolate_input,
3091                                    f->val,
3092                                    ts);
3093       }
3094 
3095       else if (   field_loc_type == CS_MESH_LOCATION_CELLS
3096                || field_loc_type == CS_MESH_LOCATION_BOUNDARY_FACES
3097                || field_loc_type == CS_MESH_LOCATION_INTERIOR_FACES) {
3098 
3099         const cs_real_t *cell_val = NULL, *b_face_val = NULL;
3100 
3101         if (field_loc_type == CS_MESH_LOCATION_CELLS)
3102           cell_val = f->val;
3103         else /* if (field_loc_type == CS_MESH_LOCATION_BOUNDARY_FACES) */
3104           b_face_val = f->val;
3105 
3106         cs_post_write_var(post_mesh->id,
3107                           CS_POST_WRITER_ALL_ASSOCIATED,
3108                           name,
3109                           f->dim,
3110                           true,
3111                           use_parent,
3112                           CS_POST_TYPE_cs_real_t,
3113                           cell_val,
3114                           NULL,
3115                           b_face_val,
3116                           ts);
3117       }
3118 
3119       else if (field_loc_type == CS_MESH_LOCATION_VERTICES)
3120         cs_post_write_vertex_var(post_mesh->id,
3121                                  CS_POST_WRITER_ALL_ASSOCIATED,
3122                                  name,
3123                                  f->dim,
3124                                  true,
3125                                  use_parent,
3126                                  CS_POST_TYPE_cs_real_t,
3127                                  f->val,
3128                                  ts);
3129 
3130     } /* End of loop on fields */
3131 
3132   } /* End of main output for cell or boundary mesh or submesh */
3133 
3134   /* Base output for probes */
3135   /*------------------------*/
3136 
3137   else if (post_mesh->cat_id == CS_POST_MESH_PROBES) {
3138 
3139     const int n_fields = cs_field_n_fields();
3140     const int vis_key_id = cs_field_key_id("post_vis");
3141     const int label_key_id = cs_field_key_id("label");
3142 
3143     /* Loop on fields */
3144 
3145     for (int f_id = 0; f_id < n_fields; f_id++) {
3146 
3147       const cs_field_t  *f = cs_field_by_id(f_id);
3148 
3149       const cs_mesh_location_type_t field_loc_type
3150         = cs_mesh_location_get_type(f->location_id);
3151 
3152       if (pset_on_boundary) {
3153         if (   field_loc_type != CS_MESH_LOCATION_CELLS
3154             && field_loc_type != CS_MESH_LOCATION_BOUNDARY_FACES
3155             && field_loc_type != CS_MESH_LOCATION_VERTICES)
3156           continue;
3157       }
3158       else {
3159         if (   field_loc_type != CS_MESH_LOCATION_CELLS
3160             && field_loc_type != CS_MESH_LOCATION_VERTICES)
3161           continue;
3162       }
3163 
3164       if (! (cs_field_get_key_int(f, vis_key_id) & CS_POST_MONITOR))
3165         continue;
3166 
3167       const char *name = cs_field_get_key_str(f, label_key_id);
3168       if (name == NULL)
3169         name = f->name;
3170 
3171       cs_interpolate_from_location_t
3172         *interpolate_func = cs_interpolate_from_location_p0;
3173       if (   field_loc_type == CS_MESH_LOCATION_CELLS
3174           && pset_interpolation == 1)
3175         interpolate_func = cs_interpolate_from_location_p1;
3176 
3177       char interpolate_input[96];
3178       strncpy(interpolate_input, f->name, 95); interpolate_input[95] = '\0';
3179 
3180       cs_post_write_probe_values(post_mesh->id,
3181                                  CS_POST_WRITER_ALL_ASSOCIATED,
3182                                  name,
3183                                  f->dim,
3184                                  CS_POST_TYPE_cs_real_t,
3185                                  f->location_id,
3186                                  interpolate_func,
3187                                  interpolate_input,
3188                                  f->val,
3189                                  ts);
3190 
3191     } /* End of loop on fields */
3192 
3193   } /* End of main output for probes */
3194 
3195   /* Special case for mesh displacement even when mesh category does
3196      not indicate automatic propagation to sub-meshes
3197      --------------------------------------------------------------- */
3198 
3199   else if (   post_mesh->ent_flag[0]
3200            || post_mesh->ent_flag[1]
3201            || post_mesh->ent_flag[2]) {
3202 
3203     const cs_field_t  *f = cs_field_by_name_try("mesh_displacement");
3204 
3205     if (   f != NULL
3206         && fvm_nodal_get_parent(post_mesh->exp_mesh) == cs_glob_mesh) {
3207 
3208       const cs_mesh_location_type_t field_loc_type
3209          = cs_mesh_location_get_type(f->location_id);
3210 
3211       if (field_loc_type == CS_MESH_LOCATION_VERTICES) {
3212 
3213         const int vis_key_id = cs_field_key_id("post_vis");
3214 
3215         if (cs_field_get_key_int(f, vis_key_id) & CS_POST_ON_LOCATION) {
3216 
3217           const int label_key_id = cs_field_key_id("label");
3218           const char *name = cs_field_get_key_str(f, label_key_id);
3219           if (name == NULL)
3220             name = f->name;
3221 
3222           cs_post_write_vertex_var(post_mesh->id,
3223                                    CS_POST_WRITER_ALL_ASSOCIATED,
3224                                    name,
3225                                    f->dim,
3226                                    true,
3227                                    true, /* use_parent */
3228                                    CS_POST_TYPE_cs_real_t,
3229                                    f->val,
3230                                    ts);
3231 
3232         }
3233       }
3234 
3235     }
3236 
3237   } /* End of special output for mesh displacement */
3238 }
3239 
3240 /*----------------------------------------------------------------------------
3241  * Post-processing output of additional associated fields.
3242  *
3243  * parameters:
3244  *   post_mesh   <-- pointer to post-processing mesh structure
3245  *   ts          <-- time step status structure, or NULL
3246  *----------------------------------------------------------------------------*/
3247 
3248 static void
_cs_post_output_attached_fields(cs_post_mesh_t * post_mesh,const cs_time_step_t * ts)3249 _cs_post_output_attached_fields(cs_post_mesh_t        *post_mesh,
3250                                 const cs_time_step_t  *ts)
3251 {
3252   const int label_key_id = cs_field_key_id("label");
3253 
3254   bool pset_on_boundary = false;
3255   cs_probe_set_t  *pset = (cs_probe_set_t *)post_mesh->sel_input[4];
3256 
3257   cs_interpolate_from_location_t
3258     *interpolate_func = cs_interpolate_from_location_p0;
3259 
3260   if (pset != NULL) {
3261     cs_probe_set_get_post_info(pset,
3262                                NULL,
3263                                &pset_on_boundary,
3264                                NULL,
3265                                NULL,
3266                                NULL,
3267                                NULL,
3268                                NULL,
3269                                NULL);
3270     if (pset_on_boundary == false && cs_probe_set_get_interpolation(pset) == 1)
3271       interpolate_func = cs_interpolate_from_location_p1;
3272   }
3273 
3274   for (int i = 0; i < post_mesh->n_a_fields; i++) {
3275 
3276     bool  use_parent = true;
3277 
3278     char name_buf[96];
3279 
3280     int writer_id = post_mesh->a_field_info[i*3];
3281     int f_id = post_mesh->a_field_info[i*3+1];
3282     int comp_id = post_mesh->a_field_info[i*3+2];
3283 
3284     const cs_field_t  *f = cs_field_by_id(f_id);
3285 
3286     const cs_mesh_location_type_t field_loc_type
3287       = cs_mesh_location_get_type(f->location_id);
3288 
3289     const char *name = cs_field_get_key_str(f, label_key_id);
3290     if (name == NULL)
3291       name = f->name;
3292 
3293     cs_interpolate_from_location_t
3294       *_interpolate_func = interpolate_func;
3295 
3296     int f_dim = f->dim;
3297     cs_real_t *_val = NULL;
3298     const cs_real_t *f_val = f->val;
3299 
3300     if (f->dim > 1 && comp_id > -1) {
3301       if (comp_id >= f->dim) /* only if input is incorrect */
3302         continue;
3303       else {
3304         _val = _extract_field_component(f, comp_id, name, name_buf);
3305         f_dim = 1;
3306         f_val = _val;
3307         name = name_buf;
3308         _interpolate_func = cs_interpolate_from_location_p0;
3309       }
3310     }
3311 
3312     /* Volume or surface mesh */
3313 
3314     if (_cs_post_match_post_write_var(post_mesh, field_loc_type)) {
3315 
3316       if (   field_loc_type == CS_MESH_LOCATION_CELLS
3317           || field_loc_type == CS_MESH_LOCATION_BOUNDARY_FACES
3318           || field_loc_type == CS_MESH_LOCATION_INTERIOR_FACES) {
3319 
3320         const cs_real_t *cell_val = NULL, *b_face_val = NULL;
3321 
3322         if (field_loc_type == CS_MESH_LOCATION_CELLS)
3323           cell_val = f_val;
3324         else /* if (field_loc_type == CS_MESH_LOCATION_BOUNDARY_FACES) */
3325           b_face_val = f_val;
3326 
3327         cs_post_write_var(post_mesh->id,
3328                           writer_id,
3329                           name,
3330                           f_dim,
3331                           true,
3332                           use_parent,
3333                           CS_POST_TYPE_cs_real_t,
3334                           cell_val,
3335                           NULL,
3336                           b_face_val,
3337                           ts);
3338       }
3339 
3340       else if (field_loc_type == CS_MESH_LOCATION_VERTICES)
3341         cs_post_write_vertex_var(post_mesh->id,
3342                                  writer_id,
3343                                  name,
3344                                  f_dim,
3345                                  true,
3346                                  use_parent,
3347                                  CS_POST_TYPE_cs_real_t,
3348                                  f_val,
3349                                  ts);
3350 
3351     }
3352 
3353     /* Probe or profile mesh */
3354 
3355     else if (   pset != NULL
3356              && (   field_loc_type == CS_MESH_LOCATION_CELLS
3357                  || field_loc_type == CS_MESH_LOCATION_BOUNDARY_FACES
3358                  || field_loc_type == CS_MESH_LOCATION_VERTICES)) {
3359 
3360       if (! pset_on_boundary
3361           && field_loc_type == CS_MESH_LOCATION_BOUNDARY_FACES)
3362         continue;
3363 
3364       char interpolate_input[96];
3365       strncpy(interpolate_input, f->name, 95); interpolate_input[95] = '\0';
3366 
3367       cs_post_write_probe_values(post_mesh->id,
3368                                  writer_id,
3369                                  name,
3370                                  f_dim,
3371                                  CS_POST_TYPE_cs_real_t,
3372                                  f->location_id,
3373                                  _interpolate_func,
3374                                  interpolate_input,
3375                                  f_val,
3376                                  ts);
3377 
3378     }
3379 
3380     BFT_FREE(_val);
3381   }
3382 }
3383 
3384 /*----------------------------------------------------------------------------*/
3385 /*!
3386  * \brief Transfer field output info from probe set to associated
3387  *        postprocessing mesh.
3388  *
3389  * \param[in]  post_mesh  postprocessing mesh structure
3390  */
3391 /*----------------------------------------------------------------------------*/
3392 
3393 static void
_attach_probe_set_fields(cs_post_mesh_t * post_mesh)3394 _attach_probe_set_fields(cs_post_mesh_t  *post_mesh)
3395 {
3396   cs_probe_set_t  *pset = (cs_probe_set_t *)post_mesh->sel_input[4];
3397 
3398   if (pset == NULL)
3399     return;
3400 
3401   int  ps_naf = 0;
3402   int *ps_afi = NULL;
3403 
3404   cs_probe_set_transfer_associated_field_info(pset, &ps_naf, &ps_afi);
3405 
3406   post_mesh->n_a_fields = 0;
3407   BFT_REALLOC(post_mesh->a_field_info, 3*ps_naf, int);
3408 
3409   const int vis_key_id = cs_field_key_id("post_vis");
3410   int vis_key_mask = 0;
3411   if (   post_mesh->cat_id == CS_POST_MESH_BOUNDARY
3412       || post_mesh->cat_id == CS_POST_MESH_VOLUME)
3413     vis_key_mask = CS_POST_ON_LOCATION;
3414   else if (post_mesh->cat_id == CS_POST_MESH_PROBES)
3415     vis_key_mask = CS_POST_MONITOR;
3416 
3417   for (int i = 0; i < ps_naf; i++) {
3418 
3419     int writer_id = ps_afi[i*3];
3420     int field_id = ps_afi[i*3 + 1];
3421     int comp_id = ps_afi[i*3 + 2];
3422 
3423     const cs_field_t  *f = cs_field_by_id(field_id);
3424 
3425     if (f == NULL)
3426       continue;
3427 
3428     /* Check that the field is not already output automatically */
3429 
3430     bool redundant = false;
3431 
3432     if (cs_field_get_key_int(f, vis_key_id) & vis_key_mask)
3433       redundant = true;
3434 
3435     if (! redundant) {
3436       for (int j = 0; j < post_mesh->n_a_fields; j++) {
3437         int *afi = post_mesh->a_field_info + 3*j;
3438         if (   afi[0] == writer_id && afi[1] == field_id
3439             && (afi[2] == comp_id || f->dim == 1)) {
3440           redundant = true;
3441           break;
3442         }
3443         afi += 3;
3444       }
3445     }
3446 
3447     if (! redundant) {
3448       int *afi = post_mesh->a_field_info + 3*post_mesh->n_a_fields;
3449       afi[0] = writer_id;
3450       afi[1] = field_id;
3451       afi[2] = comp_id;
3452       post_mesh->n_a_fields += 1;
3453     }
3454 
3455   }
3456 
3457   BFT_FREE(ps_afi);
3458   BFT_REALLOC(post_mesh->a_field_info, 3*post_mesh->n_a_fields, int);
3459 }
3460 
3461 /*----------------------------------------------------------------------------*/
3462 /*!
3463  * \brief Define a post-processing mesh for probes (set of probes should have
3464  *        been already defined)
3465  *
3466  * \param[in] mesh_id        id of mesh to define (<0 reserved, >0 for user)
3467  * \param[in] pset           pointer to a cs_probe_set_t structure
3468  * \param[in] time_varying   true if probe coords may change during computation
3469  * \param[in] is_profile     true if probe set is related to a profile
3470  * \param[in] on_boundary    true if probes are located on boundary
3471  * \param[in] auto_variable  true if the set of variables to post is predefined
3472  * \param[in] n_writers      number of associated writers
3473  * \param[in] writer_ids     ids of associated writers
3474  */
3475 /*----------------------------------------------------------------------------*/
3476 
3477 static void
_cs_post_define_probe_mesh(int mesh_id,cs_probe_set_t * pset,bool time_varying,bool is_profile,bool on_boundary,bool auto_variable,int n_writers,const int writer_ids[])3478 _cs_post_define_probe_mesh(int                    mesh_id,
3479                            cs_probe_set_t        *pset,
3480                            bool                   time_varying,
3481                            bool                   is_profile,
3482                            bool                   on_boundary,
3483                            bool                   auto_variable,
3484                            int                    n_writers,
3485                            const int              writer_ids[])
3486 {
3487   assert(pset != NULL); /* Sanity check */
3488 
3489   /* Common initializations */
3490 
3491   int  mode = (is_profile == true) ? 4 : 3;
3492   cs_post_mesh_t *post_mesh = _predefine_mesh(mesh_id, time_varying, mode,
3493                                               n_writers, writer_ids);
3494 
3495   /* Define mesh based on current arguments */
3496 
3497   const char  *mesh_name = cs_probe_set_get_name(pset);
3498   BFT_MALLOC(post_mesh->name, strlen(mesh_name) + 1, char);
3499   strcpy(post_mesh->name, mesh_name);
3500 
3501   post_mesh->sel_func[4] = NULL;
3502   post_mesh->sel_input[4] = pset;
3503 
3504   post_mesh->add_groups = false;
3505 
3506   if (auto_variable) {
3507     if (is_profile) {
3508       if (on_boundary)
3509         post_mesh->cat_id = CS_POST_MESH_BOUNDARY;
3510       else
3511         post_mesh->cat_id = CS_POST_MESH_VOLUME;
3512     }
3513     else
3514       post_mesh->cat_id = CS_POST_MESH_PROBES;
3515   }
3516 
3517   _attach_probe_set_fields(post_mesh);
3518 
3519   /* Try to assign probe location mesh */
3520 
3521   const char _select_all[] = "all[]";
3522   const char *sel_criteria = cs_probe_set_get_location_criteria(pset);
3523   if (sel_criteria == NULL)
3524     sel_criteria = _select_all;
3525 
3526   /* Check for existing meshes with the same selection criteria */
3527 
3528   int  ent_flag_id = (on_boundary) ? 2 : 0;
3529   int  match_partial[2] = {-1, -1};
3530 
3531   for (int i = 0; i < _cs_post_n_meshes; i++) {
3532 
3533     cs_post_mesh_t *post_mesh_cmp = _cs_post_meshes + i;
3534     if (post_mesh_cmp->criteria[ent_flag_id] != NULL) {
3535       if (strcmp(sel_criteria, post_mesh_cmp->criteria[ent_flag_id]) == 0) {
3536         if (   time_varying == false || post_mesh_cmp->time_varying == true)
3537           post_mesh->locate_ref = i;
3538         break;
3539       }
3540       else {
3541         if (post_mesh_cmp->n_writers == 0)
3542           match_partial[1] = i;
3543         else {
3544           for (int j = 0; j < n_writers && match_partial[0] == -1; j++) {
3545             for (int k = 0; k < post_mesh_cmp->n_writers; k++)
3546               if (writer_ids[j] == post_mesh_cmp->writer_id[k])
3547                 match_partial[0] = i;
3548           }
3549         }
3550       }
3551     }
3552   }
3553 
3554   if (post_mesh->locate_ref < 0) {
3555     if (match_partial[0] >= 0)
3556       post_mesh->locate_ref = match_partial[0];
3557     else if (match_partial[1] >= 0)
3558       post_mesh->locate_ref = match_partial[1];
3559   }
3560 
3561   /* Add (define) location mesh if none found */
3562 
3563   if (post_mesh->locate_ref == -1) {
3564     int new_id = cs_post_get_free_mesh_id();
3565     if (on_boundary)
3566       cs_post_define_surface_mesh(new_id,
3567                                   "probe_set_location_mesh",
3568                                   NULL,
3569                                   sel_criteria,
3570                                   false,
3571                                   false,
3572                                   0,
3573                                   NULL);
3574     else
3575       cs_post_define_volume_mesh(new_id,
3576                                  "probe_set_location_mesh",
3577                                  sel_criteria,
3578                                  false,
3579                                  false,
3580                                  0,
3581                                  NULL);
3582 
3583     /* In case the mesh array has been reallocated, reset pointer */
3584     int _mesh_id = _cs_post_mesh_id_try(mesh_id);
3585     post_mesh = _cs_post_meshes + _mesh_id;
3586 
3587     post_mesh->locate_ref = _cs_post_mesh_id(new_id);
3588 
3589     (_cs_post_meshes + post_mesh->locate_ref)->time_varying = true;
3590   }
3591 }
3592 
3593 /*============================================================================
3594  * Fortran wrapper function definitions
3595  *============================================================================*/
3596 
3597 /*----------------------------------------------------------------------------
3598  * Update "active" or "inactive" flag of writers based on the time step.
3599  *
3600  * Writers are activated if their output interval is a divisor of the
3601  * current time step, or if their optional time step and value output lists
3602  * contain matches for the current time step.
3603  *----------------------------------------------------------------------------*/
3604 
3605 void
cs_f_post_activate_by_time_step(void)3606 cs_f_post_activate_by_time_step(void)
3607 {
3608   cs_post_activate_by_time_step(cs_glob_time_step);
3609 }
3610 
3611 /*----------------------------------------------------------------------------
3612  * Output a floating point variable defined at cells or faces of a
3613  * post-processing mesh using associated writers.
3614  *
3615  * parameters:
3616  *   mesh_id     <-- id of associated mesh
3617  *   var_name    <-- name of variable to output
3618  *   var_dim     <-- 1 for scalar, 3 for vector
3619  *   interlace   <-- if a vector, true for interlaced values, false otherwise
3620  *   use_parent  <-- true if values are defined on "parent" mesh,
3621  *                   false if values are defined on post-processing mesh
3622  *   nt_cur_abs  <-- current time step number
3623  *   t_cur_abs   <-- current physical time
3624  *   cel_vals    <-- cell values
3625  *   i_face_vals <-- interior face values
3626  *   b_face_vals <-- boundary face values
3627  *----------------------------------------------------------------------------*/
3628 
3629 void
cs_f_post_write_var(int mesh_id,const char * var_name,int var_dim,bool interlace,bool use_parent,int nt_cur_abs,double t_cur_abs,const cs_real_t * cel_vals,const cs_real_t * i_face_vals,const cs_real_t * b_face_vals)3630 cs_f_post_write_var(int               mesh_id,
3631                     const char       *var_name,
3632                     int               var_dim,
3633                     bool              interlace,
3634                     bool              use_parent,
3635                     int               nt_cur_abs,
3636                     double            t_cur_abs,
3637                     const cs_real_t  *cel_vals,
3638                     const cs_real_t  *i_face_vals,
3639                     const cs_real_t  *b_face_vals)
3640 {
3641   CS_UNUSED(t_cur_abs);
3642 
3643   cs_post_type_t var_type
3644     = (sizeof(cs_real_t) == 8) ? CS_POST_TYPE_double : CS_POST_TYPE_float;
3645 
3646   const cs_time_step_t  *ts = cs_glob_time_step;
3647 
3648   if (nt_cur_abs < 0) /* Allow forcing of time-independent output */
3649     ts = NULL;
3650 
3651   cs_post_write_var(mesh_id,
3652                     CS_POST_WRITER_ALL_ASSOCIATED,
3653                     var_name,
3654                     var_dim,
3655                     interlace,
3656                     use_parent,
3657                     var_type,
3658                     cel_vals,
3659                     i_face_vals,
3660                     b_face_vals,
3661                     ts);
3662 }
3663 
3664 /*----------------------------------------------------------------------------*/
3665 /*!
3666  * \brief Transform time-independent values into time-dependent
3667  *        values for transient meshes.
3668  *
3669  * \param[in]       writer  pointer to associated writer
3670  * \param[in, out]  nt_cur  associated time step (-1 initially for
3671  *                          time-independent values)
3672  * \param[in, out]  t_cur   associated time value
3673  */
3674 /*----------------------------------------------------------------------------*/
3675 
3676 static inline void
_check_non_transient(const cs_post_writer_t * writer,int * nt_cur,double * t_cur)3677 _check_non_transient(const cs_post_writer_t  *writer,
3678                      int                     *nt_cur,
3679                      double                  *t_cur)
3680 {
3681   assert(writer->active > 0);
3682   assert(writer->writer != NULL);
3683 
3684   fvm_writer_time_dep_t time_dep = fvm_writer_get_time_dep(writer->writer);
3685 
3686   if (time_dep == FVM_WRITER_TRANSIENT_CONNECT) {
3687     *nt_cur = writer->tc.last_nt;
3688     *t_cur = writer->tc.last_t;
3689   }
3690 }
3691 
3692 /*! (DOXYGEN_SHOULD_SKIP_THIS) \endcond */
3693 
3694 /*============================================================================
3695  * Public function definitions
3696  *============================================================================*/
3697 
3698 /*----------------------------------------------------------------------------*/
3699 /*!
3700  * \brief Define a writer; this objects manages a case's name, directory,
3701  *        and format, as well as associated mesh's time dependency, and the
3702  *        default output interval for associated variables.
3703  *
3704  * This function must be called before the time loop. If a writer with a
3705  * given id is defined multiple times, the last definition supercedes the
3706  * previous ones.
3707  *
3708  * Current reserved ids are the following: CS_POST_WRITER_DEFAULT
3709  * for main/default output, CS_POST_WRITER_ERRORS for error visualization,
3710  * CS_POST_WRITER_PROBES for main probes, CS_POST_WRITER_PARTICLES for
3711  * particles, CS_POST_WRITER_TRAJECTORIES for trajectories. Other negative
3712  * ids may be dynamically reserved by the code depending on options.
3713  * Positive ids identify user-defined writers.
3714  *
3715  * \warning depending on the chosen format, the \em case_name may be
3716  * shortened (maximum number of characters: 32 for \em MED, 19 for \em EnSight,
3717  * or modified automatically (white-space or forbidden characters will be
3718  * replaced by "_").
3719  *
3720  * The \c \b format_name argument is used to choose the output format, and the
3721  * following values are allowed (assuming the matching
3722  * support was built):
3723  *
3724  * - \c \b EnSight \c \b Gold (\c \b EnSight also accepted)
3725  * - \c \b MED
3726  * - \c \b CGNS
3727  * - \c \b CCM (only for the full volume and boundary meshes)
3728  * - \c \b Catalyst (in-situ visualization)
3729  * - \c \b MEDCoupling (in-memory structure, to be used from other code)
3730  * - \c \b plot (comma or whitespace separated 2d plot files)
3731  * - \c \b time_plot (comma or whitespace separated time plot files)
3732  *
3733  * The format name is case-sensitive, so \c \b ensight or \c \b cgns are also valid.
3734  *
3735  * The optional \c \b fmt_opts character string contains a list of options related
3736  * to the format, separated by spaces or commas; these options include:
3737  *
3738  * - \c \b binary for a binary format version (default)
3739  * - \c \b big_endian to force outputs to be in \c \b big-endian mode
3740  *         (for \c \b EnSight).
3741  * - \c \b text for a text format version (for \c \b EnSight).
3742  * - \c \b adf for ADF file type (for \c \b CGNS).
3743  * - \c \b hdf5 for HDF5 file type (for \c \b CGNS, normally the default if
3744  *         HDF5 support is available).
3745  * - \c \b discard_polygons to prevent from exporting faces with more than
3746  *         four edges (which may not be recognized by some post-processing
3747  *         tools); such faces will therefore not appear in the post-processing
3748  *         mesh.
3749  * - \c \b discard_polyhedra to prevent from exporting elements which are
3750  *         neither tetrahedra, prisms, pyramids nor hexahedra (which may not
3751  *         be recognized by some post-processing tools); such elements will
3752  *         therefore not appear in the post-processing mesh.
3753  * - \c \b divide_polygons to divide faces with more than four edges into
3754  *         triangles, so that any post-processing tool can recognize them.
3755  * - \c \b divide_polyhedra} to divide elements which are neither tetrahedra,
3756  *         prisms, pyramids nor hexahedra into simpler elements (tetrahedra and
3757  *         pyramids), so that any post-processing tool can recognize them.
3758  * - \c \b separate_meshes to multiple meshes and associated fields to
3759  *         separate outputs.
3760  *
3761  * Note that the white-spaces in the beginning or in the end of the
3762  * character strings given as arguments here are suppressed automatically.
3763  *
3764  * \param[in]  writer_id        id of writer to create. (< 0 reserved,
3765  *                              > 0 for user); eveb for reserved ids,
3766  *                              the matching writer's options
3767  *                              may be redifined by calls to this function
3768  * \param[in]  case_name        associated case name
3769  * \param[in]  dir_name         associated directory name
3770  * \param[in]  fmt_name         associated format name
3771  * \param[in]  fmt_opts         associated format options string
3772  * \param[in]  time_dep         \ref FVM_WRITER_FIXED_MESH if mesh definitions
3773  *                              are fixed, \ref FVM_WRITER_TRANSIENT_COORDS if
3774  *                              coordinates change,
3775  *                              \ref FVM_WRITER_TRANSIENT_CONNECT if
3776  *                              connectivity changes
3777  * \param[in]  output_at_start  force output at calculation start if true
3778  * \param[in]  output_at_end    force output at calculation end if true
3779  * \param[in]  interval_n       default output interval in time-steps, or < 0
3780  * \param[in]  interval_t       default output interval in seconds, or < 0
3781  *                              (has priority over interval_n)
3782  */
3783 /*----------------------------------------------------------------------------*/
3784 
3785 void
cs_post_define_writer(int writer_id,const char * case_name,const char * dir_name,const char * fmt_name,const char * fmt_opts,fvm_writer_time_dep_t time_dep,bool output_at_start,bool output_at_end,int interval_n,double interval_t)3786 cs_post_define_writer(int                     writer_id,
3787                       const char             *case_name,
3788                       const char             *dir_name,
3789                       const char             *fmt_name,
3790                       const char             *fmt_opts,
3791                       fvm_writer_time_dep_t   time_dep,
3792                       bool                    output_at_start,
3793                       bool                    output_at_end,
3794                       int                     interval_n,
3795                       double                  interval_t)
3796 {
3797   /* local variables */
3798 
3799   int    i;
3800 
3801   cs_post_writer_t  *w = NULL;
3802   cs_post_writer_def_t  *wd = NULL;
3803 
3804   /* Initialize timer statistics if necessary */
3805 
3806   if (_post_out_stat_id < 0)
3807     _post_out_stat_id =  cs_timer_stats_id_by_name("postprocessing_output");
3808 
3809   /* Check if the required writer already exists */
3810 
3811   if (writer_id == 0)
3812     bft_error(__FILE__, __LINE__, 0,
3813               _("The requested post-processing writer number\n"
3814                 "must be < 0 (reserved) or > 0 (user).\n"));
3815 
3816   for (i = 0; i < _cs_post_n_writers; i++) {
3817     if ((_cs_post_writers + i)->id == writer_id) {
3818       w = _cs_post_writers + i;
3819       BFT_FREE(w->ot);
3820       wd = w->wd;
3821       assert(wd != NULL);
3822       BFT_FREE(wd->case_name);
3823       BFT_FREE(wd->dir_name);
3824       BFT_FREE(wd->fmt_opts);
3825       break;
3826     }
3827   }
3828 
3829   if (i == _cs_post_n_writers) { /* New definition */
3830 
3831     /* Resize global writers array */
3832 
3833     if (_cs_post_n_writers == _cs_post_n_writers_max) {
3834       if (_cs_post_n_writers_max == 0)
3835         _cs_post_n_writers_max = 4;
3836       else
3837         _cs_post_n_writers_max *= 2;
3838       BFT_REALLOC(_cs_post_writers,
3839                   _cs_post_n_writers_max,
3840                   cs_post_writer_t);
3841     }
3842 
3843     if (writer_id < _cs_post_min_writer_id)
3844       _cs_post_min_writer_id = writer_id;
3845     _cs_post_n_writers += 1;
3846 
3847     w = _cs_post_writers + i;
3848     BFT_MALLOC(w->wd, 1, cs_post_writer_def_t);
3849     wd = w->wd;
3850 
3851   }
3852 
3853   /* Assign writer definition to the structure */
3854 
3855   w->id = writer_id;
3856   w->active = 0;
3857 
3858   if (interval_t >= 0)
3859     cs_time_control_init_by_time(&(w->tc),
3860                                  -1,
3861                                  -1,
3862                                  interval_t,
3863                                  output_at_start,
3864                                  output_at_end);
3865   else
3866     cs_time_control_init_by_time_step(&(w->tc),
3867                                       -1,
3868                                       -1,
3869                                       interval_n,
3870                                       output_at_start,
3871                                       output_at_end);
3872 
3873   w->tc.last_nt = -2;
3874   w->tc.last_t = cs_glob_time_step->t_prev;
3875   if (w->tc.type == CS_TIME_CONTROL_TIME) {
3876     int n_steps = w->tc.last_t / interval_t;
3877     if (n_steps * interval_t > w->tc.last_t)
3878       n_steps -= 1;
3879     double t_prev = n_steps * interval_t;
3880     if (t_prev < cs_glob_time_step->t_prev)
3881       w->tc.last_t = t_prev;
3882   }
3883   w->ot = NULL;
3884 
3885   wd->time_dep = time_dep;
3886 
3887   BFT_MALLOC(wd->case_name, strlen(case_name) + 1, char);
3888   strcpy(wd->case_name, case_name);
3889 
3890   BFT_MALLOC(wd->dir_name, strlen(dir_name) + 1, char);
3891   strcpy(wd->dir_name, dir_name);
3892 
3893   wd->fmt_id = fvm_writer_get_format_id(fmt_name);
3894 
3895   if (fmt_opts != NULL) {
3896     BFT_MALLOC(wd->fmt_opts, strlen(fmt_opts) + 1, char);
3897     strcpy(wd->fmt_opts, fmt_opts);
3898   }
3899   else {
3900     BFT_MALLOC(wd->fmt_opts, 1, char);
3901     wd->fmt_opts[0] = '\0';
3902   }
3903 
3904   w->writer = NULL;
3905 
3906   /* If writer is the default writer (id -1), update defaults */
3907 
3908   if (writer_id == -1) {
3909     _cs_post_default_format_id = wd->fmt_id;
3910     if (wd->fmt_opts != NULL) {
3911       BFT_REALLOC(_cs_post_default_format_options,
3912                   strlen(wd->fmt_opts)+ 1,
3913                   char);
3914       strcpy(_cs_post_default_format_options, wd->fmt_opts);
3915     }
3916     else
3917       BFT_FREE(_cs_post_default_format_options);
3918     /* Remove possible "separate_writers" option from default format */
3919     fvm_writer_filter_option(_cs_post_default_format_options,
3920                              "separate_meshes");
3921   }
3922 
3923 }
3924 
3925 /*----------------------------------------------------------------------------*/
3926 /*!
3927  * \brief Define a volume post-processing mesh.
3928  *
3929  * \param[in]  mesh_id         id of mesh to define
3930  *                             (< 0 reserved, > 0 for user)
3931  * \param[in]  mesh_name       associated mesh name
3932  * \param[in]  cell_criteria   selection criteria for cells
3933  * \param[in]  add_groups      if true, add group information if present
3934  * \param[in]  auto_variables  if true, automatic output of main variables
3935  * \param[in]  n_writers       number of associated writers
3936  * \param[in]  writer_ids      ids of associated writers
3937  */
3938 /*----------------------------------------------------------------------------*/
3939 
3940 void
cs_post_define_volume_mesh(int mesh_id,const char * mesh_name,const char * cell_criteria,bool add_groups,bool auto_variables,int n_writers,const int writer_ids[])3941 cs_post_define_volume_mesh(int          mesh_id,
3942                            const char  *mesh_name,
3943                            const char  *cell_criteria,
3944                            bool         add_groups,
3945                            bool         auto_variables,
3946                            int          n_writers,
3947                            const int    writer_ids[])
3948 {
3949   /* Call common initialization */
3950 
3951   cs_post_mesh_t *post_mesh = NULL;
3952 
3953   post_mesh = _predefine_mesh(mesh_id, true, 0, n_writers, writer_ids);
3954 
3955   /* Define mesh based on current arguments */
3956 
3957   BFT_MALLOC(post_mesh->name, strlen(mesh_name) + 1, char);
3958   strcpy(post_mesh->name, mesh_name);
3959 
3960   if (cell_criteria != NULL) {
3961     BFT_MALLOC(post_mesh->criteria[0], strlen(cell_criteria) + 1, char);
3962     strcpy(post_mesh->criteria[0], cell_criteria);
3963   }
3964   post_mesh->ent_flag[0] = 1;
3965 
3966   post_mesh->add_groups = (add_groups) ? true : false;
3967   if (auto_variables)
3968     post_mesh->cat_id = CS_POST_MESH_VOLUME;
3969 
3970   if (post_mesh->cat_id == CS_POST_MESH_VOLUME)
3971     post_mesh->post_domain = true;
3972 }
3973 
3974 /*----------------------------------------------------------------------------*/
3975 /*!
3976  * \brief Define a volume post-processing mesh using a selection function.
3977  *
3978  * The selection may be updated over time steps if both the time_varying
3979  * flag is set to true and the mesh is only associated with writers defined
3980  * with the FVM_WRITER_TRANSIENT_CONNECT option.
3981  *
3982  * Note: if the cell_select_input pointer is non-NULL, it must point
3983  * to valid data when the selection function is called, so either:
3984  * - that value or structure should not be temporary (i.e. local);
3985  * - post-processing output must be ensured using cs_post_write_meshes()
3986  *   with a fixed-mesh writer before the data pointed to goes out of scope;
3987  *
3988  * \param[in]  mesh_id            id of mesh to define
3989  *                                (< 0 reserved, > 0 for user)
3990  * \param[in]  mesh_name          associated mesh name
3991  * \param[in]  cell_select_func   pointer to cells selection function
3992  * \param[in]  cell_select_input  pointer to optional input data for the cell
3993  *                                selection function, or NULL
3994  * \param[in]  time_varying       if true, try to redefine mesh at each
3995  *                                output time
3996  * \param[in]  add_groups         if true, add group information if present
3997  * \param[in]  auto_variables     if true, automatic output of main variables
3998  * \param[in]  n_writers          number of associated writers
3999  * \param[in]  writer_ids         ids of associated writers
4000  */
4001 /*----------------------------------------------------------------------------*/
4002 
4003 void
cs_post_define_volume_mesh_by_func(int mesh_id,const char * mesh_name,cs_post_elt_select_t * cell_select_func,void * cell_select_input,bool time_varying,bool add_groups,bool auto_variables,int n_writers,const int writer_ids[])4004 cs_post_define_volume_mesh_by_func(int                    mesh_id,
4005                                    const char            *mesh_name,
4006                                    cs_post_elt_select_t  *cell_select_func,
4007                                    void                  *cell_select_input,
4008                                    bool                   time_varying,
4009                                    bool                   add_groups,
4010                                    bool                   auto_variables,
4011                                    int                    n_writers,
4012                                    const int              writer_ids[])
4013 {
4014   /* Call common initialization */
4015 
4016   cs_post_mesh_t *post_mesh = NULL;
4017 
4018   post_mesh = _predefine_mesh(mesh_id, time_varying, 0, n_writers, writer_ids);
4019 
4020   /* Define mesh based on current arguments */
4021 
4022   BFT_MALLOC(post_mesh->name, strlen(mesh_name) + 1, char);
4023   strcpy(post_mesh->name, mesh_name);
4024 
4025   post_mesh->sel_func[0] = cell_select_func;
4026   post_mesh->sel_input[0] = cell_select_input;
4027   post_mesh->ent_flag[0] = 1;
4028 
4029   post_mesh->add_groups = (add_groups) ? true : false;
4030   if (auto_variables)
4031     post_mesh->cat_id = CS_POST_MESH_VOLUME;
4032 
4033   if (post_mesh->cat_id == CS_POST_MESH_VOLUME)
4034     post_mesh->post_domain = true;
4035 }
4036 
4037 /*----------------------------------------------------------------------------*/
4038 /*!
4039  * \brief Define a surface post-processing mesh.
4040  *
4041  * \param[in]  mesh_id          id of mesh to define
4042  *                              (< 0 reserved, > 0 for user)
4043  * \param[in]  mesh_name        associated mesh name
4044  * \param[in]  i_face_criteria  selection criteria for interior faces
4045  * \param[in]  b_face_criteria  selection criteria for boundary faces
4046  * \param[in]  add_groups       if true, add group information if present
4047  * \param[in]  auto_variables   if true, automatic output of main variables
4048  * \param[in]  n_writers        number of associated writers
4049  * \param[in]  writer_ids       ids of associated writers
4050  */
4051 /*----------------------------------------------------------------------------*/
4052 
4053 void
cs_post_define_surface_mesh(int mesh_id,const char * mesh_name,const char * i_face_criteria,const char * b_face_criteria,bool add_groups,bool auto_variables,int n_writers,const int writer_ids[])4054 cs_post_define_surface_mesh(int          mesh_id,
4055                             const char  *mesh_name,
4056                             const char  *i_face_criteria,
4057                             const char  *b_face_criteria,
4058                             bool         add_groups,
4059                             bool         auto_variables,
4060                             int          n_writers,
4061                             const int    writer_ids[])
4062 {
4063   /* Call common initialization */
4064 
4065   cs_post_mesh_t *post_mesh = NULL;
4066 
4067   post_mesh = _predefine_mesh(mesh_id, true, 0, n_writers, writer_ids);
4068 
4069   /* Define mesh based on current arguments */
4070 
4071   BFT_MALLOC(post_mesh->name, strlen(mesh_name) + 1, char);
4072   strcpy(post_mesh->name, mesh_name);
4073 
4074   if (i_face_criteria != NULL) {
4075     BFT_MALLOC(post_mesh->criteria[1], strlen(i_face_criteria) + 1, char);
4076     strcpy(post_mesh->criteria[1], i_face_criteria);
4077     post_mesh->ent_flag[1] = 1;
4078   }
4079 
4080   if (b_face_criteria != NULL) {
4081     BFT_MALLOC(post_mesh->criteria[2], strlen(b_face_criteria) + 1, char);
4082     strcpy(post_mesh->criteria[2], b_face_criteria);
4083     post_mesh->ent_flag[2] = 1;
4084   }
4085 
4086   post_mesh->add_groups = (add_groups != 0) ? true : false;
4087   if (auto_variables) {
4088     if (post_mesh->ent_flag[1] == 0)
4089       post_mesh->cat_id = CS_POST_MESH_BOUNDARY;
4090     else
4091       post_mesh->cat_id = CS_POST_MESH_SURFACE;
4092   }
4093 
4094   if (post_mesh->cat_id == CS_POST_MESH_BOUNDARY)
4095     post_mesh->post_domain = true;
4096 }
4097 
4098 /*----------------------------------------------------------------------------*/
4099 /*!
4100  * \brief Define a surface post-processing mesh using selection functions.
4101  *
4102  * The selection may be updated over time steps if both the time_varying
4103  * flag is set to true and the mesh is only associated with writers defined
4104  * with the FVM_WRITER_TRANSIENT_CONNECT option.
4105  *
4106  * Note: if i_face_select_input or b_face_select_input pointer is non-NULL,
4107  * it must point to valid data when the selection function is called,
4108  * so either:
4109  * - that value or structure should not be temporary (i.e. local);
4110  * - post-processing output must be ensured using cs_post_write_meshes()
4111  *   with a fixed-mesh writer before the data pointed to goes out of scope;
4112  *
4113  * \param[in]  mesh_id              id of mesh to define
4114  *                                  (< 0 reserved, > 0 for user)
4115  * \param[in]  mesh_name            associated mesh name
4116  * \param[in]  i_face_select_func   pointer to interior faces selection function
4117  * \param[in]  b_face_select_func   pointer to boundary faces selection function
4118  * \param[in]  i_face_select_input  pointer to optional input data for the
4119  *                                  interior faces selection function, or NULL
4120  * \param[in]  b_face_select_input  pointer to optional input data for the
4121  *                                  boundary faces selection function, or NULL
4122  * \param[in]  time_varying         if true, try to redefine mesh at each
4123  *                                  output time
4124  * \param[in]  add_groups           if true, add group information if present
4125  * \param[in]  auto_variables       if true, automatic output of main variables
4126  * \param[in]  n_writers            number of associated writers
4127  * \param[in]  writer_ids          ids of associated writers
4128  */
4129 /*----------------------------------------------------------------------------*/
4130 
4131 void
cs_post_define_surface_mesh_by_func(int mesh_id,const char * mesh_name,cs_post_elt_select_t * i_face_select_func,cs_post_elt_select_t * b_face_select_func,void * i_face_select_input,void * b_face_select_input,bool time_varying,bool add_groups,bool auto_variables,int n_writers,const int writer_ids[])4132 cs_post_define_surface_mesh_by_func(int                    mesh_id,
4133                                     const char            *mesh_name,
4134                                     cs_post_elt_select_t  *i_face_select_func,
4135                                     cs_post_elt_select_t  *b_face_select_func,
4136                                     void                  *i_face_select_input,
4137                                     void                  *b_face_select_input,
4138                                     bool                   time_varying,
4139                                     bool                   add_groups,
4140                                     bool                   auto_variables,
4141                                     int                    n_writers,
4142                                     const int              writer_ids[])
4143 {
4144   /* Call common initialization */
4145 
4146   cs_post_mesh_t *post_mesh = NULL;
4147 
4148   post_mesh = _predefine_mesh(mesh_id, time_varying, 0, n_writers, writer_ids);
4149 
4150   /* Define mesh based on current arguments */
4151 
4152   BFT_MALLOC(post_mesh->name, strlen(mesh_name) + 1, char);
4153   strcpy(post_mesh->name, mesh_name);
4154 
4155   post_mesh->sel_func[1] = i_face_select_func;
4156   post_mesh->sel_func[2] = b_face_select_func;
4157 
4158   post_mesh->sel_input[1] = i_face_select_input;
4159   post_mesh->sel_input[2] = b_face_select_input;
4160 
4161   post_mesh->add_groups = (add_groups != 0) ? true : false;
4162 
4163   if (post_mesh->sel_func[1] != NULL)
4164     post_mesh->ent_flag[1] = 1;
4165   if (post_mesh->sel_func[2] != NULL)
4166     post_mesh->ent_flag[2] = 1;
4167 
4168   if (auto_variables)
4169     post_mesh->cat_id = CS_POST_MESH_BOUNDARY;
4170 
4171   if (post_mesh->cat_id == CS_POST_MESH_BOUNDARY)
4172     post_mesh->post_domain = true;
4173 }
4174 
4175 /*----------------------------------------------------------------------------*/
4176 /*!
4177  * \brief Define a particles post-processing mesh.
4178  *
4179  * Such a mesh is always time-varying, and will only be output by writers
4180  * defined with the FVM_WRITER_TRANSIENT_CONNECT option.
4181  *
4182  * If the trajectory_mode argument is set to true, this logic is reversed,
4183  * and output will only occur for writers defined with the
4184  * FVM_WRITER_FIXED_MESH option. In this case, a submesh consisting of
4185  * trajectory segments for the current time step will be added to
4186  * the output at each output time step.
4187  *
4188  * \param[in]  mesh_id         id of mesh to define
4189  *                             (< 0 reserved, > 0 for user)
4190  * \param[in]  mesh_name       associated mesh name
4191  * \param[in]  cell_criteria   selection criteria for cells containing
4192  *                             particles, or NULL.
4193  * \param[in]  density         fraction of the particles in the selected area
4194  *                             which should be output (0 < density <= 1)
4195  * \param[in]  trajectory      if true, activate trajectory mode
4196  * \param[in]  auto_variables  if true, automatic output of main variables
4197  * \param[in]  n_writers       number of associated writers
4198  * \param[in]  writer_ids      ids of associated writers
4199  */
4200 /*----------------------------------------------------------------------------*/
4201 
4202 void
cs_post_define_particles_mesh(int mesh_id,const char * mesh_name,const char * cell_criteria,double density,bool trajectory,bool auto_variables,int n_writers,const int writer_ids[])4203 cs_post_define_particles_mesh(int          mesh_id,
4204                               const char  *mesh_name,
4205                               const char  *cell_criteria,
4206                               double       density,
4207                               bool         trajectory,
4208                               bool         auto_variables,
4209                               int          n_writers,
4210                               const int    writer_ids[])
4211 {
4212   /* Call common initialization */
4213 
4214   int flag = (trajectory) ? 2 : 1;
4215   cs_post_mesh_t *post_mesh = NULL;
4216 
4217   post_mesh = _predefine_mesh(mesh_id, true, flag, n_writers, writer_ids);
4218 
4219   /* Define mesh based on current arguments */
4220 
4221   BFT_MALLOC(post_mesh->name, strlen(mesh_name) + 1, char);
4222   strcpy(post_mesh->name, mesh_name);
4223 
4224   if (cell_criteria != NULL) {
4225     BFT_MALLOC(post_mesh->criteria[3], strlen(cell_criteria) + 1, char);
4226     strcpy(post_mesh->criteria[3], cell_criteria);
4227   }
4228 
4229   post_mesh->add_groups = false;
4230 
4231   post_mesh->density = CS_MIN(density, 1.);
4232   post_mesh->density = CS_MAX(post_mesh->density, 0.);
4233 
4234   if (auto_variables)
4235     post_mesh->cat_id = CS_POST_MESH_VOLUME;
4236 }
4237 
4238 /*----------------------------------------------------------------------------*/
4239 /*!
4240  * \brief Define a particles post-processing mesh using a selection function.
4241  *
4242  * The selection may be updated over time steps.
4243  *
4244  * Such a mesh is always time-varying, and will only be output by writers
4245  * defined with the FVM_WRITER_TRANSIENT_CONNECT option.
4246  *
4247  * If the trajectory_mode argument is set to true, this logic is reversed,
4248  * and output will only occur for writers defined with the
4249  * FVM_WRITER_FIXED_MESH option. In this case, a submesh consisting of
4250  * trajectory segments for the current time step will be added to
4251  * the output at each output time step.
4252  *
4253  * Note: if the p_select_input pointer is non-NULL, it must point
4254  * to valid data when the selection function is called, so
4255  * that value or structure should not be temporary (i.e. local);
4256  *
4257  * \param[in]  mesh_id         id of mesh to define
4258  *                             (< 0 reserved, > 0 for user)
4259  * \param[in]  mesh_name       associated mesh name
4260  * \param[in]  p_select_func   pointer to particles selection function
4261  * \param[in]  p_select_input  pointer to optional input data for the particles
4262  *                             selection function, or NULL
4263  * \param[in]  trajectory      if true, activate trajectory mode
4264  * \param[in]  auto_variables  if true, automatic output of main variables
4265  * \param[in]  n_writers       number of associated writers
4266  * \param[in]  writer_ids      ids of associated writers
4267  */
4268 /*----------------------------------------------------------------------------*/
4269 
4270 void
cs_post_define_particles_mesh_by_func(int mesh_id,const char * mesh_name,cs_post_elt_select_t * p_select_func,void * p_select_input,bool trajectory,bool auto_variables,int n_writers,const int writer_ids[])4271 cs_post_define_particles_mesh_by_func(int                    mesh_id,
4272                                       const char            *mesh_name,
4273                                       cs_post_elt_select_t  *p_select_func,
4274                                       void                  *p_select_input,
4275                                       bool                   trajectory,
4276                                       bool                   auto_variables,
4277                                       int                    n_writers,
4278                                       const int              writer_ids[])
4279 {
4280   /* Call common initialization */
4281 
4282   int flag = (trajectory) ? 2 : 1;
4283   cs_post_mesh_t *post_mesh = NULL;
4284 
4285   post_mesh = _predefine_mesh(mesh_id, true, flag, n_writers, writer_ids);
4286 
4287   /* Define mesh based on current arguments */
4288 
4289   BFT_MALLOC(post_mesh->name, strlen(mesh_name) + 1, char);
4290   strcpy(post_mesh->name, mesh_name);
4291 
4292   post_mesh->sel_func[3] = p_select_func;
4293   post_mesh->sel_input[3] = p_select_input;
4294   post_mesh->ent_flag[3] = 1;
4295 
4296   post_mesh->add_groups = false;
4297 
4298   post_mesh->density = 1.;
4299 
4300   if (auto_variables)
4301     post_mesh->cat_id = CS_POST_MESH_PARTICLES;
4302 }
4303 
4304 /*----------------------------------------------------------------------------*/
4305 /*!
4306  * \brief Create a post-processing mesh associated with an existing exportable
4307  * mesh representation.
4308  *
4309  * If the exportable mesh is not intended to be used elsewhere, one can choose
4310  * to transfer its property to the post-processing mesh, which will then
4311  * manage its lifecycle based on its own requirements.
4312  *
4313  * If the exportable mesh must still be shared, one must be careful to
4314  * maintain consistency between this mesh and the post-processing output.
4315  *
4316  * The mesh in exportable dimension may be of a lower dimension than
4317  * its parent mesh, if it has been projected. In this case, a
4318  * dim_shift value of 1 indicates that parent cells are mapped to
4319  * exportable faces, and faces to edges, while a dim_shift value of 2
4320  * would indicate that parent cells are mapped to edges.
4321  * This is important when variables values are exported.
4322  *
4323  * \param[in]  mesh_id         id of mesh to define
4324  *                             (< 0 reserved, > 0 for user)
4325  * \param[in]  exp_mesh        mesh in exportable representation
4326  *                             (i.e. fvm_nodal_t)
4327  * \param[in]  dim_shift       nonzero if exp_mesh has been projected
4328  * \param[in]  transfer        if true, ownership of exp_mesh is transferred
4329  *                             to the post-processing mesh
4330  * \param[in]  auto_variables  if true, automatic output of main variables
4331  * \param[in]  n_writers       number of associated writers
4332  * \param[in]  writer_ids      ids of associated writers
4333  */
4334 /*----------------------------------------------------------------------------*/
4335 
4336 void
cs_post_define_existing_mesh(int mesh_id,fvm_nodal_t * exp_mesh,int dim_shift,bool transfer,bool auto_variables,int n_writers,const int writer_ids[])4337 cs_post_define_existing_mesh(int           mesh_id,
4338                              fvm_nodal_t  *exp_mesh,
4339                              int           dim_shift,
4340                              bool          transfer,
4341                              bool          auto_variables,
4342                              int           n_writers,
4343                              const int     writer_ids[])
4344 {
4345   /* local variables */
4346 
4347   int        i;
4348   int        glob_flag[3];
4349   cs_lnum_t  b_f_num_shift, ind_fac;
4350 
4351   int    loc_flag[3] = {1, 1, 1};  /* Flags 0 to 2 "inverted" compared
4352                                       to others so as to use a single
4353                                       call to
4354                                       MPI_Allreduce(..., MPI_MIN, ...) */
4355 
4356   int         dim_ent = 0;
4357   int         dim_ext_ent = 0;
4358   bool        maj_ent_flag = false;
4359   cs_lnum_t   n_elts = 0;
4360 
4361   cs_lnum_t       *num_ent_parent = NULL;
4362   cs_post_mesh_t  *post_mesh = NULL;
4363 
4364   /* Initialization of base structure */
4365 
4366   post_mesh = _predefine_mesh(mesh_id, true, 0, n_writers, writer_ids);
4367 
4368   /* Assign mesh to structure */
4369 
4370   post_mesh->exp_mesh = exp_mesh;
4371 
4372   if (transfer == true)
4373     post_mesh->_exp_mesh = exp_mesh;
4374 
4375   /* Compute number of cells and/or faces */
4376 
4377   dim_ext_ent = fvm_nodal_get_max_entity_dim(exp_mesh);
4378   dim_ent = dim_ext_ent + dim_shift;
4379   n_elts = fvm_nodal_get_n_entities(exp_mesh, dim_ext_ent);
4380 
4381   if (dim_ent == 3 && n_elts > 0)
4382     loc_flag[0] = 0;
4383 
4384   else if (dim_ent == 2 && n_elts > 0) {
4385 
4386     BFT_MALLOC(num_ent_parent, n_elts, cs_lnum_t);
4387 
4388     fvm_nodal_get_parent_num(exp_mesh, dim_ext_ent, num_ent_parent);
4389 
4390     b_f_num_shift = cs_glob_mesh->n_b_faces;
4391     for (ind_fac = 0; ind_fac < n_elts; ind_fac++) {
4392       if (num_ent_parent[ind_fac] > b_f_num_shift)
4393         post_mesh->n_i_faces += 1;
4394       else
4395         post_mesh->n_b_faces += 1;
4396     }
4397 
4398     BFT_FREE(num_ent_parent);
4399 
4400     if (post_mesh->n_i_faces > 0)
4401       loc_flag[1] = 0;
4402     else if (post_mesh->n_b_faces > 0)
4403       loc_flag[2] = 0;
4404 
4405   }
4406 
4407   for (i = 0; i < 3; i++)
4408     glob_flag[i] = loc_flag[i];
4409 
4410 #if defined(HAVE_MPI)
4411   if (cs_glob_n_ranks > 1)
4412     MPI_Allreduce (loc_flag, glob_flag, 3, MPI_INT, MPI_MIN,
4413                    cs_glob_mpi_comm);
4414 #endif
4415 
4416   /* Global indicators of mesh entity type presence;
4417      updated only if the mesh is not totally empty (for time-depending
4418      meshes, empty at certain times, we want to know the last type
4419      of entity used) */
4420 
4421   for (i = 0; i < 3; i++) {
4422     if (glob_flag[i] == 0)
4423       maj_ent_flag = true;
4424   }
4425 
4426   if (maj_ent_flag == true) {
4427     for (i = 0; i < 3; i++) {
4428       if (glob_flag[i] == 0)         /* Inverted glob_flag 0 to 2 logic */
4429         post_mesh->ent_flag[i] = 1;  /* (c.f. remark above) */
4430       else
4431         post_mesh->ent_flag[i] = 0;
4432     }
4433   }
4434 
4435   if (auto_variables) {
4436     post_mesh->cat_id = CS_POST_MESH_VOLUME;
4437     _check_mesh_cat_id(post_mesh);
4438   }
4439 }
4440 
4441 /*----------------------------------------------------------------------------*/
4442 /*!
4443  * \brief Create a mesh based upon the extraction of edges from an existing mesh.
4444  *
4445  * The newly created edges have no link to their parent elements, so
4446  * no variable referencing parent elements may be output to this mesh,
4447  * whose main use is to visualize "true" face edges when polygonal faces
4448  * are subdivided by the writer. In this way, even highly non-convex
4449  * faces may be visualized correctly if their edges are overlaid on
4450  * the surface mesh with subdivided polygons.
4451  *
4452  * \param[in]  mesh_id       id of edges mesh to create
4453  *                           (< 0 reserved, > 0 for user)
4454  * \param[in]  base_mesh_id  id of existing mesh (< 0 reserved, > 0 for user)
4455  * \param[in]  n_writers     number of associated writers
4456  * \param[in]  writer_ids    ids of associated writers
4457  */
4458 /*----------------------------------------------------------------------------*/
4459 
4460 void
cs_post_define_edges_mesh(int mesh_id,int base_mesh_id,int n_writers,const int writer_ids[])4461 cs_post_define_edges_mesh(int        mesh_id,
4462                           int        base_mesh_id,
4463                           int        n_writers,
4464                           const int  writer_ids[])
4465 {
4466   /* local variables */
4467 
4468   cs_post_mesh_t *post_mesh = NULL;
4469 
4470   cs_post_mesh_t *post_base
4471     = _cs_post_meshes + _cs_post_mesh_id(base_mesh_id);
4472 
4473   /* Add and initialize base structure */
4474 
4475   post_mesh = _predefine_mesh(mesh_id, true, 0, n_writers, writer_ids);
4476 
4477   BFT_MALLOC(post_mesh->name,
4478              strlen(post_base->name) + strlen(_(" edges")) + 1,
4479              char);
4480   strcpy(post_mesh->name, post_base->name);
4481   strcat(post_mesh->name, _(" edges"));
4482 }
4483 
4484 /*----------------------------------------------------------------------------*/
4485 /*!
4486  * \brief Associate a writer to a postprocessing mesh.
4487  *
4488  * This function must be called during the postprocessing output definition
4489  * stage, before any output actually occurs.
4490  *
4491  * If called with a non-existing mesh or writer id, or if the writer is
4492  * already associated, no setting is changed, and this function
4493  * returns silently.
4494  *
4495  * \param[in]  mesh_id      id of mesh to define
4496  *                          (< 0 reserved, > 0 for user)
4497  * \param[in]  writer_id    id of writer to associate
4498  */
4499 /*----------------------------------------------------------------------------*/
4500 
4501 void
cs_post_mesh_attach_writer(int mesh_id,int writer_id)4502 cs_post_mesh_attach_writer(int  mesh_id,
4503                            int  writer_id)
4504 {
4505   int _mesh_id = _cs_post_mesh_id_try(mesh_id);
4506   int _writer_id = _cs_post_writer_id_try(writer_id);
4507 
4508   if (_mesh_id < 0 || _writer_id < 0)
4509     return;
4510 
4511   cs_post_mesh_t *post_mesh = _cs_post_meshes + _mesh_id;
4512 
4513   /* Check we have not output this mesh yet */
4514 
4515   if (post_mesh->nt_last > -2)
4516     bft_error(__FILE__, __LINE__, 0,
4517               _("Error associating writer %d with mesh %d:"
4518                 "output has already been done for this mesh, "
4519                 "so mesh-writer association is locked."),
4520               writer_id, mesh_id);
4521 
4522   /* Ignore if writer id already associated */
4523 
4524   for (int i = 0; i < post_mesh->n_writers; i++) {
4525     if (post_mesh->writer_id[i] == _writer_id)
4526       return;
4527   }
4528 
4529   BFT_REALLOC(post_mesh->writer_id, post_mesh->n_writers + 1, int);
4530   post_mesh->writer_id[post_mesh->n_writers] = _writer_id;
4531   post_mesh->n_writers += 1;
4532 
4533   _update_mesh_writer_associations(post_mesh);
4534 }
4535 
4536 /*----------------------------------------------------------------------------*/
4537 /*!
4538  * \brief De-associate a writer from a postprocessing mesh.
4539  *
4540  * This function must be called during the postprocessing output definition
4541  * stage, before any output actually occurs.
4542  *
4543  * If called with a non-existing mesh or writer id, or if the writer was not
4544  * previously associated, no setting is changed, and this function
4545  * returns silently.
4546  *
4547  * \param[in]  mesh_id      id of mesh to define
4548  *                          (< 0 reserved, > 0 for user)
4549  * \param[in]  writer_id    id of writer to associate
4550  */
4551 /*----------------------------------------------------------------------------*/
4552 
4553 void
cs_post_mesh_detach_writer(int mesh_id,int writer_id)4554 cs_post_mesh_detach_writer(int  mesh_id,
4555                            int  writer_id)
4556 {
4557   int _mesh_id = _cs_post_mesh_id_try(mesh_id);
4558   int _writer_id = _cs_post_writer_id_try(writer_id);
4559 
4560   if (_mesh_id < 0 || _writer_id < 0)
4561     return;
4562 
4563   cs_post_mesh_t *post_mesh = _cs_post_meshes + _mesh_id;
4564 
4565   /* Check we have not output this mesh yet */
4566 
4567   if (post_mesh->nt_last > -2)
4568     bft_error(__FILE__, __LINE__, 0,
4569               _("Error unassociating writer %d from mesh %d:"
4570                 "output has already been done for this mesh, "
4571                 "so mesh-writer association is locked."),
4572               writer_id, mesh_id);
4573 
4574   /* Ignore if writer id already associated */
4575 
4576   int i, j;
4577   for (i = 0, j = 0; i < post_mesh->n_writers; i++) {
4578     if (post_mesh->writer_id[i] != _writer_id) {
4579       post_mesh->writer_id[j] = post_mesh->writer_id[i];
4580       j++;
4581     }
4582   }
4583 
4584   if (j < post_mesh->n_writers) {
4585 
4586     post_mesh->n_writers = j;
4587     BFT_REALLOC(post_mesh->writer_id, post_mesh->n_writers, int);
4588 
4589     _update_mesh_writer_associations(post_mesh);
4590   }
4591 }
4592 
4593 /*----------------------------------------------------------------------------*/
4594 /*!
4595  * \brief Associate a field to a writer and postprocessing mesh combination.
4596  *
4597  * This function must be called during the postprocessing output definition
4598  * stage, before any output actually occurs.
4599  *
4600  * If the field should already be output automatically based on the mesh
4601  * category and field output keywords, it will not be added.
4602  *
4603  * \param[in]  mesh_id    id of associated mesh
4604  * \param[in]  writer_id  id of specified associated writer,
4605  *                        or \ref CS_POST_WRITER_ALL_ASSOCIATED for all
4606  * \param[in]  field_id   id of field to attach
4607  * \param[in]  comp_id    id of field component (-1 for all)
4608  */
4609 /*----------------------------------------------------------------------------*/
4610 
4611 void
cs_post_mesh_attach_field(int mesh_id,int writer_id,int field_id,int comp_id)4612 cs_post_mesh_attach_field(int  mesh_id,
4613                           int  writer_id,
4614                           int  field_id,
4615                           int  comp_id)
4616 {
4617   const int _mesh_id = _cs_post_mesh_id_try(mesh_id);
4618   const cs_field_t  *f = cs_field_by_id(field_id);
4619 
4620   if (f == NULL || _mesh_id < 0)
4621     return;
4622 
4623   cs_post_mesh_t *post_mesh = _cs_post_meshes + _mesh_id;
4624 
4625   /* Check that the field is not already output automatically */
4626 
4627   bool redundant = false;
4628 
4629   if (   post_mesh->cat_id == CS_POST_MESH_VOLUME
4630       || post_mesh->cat_id == CS_POST_MESH_BOUNDARY
4631       || post_mesh->cat_id == CS_POST_MESH_SURFACE) {
4632     const int vis_key_id = cs_field_key_id("post_vis");
4633     if (cs_field_get_key_int(f, vis_key_id) & CS_POST_ON_LOCATION)
4634       redundant = true;
4635   }
4636 
4637   if (! redundant) {
4638     int *afi = post_mesh->a_field_info;
4639     for (int i = 0; i < post_mesh->n_a_fields; i++) {
4640       if (   afi[0] == writer_id && afi[1] == field_id
4641           && (afi[2] == comp_id || f->dim == 1)) {
4642         redundant = true;
4643         break;
4644       }
4645       afi += 3;
4646     }
4647   }
4648 
4649   if (! redundant) {
4650     BFT_REALLOC(post_mesh->a_field_info, 3*(post_mesh->n_a_fields+1), int);
4651     int *afi = post_mesh->a_field_info + 3*post_mesh->n_a_fields;
4652     afi[0] = writer_id;
4653     afi[1] = field_id;
4654     afi[2] = comp_id;
4655     post_mesh->n_a_fields += 1;
4656   }
4657 }
4658 
4659 /*----------------------------------------------------------------------------*/
4660 /*!
4661  * \brief Get a postprocessing meshes entity presence flag.
4662  *
4663  * This flag is an array of 3 integers, indicating the presence of elements
4664  * of given types on at least one subdomain (i.e. rank):
4665  *   0: presence of cells
4666  *   1: presence of interior faces
4667  *   2: presence of boundary faces
4668  *
4669  * \param[in]  mesh_id  postprocessing mesh id
4670  *
4671  * \return  pointer to entity presence flag
4672  */
4673 /*----------------------------------------------------------------------------*/
4674 
4675 const int *
cs_post_mesh_get_ent_flag(int mesh_id)4676 cs_post_mesh_get_ent_flag(int  mesh_id)
4677 {
4678   const cs_post_mesh_t  *mesh = _cs_post_meshes + _cs_post_mesh_id(mesh_id);
4679 
4680   return mesh->ent_flag;
4681 }
4682 
4683 /*----------------------------------------------------------------------------*/
4684 /*!
4685  * \brief Get a postprocessing mesh's number of cells
4686  *
4687  * \param[in]  mesh_id  postprocessing mesh id
4688  *
4689  * \return  number of cells of postprocessing mesh.
4690  */
4691 /*----------------------------------------------------------------------------*/
4692 
4693 cs_lnum_t
cs_post_mesh_get_n_cells(int mesh_id)4694 cs_post_mesh_get_n_cells(int  mesh_id)
4695 {
4696   cs_lnum_t retval = 0;
4697 
4698   const cs_post_mesh_t  *mesh = _cs_post_meshes + _cs_post_mesh_id(mesh_id);
4699 
4700   if (mesh->exp_mesh != NULL)
4701     retval = fvm_nodal_get_n_entities(mesh->exp_mesh, 3);
4702   else
4703     bft_error(__FILE__, __LINE__, 0,
4704               _("%s called before post-processing meshes are built."),
4705               __func__);
4706 
4707   return retval;
4708 }
4709 
4710 /*----------------------------------------------------------------------------*/
4711 /*!
4712  * \brief Get a postprocessing mesh's list of cells
4713  *
4714  * The array of cell ids must be of at least size
4715  * cs_post_mesh_get_n_cells(mesh_id).
4716  *
4717  * \param[in]   mesh_id   postprocessing mesh id
4718  * \param[out]  cell_ids  array of associated cell ids (0 to n-1 numbering,
4719  *                        relative to main mesh)
4720  */
4721 /*----------------------------------------------------------------------------*/
4722 
4723 void
cs_post_mesh_get_cell_ids(int mesh_id,cs_lnum_t * cell_ids)4724 cs_post_mesh_get_cell_ids(int         mesh_id,
4725                           cs_lnum_t  *cell_ids)
4726 {
4727   const cs_post_mesh_t  *mesh = _cs_post_meshes + _cs_post_mesh_id(mesh_id);
4728 
4729   if (mesh->exp_mesh != NULL) {
4730     cs_lnum_t i;
4731     cs_lnum_t n_cells = fvm_nodal_get_n_entities(mesh->exp_mesh, 3);
4732     fvm_nodal_get_parent_num(mesh->exp_mesh, 3, cell_ids);
4733     for (i = 0; i < n_cells; i++)
4734       cell_ids[i] -= 1;
4735   }
4736   else
4737     bft_error(__FILE__, __LINE__, 0,
4738               _("%s called before post-processing meshes are built."),
4739               __func__);
4740 }
4741 
4742 /*----------------------------------------------------------------------------*/
4743 /*!
4744  * \brief Get a postprocessing mesh's number of interior faces
4745  *
4746  * \param[in]  mesh_id  postprocessing mesh id
4747  *
4748  * \return  number of cells of postprocessing mesh.
4749  */
4750 /*----------------------------------------------------------------------------*/
4751 
4752 cs_lnum_t
cs_post_mesh_get_n_i_faces(int mesh_id)4753 cs_post_mesh_get_n_i_faces(int  mesh_id)
4754 {
4755   cs_lnum_t retval = 0;
4756 
4757   const cs_post_mesh_t  *mesh = _cs_post_meshes + _cs_post_mesh_id(mesh_id);
4758 
4759   if (mesh->exp_mesh != NULL)
4760     retval = mesh->n_i_faces;
4761   else
4762     bft_error(__FILE__, __LINE__, 0,
4763               _("%s called before post-processing meshes are built."),
4764               __func__);
4765 
4766   return retval;
4767 }
4768 
4769 /*----------------------------------------------------------------------------*/
4770 /*!
4771  * \brief Get a postprocessing mesh's list of boundary faces.
4772  *
4773  * The array of boundary face ids must be of at least size
4774  * cs_post_mesh_get_n_b_faces(mesh_id).
4775  *
4776  * \param[in]   mesh_id     postprocessing mesh id
4777  * \param[out]  i_face_ids  array of associated interior faces ids
4778  *                          (0 to n-1 numbering, relative to main mesh)
4779  */
4780 /*----------------------------------------------------------------------------*/
4781 
4782 void
cs_post_mesh_get_i_face_ids(int mesh_id,cs_lnum_t i_face_ids[])4783 cs_post_mesh_get_i_face_ids(int        mesh_id,
4784                             cs_lnum_t  i_face_ids[])
4785 {
4786   const cs_post_mesh_t  *mesh = _cs_post_meshes + _cs_post_mesh_id(mesh_id);
4787 
4788   if (mesh->exp_mesh != NULL) {
4789     cs_lnum_t i;
4790     cs_lnum_t n_faces = fvm_nodal_get_n_entities(mesh->exp_mesh, 2);
4791     const cs_lnum_t num_shift = cs_glob_mesh->n_b_faces + 1;
4792     if (mesh->n_b_faces == 0) {
4793       fvm_nodal_get_parent_num(mesh->exp_mesh, 3, i_face_ids);
4794       for (i = 0; i < n_faces; i++)
4795         i_face_ids[i] -= num_shift;
4796     }
4797     else {
4798       cs_lnum_t n_i_faces = 0;
4799       cs_lnum_t *tmp_ids = NULL;
4800       BFT_MALLOC(tmp_ids, n_faces, cs_lnum_t);
4801       fvm_nodal_get_parent_num(mesh->exp_mesh, 3, tmp_ids);
4802       for (i = 0; i < n_faces; i++) {
4803         if (tmp_ids[i] > cs_glob_mesh->n_b_faces)
4804           i_face_ids[n_i_faces++] = tmp_ids[i] - num_shift;
4805       }
4806       BFT_FREE(tmp_ids);
4807     }
4808   }
4809   else
4810     bft_error(__FILE__, __LINE__, 0,
4811               _("%s called before post-processing meshes are built."),
4812               __func__);
4813 }
4814 
4815 /*----------------------------------------------------------------------------*/
4816 /*!
4817  * \brief Get a postprocessing mesh's number of boundary faces
4818  *
4819  * \param[in]  mesh_id  postprocessing mesh id
4820  *
4821  * \return  number of cells of postprocessing mesh.
4822  */
4823 /*----------------------------------------------------------------------------*/
4824 
4825 cs_lnum_t
cs_post_mesh_get_n_b_faces(int mesh_id)4826 cs_post_mesh_get_n_b_faces(int  mesh_id)
4827 {
4828   cs_lnum_t retval = 0;
4829 
4830   const cs_post_mesh_t  *mesh = _cs_post_meshes + _cs_post_mesh_id(mesh_id);
4831 
4832   if (mesh->exp_mesh != NULL)
4833     retval = mesh->n_b_faces;
4834   else
4835     bft_error(__FILE__, __LINE__, 0,
4836               _("%s called before post-processing meshes are built."),
4837               __func__);
4838 
4839   return retval;
4840 }
4841 
4842 /*----------------------------------------------------------------------------*/
4843 /*!
4844  * \brief Get a postprocessing mesh's list of boundary faces.
4845  *
4846  * The array of boundary face ids must be of at least size
4847  * cs_post_mesh_get_n_b_faces(mesh_id).
4848  *
4849  * \param[in]   mesh_id     postprocessing mesh id
4850  * \param[out]  b_face_ids  array of associated boundary faces ids
4851  *                          (0 to n-1 numbering, relative to main mesh)
4852  */
4853 /*----------------------------------------------------------------------------*/
4854 
4855 void
cs_post_mesh_get_b_face_ids(int mesh_id,cs_lnum_t b_face_ids[])4856 cs_post_mesh_get_b_face_ids(int        mesh_id,
4857                             cs_lnum_t  b_face_ids[])
4858 {
4859   const cs_post_mesh_t  *mesh = _cs_post_meshes + _cs_post_mesh_id(mesh_id);
4860 
4861   if (mesh->exp_mesh != NULL) {
4862     cs_lnum_t i;
4863     cs_lnum_t n_faces = fvm_nodal_get_n_entities(mesh->exp_mesh, 2);
4864     if (mesh->n_i_faces == 0) {
4865       fvm_nodal_get_parent_num(mesh->exp_mesh, 3, b_face_ids);
4866       for (i = 0; i < n_faces; i++)
4867         b_face_ids[i] -= 1;
4868     }
4869     else {
4870       cs_lnum_t n_b_faces = 0;
4871       cs_lnum_t *tmp_ids = NULL;
4872       BFT_MALLOC(tmp_ids, n_faces, cs_lnum_t);
4873       fvm_nodal_get_parent_num(mesh->exp_mesh, 3, tmp_ids);
4874       for (i = 0; i < n_faces; i++) {
4875         if (tmp_ids[i] > cs_glob_mesh->n_b_faces)
4876           b_face_ids[n_b_faces++] = tmp_ids[i] - 1;
4877       }
4878       BFT_FREE(tmp_ids);
4879     }
4880   }
4881   else
4882     bft_error(__FILE__, __LINE__, 0,
4883               _("%s called before post-processing meshes are built."),
4884               __func__);
4885 }
4886 
4887 /*----------------------------------------------------------------------------*/
4888 /*!
4889  * \brief Get a postprocessing mesh's number of vertices
4890  *
4891  * \param[in]  mesh_id  postprocessing mesh id
4892  *
4893  * \return  number of vertices of postprocessing mesh.
4894  */
4895 /*----------------------------------------------------------------------------*/
4896 
4897 cs_lnum_t
cs_post_mesh_get_n_vertices(int mesh_id)4898 cs_post_mesh_get_n_vertices(int  mesh_id)
4899 {
4900   cs_lnum_t retval = 0;
4901 
4902   const cs_post_mesh_t  *mesh = _cs_post_meshes + _cs_post_mesh_id(mesh_id);
4903 
4904   if (mesh->exp_mesh != NULL)
4905     retval = fvm_nodal_get_n_entities(mesh->exp_mesh, 0);
4906   else
4907     bft_error(__FILE__, __LINE__, 0,
4908               _("%s called before post-processing meshes are built."),
4909               __func__);
4910 
4911   return retval;
4912 }
4913 
4914 /*----------------------------------------------------------------------------*/
4915 /*!
4916  * \brief Get a postprocessing mesh's list of vertices
4917  *
4918  * The array of vertex ids must be of at least size
4919  * cs_post_mesh_get_n_vertices(mesh_id).
4920  *
4921  * \param[in]   mesh_id     postprocessing mesh id
4922  * \param[out]  vertex_ids  array of associated vertex ids (0 to n-1 numbering,
4923  *                          relative to main mesh)
4924  */
4925 /*----------------------------------------------------------------------------*/
4926 
4927 void
cs_post_mesh_get_vertex_ids(int mesh_id,cs_lnum_t * vertex_ids)4928 cs_post_mesh_get_vertex_ids(int         mesh_id,
4929                             cs_lnum_t  *vertex_ids)
4930 {
4931   const cs_post_mesh_t  *mesh = _cs_post_meshes + _cs_post_mesh_id(mesh_id);
4932 
4933   if (mesh->exp_mesh != NULL) {
4934     cs_lnum_t i;
4935     cs_lnum_t n_vertices = fvm_nodal_get_n_entities(mesh->exp_mesh, 0);
4936     fvm_nodal_get_parent_num(mesh->exp_mesh, 0, vertex_ids);
4937     for (i = 0; i < n_vertices; i++)
4938       vertex_ids[i] -= 1;
4939   }
4940   else
4941     bft_error(__FILE__, __LINE__, 0,
4942               _("%s called before post-processing meshes are built."),
4943               __func__);
4944 }
4945 
4946 /*----------------------------------------------------------------------------*/
4947 /*!
4948  * \brief Set whether postprocessing mesh's parallel domain should be output.
4949  *
4950  * \param[in]  mesh_id      postprocessing mesh id
4951  * \param[in]  post_domain  true if parallel domain should be output,
4952  *                          false otherwise.
4953  */
4954 /*----------------------------------------------------------------------------*/
4955 
4956 void
cs_post_mesh_set_post_domain(int mesh_id,bool post_domain)4957 cs_post_mesh_set_post_domain(int   mesh_id,
4958                              bool  post_domain)
4959 {
4960   cs_post_mesh_t  *mesh = _cs_post_meshes + _cs_post_mesh_id(mesh_id);
4961 
4962   mesh->post_domain = post_domain;
4963 }
4964 
4965 /*----------------------------------------------------------------------------*/
4966 /*!
4967  * \brief Remove a post-processing mesh.
4968  *
4969  * No further post-processing output will be allowed on this mesh,
4970  * so the associated structures may be freed.
4971  *
4972  * A post-processing mesh that has been associated with a time-varying
4973  * writer may not be removed.
4974  *
4975  * \param[in]  mesh_id  postprocessing mesh id
4976  */
4977 /*----------------------------------------------------------------------------*/
4978 
4979 void
cs_post_free_mesh(int mesh_id)4980 cs_post_free_mesh(int  mesh_id)
4981 {
4982   int i;
4983   cs_post_mesh_t  *post_mesh = NULL;
4984 
4985   /* Search for requested mesh */
4986 
4987   int _mesh_id = _cs_post_mesh_id(mesh_id);
4988 
4989   /* Check if mesh was referenced for probe location */
4990 
4991   for (i = 0; i < _cs_post_n_meshes; i++) {
4992 
4993     post_mesh = _cs_post_meshes + i;
4994     if (post_mesh->locate_ref == _mesh_id)
4995       bft_error(__FILE__, __LINE__, 0,
4996                 _("Post-processing mesh number %d has been referenced\n"
4997                   "by probe set mesh %d, so it may not be freed.\n"),
4998                 mesh_id, post_mesh->id);
4999   }
5000 
5001   /* Now set pointer to mesh and check for time dependency */
5002 
5003   post_mesh = _cs_post_meshes + _mesh_id;
5004 
5005   for (i = 0; i < post_mesh->n_writers; i++) {
5006 
5007     cs_post_writer_t *writer = _cs_post_writers + post_mesh->writer_id[i];
5008 
5009     fvm_writer_time_dep_t time_dep = fvm_writer_get_time_dep(writer->writer);
5010 
5011     if (post_mesh->nt_last > -2 && time_dep != FVM_WRITER_FIXED_MESH)
5012       bft_error(__FILE__, __LINE__, 0,
5013                 _("Post-processing mesh number %d has been associated\n"
5014                   "to writer %d which allows time-varying meshes, so\n"
5015                   "it may not be freed.\n"),
5016                 mesh_id, writer->id);
5017   }
5018 
5019   /* Remove mesh if allowed */
5020 
5021   _free_mesh(_mesh_id);
5022 
5023   /* Finally, update free mesh ids */
5024 
5025   int min_id = _MIN_RESERVED_MESH_ID;
5026   for (i = 0; i < _cs_post_n_meshes; i++) {
5027     post_mesh = _cs_post_meshes + i;
5028     if (post_mesh->id < min_id)
5029       min_id = post_mesh->id;
5030   }
5031   _cs_post_min_mesh_id = min_id;
5032 }
5033 
5034 /*----------------------------------------------------------------------------*/
5035 /*!
5036  * \brief Check for the existence of a writer of the given id.
5037  *
5038  * \param[in]  writer_id  writer id to check
5039  *
5040  * \return  true if writer with this id exists, false otherwise
5041  */
5042 /*----------------------------------------------------------------------------*/
5043 
5044 bool
cs_post_writer_exists(int writer_id)5045 cs_post_writer_exists(int  writer_id)
5046 {
5047   /* local variables */
5048 
5049   int id;
5050   cs_post_writer_t  *writer = NULL;
5051 
5052   /* Search for requested mesh */
5053 
5054   for (id = 0; id < _cs_post_n_writers; id++) {
5055     writer = _cs_post_writers + id;
5056     if (writer->id == writer_id)
5057       return true;
5058   }
5059 
5060   return false;
5061 }
5062 
5063 /*----------------------------------------------------------------------------*/
5064 /*!
5065  * \brief Check for the existence of a post-processing mesh of the given id.
5066  *
5067  * \param[in]  mesh_id  mesh id to check
5068  *
5069  * \return  true if mesh with this id exists, false otherwise
5070  */
5071 /*----------------------------------------------------------------------------*/
5072 
5073 bool
cs_post_mesh_exists(int mesh_id)5074 cs_post_mesh_exists(int  mesh_id)
5075 {
5076   int id;
5077   cs_post_mesh_t  *post_mesh = NULL;
5078 
5079   /* Search for requested mesh */
5080 
5081   for (id = 0; id < _cs_post_n_meshes; id++) {
5082     post_mesh = _cs_post_meshes + id;
5083     if (post_mesh->id == mesh_id)
5084       return true;
5085   }
5086 
5087   return false;
5088 }
5089 
5090 /*----------------------------------------------------------------------------*/
5091 /*!
5092  * \brief Return the default writer format name
5093  *
5094  * \return  name of the default writer format
5095  */
5096 /*----------------------------------------------------------------------------*/
5097 
5098 const char *
cs_post_get_default_format(void)5099 cs_post_get_default_format(void)
5100 {
5101   return (fvm_writer_format_name(_cs_post_default_format_id));
5102 }
5103 
5104 /*----------------------------------------------------------------------------*/
5105 /*!
5106  * \brief Return the default writer format options
5107  *
5108  * \return  default writer format options string
5109  */
5110 /*----------------------------------------------------------------------------*/
5111 
5112 const char *
cs_post_get_default_format_options(void)5113 cs_post_get_default_format_options(void)
5114 {
5115   return (_cs_post_default_format_options);
5116 }
5117 
5118 /*----------------------------------------------------------------------------*/
5119 /*!
5120  * \brief Return the next "reservable" (i.e. non-user) writer id available.
5121  *
5122  * \return  the smallest negative integer present, -1
5123  */
5124 /*----------------------------------------------------------------------------*/
5125 
5126 int
cs_post_get_free_writer_id(void)5127 cs_post_get_free_writer_id(void)
5128 {
5129   return (_cs_post_min_writer_id - 1);
5130 }
5131 
5132 /*----------------------------------------------------------------------------*/
5133 /*!
5134  * \brief Return the next "reservable" (i.e. non-user) mesh id available.
5135  *
5136  * \return  the smallest negative integer present, -1
5137  */
5138 /*----------------------------------------------------------------------------*/
5139 
5140 int
cs_post_get_free_mesh_id(void)5141 cs_post_get_free_mesh_id(void)
5142 {
5143   return (_cs_post_min_mesh_id - 1);
5144 }
5145 
5146 /*----------------------------------------------------------------------------*/
5147 /*!
5148  * \brief Update "active" or "inactive" flag of writers based on the time step.
5149  *
5150  * Writers are activated if their output interval is a divisor of the
5151  * current time step, or if their optional time step and value output lists
5152  * contain matches for the current time step.
5153  *
5154  * \param[in]  ts  time step status structure
5155  */
5156 /*----------------------------------------------------------------------------*/
5157 
5158 void
cs_post_activate_by_time_step(const cs_time_step_t * ts)5159 cs_post_activate_by_time_step(const cs_time_step_t  *ts)
5160 {
5161   int  i;
5162   cs_post_writer_t  *writer;
5163 
5164   for (i = 0; i < _cs_post_n_writers; i++) {
5165 
5166     writer = _cs_post_writers + i;
5167 
5168     if (writer->active < 0)
5169       continue;
5170 
5171     /* In case of previous calls for a given time step,
5172        a writer's status may not be changed */
5173 
5174     if (writer->tc.last_nt == ts->nt_cur) {
5175       writer->active = 1;
5176       continue;
5177     }
5178 
5179     /* Activation based on interval */
5180 
5181     writer->active = cs_time_control_is_active(&(writer->tc), ts);
5182 
5183     /* Activation based on time step lists */
5184 
5185     _activate_if_listed(writer, ts);
5186 
5187     /* Do not activate transient writers for time-independent stages */
5188 
5189     if (ts->nt_cur < 0) {
5190       fvm_writer_time_dep_t  time_dep;
5191       if (writer->writer)
5192         time_dep = fvm_writer_get_time_dep(writer->writer);
5193       else
5194         time_dep = writer->wd->time_dep;
5195       if (time_dep != FVM_WRITER_FIXED_MESH)
5196         writer->active = 0;
5197     }
5198 
5199   }
5200 }
5201 
5202 /*----------------------------------------------------------------------------*/
5203 /*!
5204  * \brief Force the "active" or "inactive" flag for a specific writer or for all
5205  * writers for the current time step.
5206  *
5207  * \param[in]  writer_id  writer id, or 0 for all writers
5208  * \param[in]  activate   false to deactivate, true to activate
5209  */
5210 /*----------------------------------------------------------------------------*/
5211 
5212 void
cs_post_activate_writer(int writer_id,bool activate)5213 cs_post_activate_writer(int   writer_id,
5214                         bool  activate)
5215 {
5216   int i;
5217   cs_post_writer_t  *writer;
5218 
5219   if (writer_id != 0) {
5220     i = _cs_post_writer_id(writer_id);
5221     writer = _cs_post_writers + i;
5222     writer->active = (activate) ? 1 : 0;
5223   }
5224   else {
5225     for (i = 0; i < _cs_post_n_writers; i++) {
5226       writer = _cs_post_writers + i;
5227       writer->active = (activate) ? 1 : 0;
5228     }
5229   }
5230 }
5231 
5232 /*----------------------------------------------------------------------------*/
5233 /*!
5234  * \brief Disable specific writer or all writers not currently active until
5235  *        \ref cs_post_enable_writer or \ref cs_post_activate_writer
5236  *        is called for those writers.
5237  *
5238  * For each call to this function for a given writer, the same number
5239  * of calls to \ref cs_post_enable_writer or a single call to
5240  * \ref cs_post_activate_writer is required to re-enable the writer.
5241  *
5242  * This is useful to disable output even of fixed meshes in preprocessing
5243  * stages.
5244  *
5245  * \param[in]  writer_id  writer id, or 0 for all writers
5246  */
5247 /*----------------------------------------------------------------------------*/
5248 
5249 void
cs_post_disable_writer(int writer_id)5250 cs_post_disable_writer(int   writer_id)
5251 {
5252   int i;
5253   cs_post_writer_t  *writer;
5254 
5255   if (writer_id != 0) {
5256     i = _cs_post_writer_id(writer_id);
5257     writer = _cs_post_writers + i;
5258     if (writer->active < 1)
5259       writer->active -= 1;
5260   }
5261   else {
5262     for (i = 0; i < _cs_post_n_writers; i++) {
5263       writer = _cs_post_writers + i;
5264       if (writer->active < 1)
5265         writer->active -= 1;
5266     }
5267   }
5268 }
5269 
5270 /*----------------------------------------------------------------------------*/
5271 /*!
5272  * \brief Enable a specific writer or all writers currently disabled by
5273  *        previous calls to \ref cs_post_disable_writer.
5274  *
5275  * For each previous call to \ref cs_post_disable_writer for a given writer,
5276  * a call to this function (or a single call to \ref cs_post_activate_writer)
5277  * is required to re-enable the writer.
5278  *
5279  * This is useful to disable output even of fixed meshes in preprocessing
5280  * stages.
5281  *
5282  * \param[in]  writer_id  writer id, or 0 for all writers
5283  */
5284 /*----------------------------------------------------------------------------*/
5285 
5286 void
cs_post_enable_writer(int writer_id)5287 cs_post_enable_writer(int   writer_id)
5288 {
5289   int i;
5290   cs_post_writer_t  *writer;
5291 
5292   if (writer_id != 0) {
5293     i = _cs_post_writer_id(writer_id);
5294     writer = _cs_post_writers + i;
5295     if (writer->active < 0)
5296       writer->active += 1;
5297   }
5298   else {
5299     for (i = 0; i < _cs_post_n_writers; i++) {
5300       writer = _cs_post_writers + i;
5301       if (writer->active < 0)
5302         writer->active += 1;
5303     }
5304   }
5305 }
5306 
5307 /*----------------------------------------------------------------------------*/
5308 /*!
5309  * \brief Return a pointer to the FVM writer associated to a writer_id.
5310  *
5311  * \param[in]  writer_id  associated writer id
5312  *
5313  * \return  a pointer to a fvm_writer_t structure
5314  */
5315 /*----------------------------------------------------------------------------*/
5316 
5317 fvm_writer_t *
cs_post_get_writer(int writer_id)5318 cs_post_get_writer(int  writer_id)
5319 {
5320   int  id;
5321   cs_post_writer_t  *writer = NULL;
5322 
5323   id = _cs_post_writer_id(writer_id);
5324   writer = _cs_post_writers + id;
5325 
5326   if (writer->writer == NULL)
5327     _init_writer(writer);
5328 
5329   return writer->writer;
5330 }
5331 
5332 /*----------------------------------------------------------------------------*/
5333 /*!
5334  * \brief Return a pointer to the time control associated to a writer_id.
5335  *
5336  * \param[in]  writer_id  associated writer id
5337  *
5338  * \return  a pointer to a cs_time_control_t structure
5339  */
5340 /*----------------------------------------------------------------------------*/
5341 
5342 cs_time_control_t *
cs_post_get_time_control(int writer_id)5343 cs_post_get_time_control(int  writer_id)
5344 {
5345   int id = _cs_post_writer_id(writer_id);
5346   cs_post_writer_t  *writer = _cs_post_writers + id;
5347 
5348   return &(writer->tc);
5349 }
5350 
5351 /*----------------------------------------------------------------------------*/
5352 /*!
5353  * \brief Return time dependency associated to a writer_id.
5354  *
5355  * \param[in]  writer_id  associated writer id
5356  *
5357  * \return  associated writer's time dependency
5358  */
5359 /*----------------------------------------------------------------------------*/
5360 
5361 fvm_writer_time_dep_t
cs_post_get_writer_time_dep(int writer_id)5362 cs_post_get_writer_time_dep(int  writer_id)
5363 {
5364   int  id;
5365   cs_post_writer_t  *writer = NULL;
5366 
5367   fvm_writer_time_dep_t   time_dep = FVM_WRITER_FIXED_MESH;
5368 
5369   id = _cs_post_writer_id(writer_id);
5370   writer = _cs_post_writers + id;
5371 
5372   if (writer->wd != NULL)
5373     time_dep = writer->wd->time_dep;
5374   else if (writer->writer != NULL)
5375     time_dep = fvm_writer_get_time_dep(writer->writer);
5376 
5377   return time_dep;
5378 }
5379 
5380 /*----------------------------------------------------------------------------*/
5381 /*!
5382  * \brief Add an activation time step for a specific writer or for all writers.
5383  *
5384  * If a negative value is provided, a previously added activation time
5385  * step matching that absolute value will be removed, if present.
5386  *
5387  * \param[in]  writer_id  writer id, or 0 for all writers
5388  * \param[in]  nt         time step value to add (or remove)
5389  */
5390 /*----------------------------------------------------------------------------*/
5391 
5392 void
cs_post_add_writer_t_step(int writer_id,int nt)5393 cs_post_add_writer_t_step(int  writer_id,
5394                           int  nt)
5395 {
5396   int i;
5397 
5398   if (writer_id != 0) {
5399     i = _cs_post_writer_id(writer_id);
5400     _add_writer_ts(_cs_post_writers + i, nt);
5401   }
5402   else {
5403     for (i = 0; i < _cs_post_n_writers; i++)
5404       _add_writer_ts(_cs_post_writers + i, nt);
5405   }
5406 }
5407 
5408 /*----------------------------------------------------------------------------*/
5409 /*!
5410  * \brief Add an activation time value for a specific writer or for all writers.
5411  *
5412  * If a negative value is provided, a previously added activation time
5413  * step matching that absolute value will be removed, if present.
5414  *
5415  * \param[in]  writer_id  writer id, or 0 for all writers
5416  * \param[in]  t          time value to add (or remove)
5417  */
5418 /*----------------------------------------------------------------------------*/
5419 
5420 void
cs_post_add_writer_t_value(int writer_id,double t)5421 cs_post_add_writer_t_value(int     writer_id,
5422                            double  t)
5423 {
5424   int i;
5425 
5426   if (writer_id != 0) {
5427     i = _cs_post_writer_id(writer_id);
5428     _add_writer_tv(_cs_post_writers + i, t);
5429   }
5430   else {
5431     for (i = 0; i < _cs_post_n_writers; i++)
5432       _add_writer_tv(_cs_post_writers + i, t);
5433   }
5434 }
5435 
5436 /*----------------------------------------------------------------------------*/
5437 /*!
5438  * \brief Output post-processing meshes using associated writers.
5439  *
5440  * If the time step structure argument passed is NULL, a time-independent
5441  * output will be assumed.
5442  *
5443  * \param[in]  ts  time step status structure, or NULL
5444  */
5445 /*----------------------------------------------------------------------------*/
5446 
5447 void
cs_post_write_meshes(const cs_time_step_t * ts)5448 cs_post_write_meshes(const cs_time_step_t  *ts)
5449 {
5450   int  i;
5451   cs_post_mesh_t  *post_mesh;
5452 
5453   int t_top_id = cs_timer_stats_switch(_post_out_stat_id);
5454 
5455   /* First loop on meshes, for probes and profiles (which must not be
5456      "reduced" afer first output, as coordinates may be required for
5457      interpolation, and also and share volume or surface location meshes) */
5458 
5459   for (i = 0; i < _cs_post_n_meshes; i++) {
5460     post_mesh = _cs_post_meshes + i;
5461     if (post_mesh->ent_flag[4] != 0)
5462       _cs_post_write_mesh(post_mesh, ts);
5463   }
5464 
5465   /* Main Loops on meshes and writers for output */
5466 
5467   for (i = 0; i < _cs_post_n_meshes; i++) {
5468     post_mesh = _cs_post_meshes + i;
5469     if (post_mesh->ent_flag[4] != 0)
5470       continue;
5471     _cs_post_write_mesh(post_mesh, ts);
5472     /* reduce mesh definitions if not required anymore */
5473     if (   post_mesh->mod_flag_max == FVM_WRITER_FIXED_MESH
5474         && post_mesh->_exp_mesh != NULL)
5475       fvm_nodal_reduce(post_mesh->_exp_mesh, 0);
5476   }
5477 
5478   cs_timer_stats_switch(t_top_id);
5479 }
5480 
5481 /*----------------------------------------------------------------------------*/
5482 /*!
5483  * \brief Output a variable defined at cells or faces of a post-processing mesh
5484  *        using associated writers.
5485  *
5486  * \param[in]  mesh_id      id of associated mesh
5487  * \param[in]  writer_id    id of specified associated writer,
5488  *                          or \ref CS_POST_WRITER_ALL_ASSOCIATED for all
5489  * \param[in]  var_name     name of variable to output
5490  * \param[in]  var_dim      1 for scalar, 3 for vector, 6 for symmetric tensor,
5491  *                          9 for non-symmetric tensor
5492  * \param[in]  interlace    if a vector, true for interlaced values,
5493  *                          false otherwise
5494  * \param[in]  use_parent   true if values are defined on "parent" mesh,
5495  *                          false if values are defined on post-processing mesh
5496  * \param[in]  var_type     variable's data type
5497  * \param[in]  cel_vals     cell values
5498  * \param[in]  i_face_vals  interior face values
5499  * \param[in]  b_face_vals  boundary face values
5500  * \param[in]  ts           time step status structure, or NULL
5501  */
5502 /*----------------------------------------------------------------------------*/
5503 
5504 void
cs_post_write_var(int mesh_id,int writer_id,const char * var_name,int var_dim,bool interlace,bool use_parent,cs_post_type_t var_type,const void * cel_vals,const void * i_face_vals,const void * b_face_vals,const cs_time_step_t * ts)5505 cs_post_write_var(int                    mesh_id,
5506                   int                    writer_id,
5507                   const char            *var_name,
5508                   int                    var_dim,
5509                   bool                   interlace,
5510                   bool                   use_parent,
5511                   cs_post_type_t         var_type,
5512                   const void            *cel_vals,
5513                   const void            *i_face_vals,
5514                   const void            *b_face_vals,
5515                   const cs_time_step_t  *ts)
5516 {
5517   cs_lnum_t  i;
5518   int        _mesh_id;
5519 
5520   cs_interlace_t  _interlace;
5521   cs_datatype_t    datatype;
5522 
5523   size_t       dec_ptr = 0;
5524   int          n_parent_lists = 0;
5525   cs_lnum_t    parent_num_shift[2]  = {0, 0};
5526   cs_real_t   *var_tmp = NULL;
5527   cs_post_mesh_t  *post_mesh = NULL;
5528   cs_post_writer_t    *writer = NULL;
5529 
5530   const void  *var_ptr[2*9] = {NULL, NULL, NULL,
5531                                NULL, NULL, NULL,
5532                                NULL, NULL, NULL,
5533                                NULL, NULL, NULL,
5534                                NULL, NULL, NULL,
5535                                NULL, NULL, NULL};
5536 
5537   int nt_cur = (ts != NULL) ? ts->nt_cur : -1;
5538   double t_cur = (ts != NULL) ? ts->t_cur : 0.;
5539 
5540   /* Initializations */
5541 
5542   _mesh_id = _cs_post_mesh_id_try(mesh_id);
5543 
5544   if (_mesh_id < 0)
5545     return;
5546 
5547   post_mesh = _cs_post_meshes + _mesh_id;
5548 
5549   if (interlace)
5550     _interlace = CS_INTERLACE;
5551   else
5552     _interlace = CS_NO_INTERLACE;
5553 
5554   datatype =  _cs_post_cnv_datatype(var_type);
5555 
5556   /* Assign appropriate array to FVM for output */
5557 
5558   /* Case of cells */
5559   /*---------------*/
5560 
5561   if (post_mesh->ent_flag[CS_POST_LOCATION_CELL] == 1) {
5562 
5563     if (use_parent) {
5564       n_parent_lists = 1;
5565       parent_num_shift[0] = 0;
5566     }
5567     else
5568       n_parent_lists = 0;
5569 
5570     var_ptr[0] = cel_vals;
5571     if (interlace == false) {
5572       if (use_parent)
5573         dec_ptr = cs_glob_mesh->n_cells_with_ghosts;
5574       else
5575         dec_ptr = fvm_nodal_get_n_entities(post_mesh->exp_mesh, 3);
5576       dec_ptr *= cs_datatype_size[datatype];
5577       for (i = 1; i < var_dim; i++)
5578         var_ptr[i] = ((const char *)cel_vals) + i*dec_ptr;
5579     }
5580   }
5581 
5582   /* Case of faces */
5583   /*---------------*/
5584 
5585   else if (   post_mesh->ent_flag[CS_POST_LOCATION_I_FACE] == 1
5586            || post_mesh->ent_flag[CS_POST_LOCATION_B_FACE] == 1) {
5587 
5588     /* In case of indirection, all that is necessary is to set pointers */
5589 
5590     if (use_parent) {
5591 
5592       n_parent_lists = 2;
5593       parent_num_shift[0] = 0;
5594       parent_num_shift[1] = cs_glob_mesh->n_b_faces;
5595 
5596       if (post_mesh->ent_flag[CS_POST_LOCATION_B_FACE] == 1) {
5597         if (interlace == false) {
5598           dec_ptr = cs_glob_mesh->n_b_faces * cs_datatype_size[datatype];
5599           for (i = 0; i < var_dim; i++)
5600             var_ptr[i] = ((const char *)b_face_vals) + i*dec_ptr;
5601         }
5602         else
5603           var_ptr[0] = b_face_vals;
5604       }
5605 
5606       if (post_mesh->ent_flag[CS_POST_LOCATION_I_FACE] == 1) {
5607         if (interlace == false) {
5608           dec_ptr = cs_glob_mesh->n_i_faces * cs_datatype_size[datatype];
5609           for (i = 0; i < var_dim; i++)
5610             var_ptr[var_dim + i] = ((const char *)i_face_vals) + i*dec_ptr;
5611         }
5612         else
5613           var_ptr[1] = i_face_vals;
5614       }
5615 
5616     }
5617 
5618     /* With no indirection, we must switch to a variable defined on two
5619        lists of faces to a variable defined on one list */
5620 
5621     else {
5622 
5623       n_parent_lists = 0;
5624 
5625       if (post_mesh->ent_flag[CS_POST_LOCATION_B_FACE] == 1) {
5626 
5627         /* Case where a variable is defined both on boundary and
5628            interior faces: we must switch to a single list, as
5629            indirection is not used */
5630 
5631         if (post_mesh->ent_flag[CS_POST_LOCATION_I_FACE] == 1) {
5632 
5633           BFT_MALLOC(var_tmp,
5634                      (   post_mesh->n_i_faces
5635                       +  post_mesh->n_b_faces) * var_dim,
5636                      cs_real_t);
5637 
5638           _cs_post_assmb_var_faces(post_mesh->exp_mesh,
5639                                    post_mesh->n_i_faces,
5640                                    post_mesh->n_b_faces,
5641                                    var_dim,
5642                                    _interlace,
5643                                    i_face_vals,
5644                                    b_face_vals,
5645                                    var_tmp);
5646 
5647           _interlace = CS_NO_INTERLACE;
5648 
5649           dec_ptr = cs_datatype_size[datatype] * (  post_mesh->n_i_faces
5650                                                   + post_mesh->n_b_faces);
5651 
5652           for (i = 0; i < var_dim; i++)
5653             var_ptr[i] = ((char *)var_tmp) + i*dec_ptr;
5654 
5655         }
5656 
5657         /* Case where we only have boundary faces */
5658 
5659         else {
5660 
5661           if (interlace == false) {
5662             dec_ptr = cs_datatype_size[datatype] * post_mesh->n_b_faces;
5663             for (i = 0; i < var_dim; i++)
5664               var_ptr[i] = ((const char *)b_face_vals) + i*dec_ptr;
5665           }
5666           else
5667             var_ptr[0] = b_face_vals;
5668         }
5669 
5670       }
5671 
5672       /* Case where we only have interior faces */
5673 
5674       else if (post_mesh->ent_flag[CS_POST_LOCATION_I_FACE] == 1) {
5675 
5676         if (interlace == false) {
5677           dec_ptr = cs_datatype_size[datatype] * post_mesh->n_i_faces;
5678           for (i = 0; i < var_dim; i++)
5679             var_ptr[i] = ((const char *)i_face_vals) + i*dec_ptr;
5680         }
5681         else
5682           var_ptr[0] = i_face_vals;
5683       }
5684 
5685     }
5686 
5687   }
5688 
5689   /* Effective output: loop on writers */
5690   /*-----------------------------------*/
5691 
5692   for (i = 0; i < post_mesh->n_writers; i++) {
5693 
5694     writer = _cs_post_writers + post_mesh->writer_id[i];
5695 
5696     if (writer->id != writer_id && writer_id != CS_POST_WRITER_ALL_ASSOCIATED)
5697       continue;
5698 
5699     if (writer->active == 1) {
5700 
5701       _check_non_transient(writer, &nt_cur, &t_cur);
5702 
5703       fvm_writer_export_field(writer->writer,
5704                               post_mesh->exp_mesh,
5705                               var_name,
5706                               FVM_WRITER_PER_ELEMENT,
5707                               var_dim,
5708                               _interlace,
5709                               n_parent_lists,
5710                               parent_num_shift,
5711                               datatype,
5712                               nt_cur,
5713                               t_cur,
5714                               (const void * *)var_ptr);
5715 
5716       if (nt_cur >= 0) {
5717         writer->tc.last_nt = nt_cur;
5718         writer->tc.last_t = t_cur;
5719       }
5720 
5721     }
5722 
5723   }
5724 
5725   /* Free memory (if both interior and boundary faces present) */
5726 
5727   if (var_tmp != NULL)
5728     BFT_FREE(var_tmp);
5729 }
5730 
5731 /*----------------------------------------------------------------------------*/
5732 /*!
5733  * \brief Output a variable defined at vertices of a post-processing mesh using
5734  *        associated writers.
5735  *
5736  * \param[in]  mesh_id     id of associated mesh
5737  * \param[in]  writer_id   id of specified associated writer,
5738  *                         or \ref CS_POST_WRITER_ALL_ASSOCIATED for all
5739  * \param[in]  var_name    name of variable to output
5740  * \param[in]  var_dim     1 for scalar, 3 for vector, 6 for symmetric tensor,
5741  *                         9 for non-symmetric tensor
5742  * \param[in]  interlace   if a vector, true for interlaced values,
5743  *                         false otherwise
5744  * \param[in]  use_parent  true if values are defined on "parent" mesh,
5745  *                         false if values are defined on post-processing mesh
5746  * \param[in]  var_type    variable's data type
5747  * \param[in]  vtx_vals    vertex values
5748  * \param[in]  ts          time step status structure, or NULL
5749  */
5750 /*----------------------------------------------------------------------------*/
5751 
5752 void
cs_post_write_vertex_var(int mesh_id,int writer_id,const char * var_name,int var_dim,bool interlace,bool use_parent,cs_post_type_t var_type,const void * vtx_vals,const cs_time_step_t * ts)5753 cs_post_write_vertex_var(int                    mesh_id,
5754                          int                    writer_id,
5755                          const char            *var_name,
5756                          int                    var_dim,
5757                          bool                   interlace,
5758                          bool                   use_parent,
5759                          cs_post_type_t         var_type,
5760                          const void            *vtx_vals,
5761                          const cs_time_step_t  *ts)
5762 {
5763   cs_lnum_t  i;
5764   int        _mesh_id;
5765 
5766   cs_post_mesh_t  *post_mesh;
5767   cs_post_writer_t  *writer;
5768   cs_interlace_t  _interlace;
5769   cs_datatype_t  datatype;
5770 
5771   size_t       dec_ptr = 0;
5772   int          n_parent_lists = 0;
5773   cs_lnum_t    parent_num_shift[1]  = {0};
5774 
5775   const void  *var_ptr[9] = {NULL, NULL, NULL,
5776                              NULL, NULL, NULL,
5777                              NULL, NULL, NULL};
5778 
5779   int nt_cur = (ts != NULL) ? ts->nt_cur : -1;
5780   double t_cur = (ts != NULL) ? ts->t_cur : 0.;
5781 
5782   /* Initializations */
5783 
5784   _mesh_id = _cs_post_mesh_id_try(mesh_id);
5785 
5786   if (_mesh_id < 0)
5787     return;
5788 
5789   post_mesh = _cs_post_meshes + _mesh_id;
5790 
5791   if (interlace)
5792     _interlace = CS_INTERLACE;
5793   else
5794     _interlace = CS_NO_INTERLACE;
5795 
5796   assert(   sizeof(cs_real_t) == sizeof(double)
5797          || sizeof(cs_real_t) == sizeof(float));
5798 
5799   datatype =  _cs_post_cnv_datatype(var_type);
5800 
5801   /* Assign appropriate array to FVM for output */
5802 
5803   if (use_parent)
5804     n_parent_lists = 1;
5805   else
5806     n_parent_lists = 0;
5807 
5808   var_ptr[0] = vtx_vals;
5809   if (interlace == false) {
5810     if (use_parent)
5811       dec_ptr = cs_glob_mesh->n_vertices;
5812     else
5813       dec_ptr =   fvm_nodal_get_n_entities(post_mesh->exp_mesh, 0)
5814                 * cs_datatype_size[datatype];
5815     for (i = 1; i < var_dim; i++)
5816       var_ptr[i] = ((const char *)vtx_vals) + i*dec_ptr;
5817   }
5818 
5819   /* Effective output: loop on writers */
5820   /*-----------------------------------*/
5821 
5822   for (i = 0; i < post_mesh->n_writers; i++) {
5823 
5824     writer = _cs_post_writers + post_mesh->writer_id[i];
5825 
5826     if (writer->id != writer_id && writer_id != CS_POST_WRITER_ALL_ASSOCIATED)
5827       continue;
5828 
5829     if (writer->active == 1) {
5830 
5831       _check_non_transient(writer, &nt_cur, &t_cur);
5832 
5833       fvm_writer_export_field(writer->writer,
5834                               post_mesh->exp_mesh,
5835                               var_name,
5836                               FVM_WRITER_PER_NODE,
5837                               var_dim,
5838                               _interlace,
5839                               n_parent_lists,
5840                               parent_num_shift,
5841                               datatype,
5842                               nt_cur,
5843                               t_cur,
5844                               (const void * *)var_ptr);
5845 
5846       if (nt_cur >= 0) {
5847         writer->tc.last_t = nt_cur;
5848         writer->tc.last_t = t_cur;
5849       }
5850 
5851     }
5852 
5853   }
5854 
5855 }
5856 
5857 /*----------------------------------------------------------------------------*/
5858 /*!
5859  * \brief Output an existing lagrangian particle attribute at particle
5860  *        positions or trajectory endpoints of a particle mesh using
5861  *        associated writers.
5862  *
5863  * \param[in]  mesh_id       id of associated mesh
5864  * \param[in]  writer_id     id of specified associated writer,
5865  *                           or \ref CS_POST_WRITER_ALL_ASSOCIATED for all
5866  * \param[in]  attr_id       associated particle attribute id
5867  * \param[in]  var_name      name of variable to output
5868  * \param[in]  component_id  if -1 : extract the whole attribute
5869  *                           if >0 : id of the component to extract
5870  * \param[in]  ts            time step status structure, or NULL
5871  */
5872 /*----------------------------------------------------------------------------*/
5873 
5874 void
cs_post_write_particle_values(int mesh_id,int writer_id,int attr_id,const char * var_name,int component_id,const cs_time_step_t * ts)5875 cs_post_write_particle_values(int                    mesh_id,
5876                               int                    writer_id,
5877                               int                    attr_id,
5878                               const char            *var_name,
5879                               int                    component_id,
5880                               const cs_time_step_t  *ts)
5881 {
5882   int  _mesh_id, i, _length;
5883   int _stride_export_field = 1;
5884   cs_post_mesh_t  *post_mesh;
5885   cs_post_writer_t  *writer;
5886 
5887   cs_lagr_attribute_t  attr = attr_id;
5888 
5889   cs_lnum_t    n_particles = 0, n_pts = 0;
5890   cs_lnum_t    parent_num_shift[1]  = {0};
5891   cs_lnum_t   *particle_list = NULL;
5892 
5893   size_t  extents, size;
5894   ptrdiff_t  displ;
5895   cs_datatype_t datatype;
5896   int  stride;
5897   unsigned char *vals = NULL;
5898 
5899   int nt_cur = (ts != NULL) ? ts->nt_cur : -1;
5900   double t_cur = (ts != NULL) ? ts->t_cur : 0.;
5901 
5902   const void  *var_ptr[1] = {NULL};
5903 
5904   /* Initializations */
5905 
5906   _mesh_id = _cs_post_mesh_id_try(mesh_id);
5907 
5908   if (_mesh_id < 0)
5909     return;
5910 
5911   post_mesh = _cs_post_meshes + _mesh_id;
5912 
5913   if (post_mesh->ent_flag[3] == 0 || post_mesh->exp_mesh == NULL)
5914     return;
5915 
5916   n_particles = cs_lagr_get_n_particles();
5917 
5918   const cs_lagr_particle_set_t  *p_set = cs_lagr_get_particle_set();
5919 
5920   assert(p_set != NULL);
5921 
5922   /* Get attribute values info, returning if not present */
5923 
5924   cs_lagr_get_attr_info(p_set, 0, attr,
5925                         &extents, &size, &displ, &datatype, &stride);
5926 
5927   if (stride == 0)
5928     return;
5929   else {
5930     if (component_id == -1) {
5931       _length = size;
5932       _stride_export_field = stride;
5933     }
5934     else {
5935       _length = size/stride;
5936       _stride_export_field = 1;
5937      }
5938   }
5939 
5940   assert(ts->nt_cur > -1);
5941 
5942   /* Allocate work arrays */
5943 
5944   n_pts = fvm_nodal_get_n_entities(post_mesh->exp_mesh, 0);
5945 
5946   BFT_MALLOC(vals, n_pts*_length, unsigned char);
5947 
5948   var_ptr[0] = vals;
5949 
5950   if (n_pts != n_particles) {
5951     int parent_dim = (post_mesh->ent_flag[3] == 2) ? 1 : 0;
5952     BFT_MALLOC(particle_list, n_particles, cs_lnum_t);
5953     fvm_nodal_get_parent_num(post_mesh->exp_mesh, parent_dim, particle_list);
5954   }
5955 
5956   /* Particle values */
5957 
5958   if (post_mesh->ent_flag[3] == 1)
5959     cs_lagr_get_particle_values(p_set,
5960                                 attr,
5961                                 datatype,
5962                                 stride,
5963                                 component_id,
5964                                 n_pts,
5965                                 particle_list,
5966                                 vals);
5967 
5968   else if (post_mesh->ent_flag[3] == 2) {
5969     nt_cur = -1; t_cur = 0.;
5970     cs_lagr_get_trajectory_values(p_set,
5971                                   attr,
5972                                   datatype,
5973                                   stride,
5974                                   component_id,
5975                                   n_pts/2,
5976                                   particle_list,
5977                                   vals);
5978   }
5979 
5980   BFT_FREE(particle_list);
5981 
5982   /* Effective output: loop on writers */
5983   /*-----------------------------------*/
5984 
5985   for (i = 0; i < post_mesh->n_writers; i++) {
5986 
5987     writer = _cs_post_writers + post_mesh->writer_id[i];
5988 
5989     if (writer->id != writer_id && writer_id != CS_POST_WRITER_ALL_ASSOCIATED)
5990       continue;
5991 
5992     if (writer->active == 1) {
5993 
5994       fvm_writer_export_field(writer->writer,
5995                               post_mesh->exp_mesh,
5996                               var_name,
5997                               FVM_WRITER_PER_NODE,
5998                               _stride_export_field,
5999                               CS_INTERLACE,
6000                               0, /* n_parent_lists, */
6001                               parent_num_shift,
6002                               datatype,
6003                               nt_cur,
6004                               t_cur,
6005                               (const void * *)var_ptr);
6006 
6007       if (nt_cur >= 0) {
6008         writer->tc.last_nt = nt_cur;
6009         writer->tc.last_t = t_cur;
6010       }
6011 
6012     }
6013 
6014   }
6015 
6016   BFT_FREE(vals);
6017 }
6018 
6019 /*----------------------------------------------------------------------------*/
6020 /*!
6021  * \brief Output a variable defined at cells or faces of a post-processing mesh
6022  *        using associated writers.
6023  *
6024  * \param[in]  mesh_id              id of associated mesh
6025  * \param[in]  writer_id            id of specified associated writer,
6026  *                                  or \ref CS_POST_WRITER_ALL_ASSOCIATED for all
6027  * \param[in]  var_name             name of variable to output
6028  * \param[in]  var_dim              1 for scalar, 3 for vector, 6 for symmetric
6029  *                                  tensor, 9 for non-symmetric tensor
6030  * \param[in]  var_type             variable's data type
6031  * \param[in]  parent_location_id   asociated values mesh location, or 0
6032  *                                  if values are passed directly
6033  * \param[in]  interpolate_func     pointer to interpolation function,
6034  *                                  or NULL for default
6035  * \param[in]  interpolate_input    pointer to optional interpolation input
6036  *                                  data, or NULL for default
6037  * \param[in]  vals                 variable's values
6038  * \param[in]  ts                   time step status structure, or NULL
6039  */
6040 /*----------------------------------------------------------------------------*/
6041 
6042 void
cs_post_write_probe_values(int mesh_id,int writer_id,const char * var_name,int var_dim,cs_post_type_t var_type,int parent_location_id,cs_interpolate_from_location_t * interpolate_func,void * interpolate_input,const void * vals,const cs_time_step_t * ts)6043 cs_post_write_probe_values(int                              mesh_id,
6044                            int                              writer_id,
6045                            const char                      *var_name,
6046                            int                              var_dim,
6047                            cs_post_type_t                   var_type,
6048                            int                              parent_location_id,
6049                            cs_interpolate_from_location_t  *interpolate_func,
6050                            void                            *interpolate_input,
6051                            const void                      *vals,
6052                            const cs_time_step_t            *ts)
6053 {
6054   int nt_cur = (ts != NULL) ? ts->nt_cur : -1;
6055   double t_cur = (ts != NULL) ? ts->t_cur : 0.;
6056 
6057   bool on_boundary;
6058 
6059   /* Initializations */
6060 
6061   int _mesh_id = _cs_post_mesh_id_try(mesh_id);
6062 
6063   if (_mesh_id < 0)
6064     return;
6065 
6066   cs_post_mesh_t  *post_mesh = _cs_post_meshes + _mesh_id;
6067   cs_probe_set_t  *pset = (cs_probe_set_t *)post_mesh->sel_input[4];
6068 
6069   cs_probe_set_get_post_info(pset,
6070                              NULL,
6071                              &on_boundary,
6072                              NULL,
6073                              NULL,
6074                              NULL,
6075                              NULL,
6076                              NULL,
6077                              NULL);
6078 
6079   cs_datatype_t datatype = _cs_post_cnv_datatype(var_type);
6080 
6081   const void  *var_ptr[1] = {vals};
6082   unsigned char *_vals = NULL;
6083 
6084   /* Extract or interpolate values */
6085 
6086   if (parent_location_id > 0) {
6087 
6088     const cs_lnum_t  n_points = fvm_nodal_get_n_entities(post_mesh->exp_mesh, 0);
6089     const cs_lnum_t *elt_ids = cs_probe_set_get_elt_ids(pset,
6090                                                         parent_location_id);
6091 
6092     cs_interpolate_from_location_t  *_interpolate_func = interpolate_func;
6093 
6094     cs_coord_t  *point_coords = NULL;
6095 
6096     if (_interpolate_func == NULL)
6097       _interpolate_func = cs_interpolate_from_location_p0;
6098 
6099     BFT_MALLOC(_vals,
6100                n_points*cs_datatype_size[datatype]*var_dim,
6101                unsigned char);
6102 
6103     if (_interpolate_func != cs_interpolate_from_location_p0) {
6104       BFT_MALLOC(point_coords, n_points*3, cs_coord_t);
6105       fvm_nodal_get_vertex_coords(post_mesh->exp_mesh,
6106                                   CS_INTERLACE,
6107                                   point_coords);
6108     }
6109 
6110     _interpolate_func(interpolate_input,
6111                       datatype,
6112                       var_dim,
6113                       n_points,
6114                       elt_ids,
6115                       (const cs_real_3_t *)point_coords,
6116                       vals,
6117                       _vals);
6118     var_ptr[0] = _vals;
6119 
6120     BFT_FREE(point_coords);
6121   }
6122 
6123   /* Effective output: loop on writers */
6124   /*-----------------------------------*/
6125 
6126   for (int i = 0; i < post_mesh->n_writers; i++) {
6127 
6128     cs_post_writer_t *writer = _cs_post_writers + post_mesh->writer_id[i];
6129 
6130     if (writer->id != writer_id && writer_id != CS_POST_WRITER_ALL_ASSOCIATED)
6131       continue;
6132 
6133     if (writer->active == 1) {
6134 
6135       cs_lnum_t  parent_num_shift[1] = {0};
6136 
6137       fvm_writer_export_field(writer->writer,
6138                               post_mesh->exp_mesh,
6139                               var_name,
6140                               FVM_WRITER_PER_NODE,
6141                               var_dim,
6142                               CS_INTERLACE,
6143                               0, /* n_parent_lists */
6144                               parent_num_shift,
6145                               datatype,
6146                               nt_cur,
6147                               t_cur,
6148                               (const void **)var_ptr);
6149 
6150       if (nt_cur >= 0) {
6151         writer->tc.last_nt = nt_cur;
6152         writer->tc.last_t = t_cur;
6153       }
6154 
6155     }
6156 
6157   }
6158 
6159   BFT_FREE(_vals);
6160 }
6161 
6162 /*----------------------------------------------------------------------------*/
6163 /*!
6164  * \brief Update references to parent mesh of post-processing meshes in case of
6165  * computational mesh cell renumbering.
6166  *
6167  * This function may be called only once, after possible renumbering of cells,
6168  * to update existing post-processing meshes. Post-processing meshes defined
6169  * after renumbering will automatically be based upon the new numbering,
6170  * so this function will not need to be called again.
6171  *
6172  * \param[in]  init_cell_num  initial cell numbering (new -> old)
6173  */
6174 /*----------------------------------------------------------------------------*/
6175 
6176 void
cs_post_renum_cells(const cs_lnum_t init_cell_num[])6177 cs_post_renum_cells(const cs_lnum_t  init_cell_num[])
6178 {
6179   int        i;
6180   cs_lnum_t  icel;
6181   cs_lnum_t  n_elts;
6182 
6183   cs_lnum_t  *renum_ent_parent = NULL;
6184 
6185   bool  need_doing = false;
6186 
6187   cs_post_mesh_t   *post_mesh;
6188   const cs_mesh_t  *mesh = cs_glob_mesh;
6189 
6190   if (init_cell_num == NULL)
6191     return;
6192 
6193   /* Loop on meshes */
6194 
6195   for (i = 0; i < _cs_post_n_meshes; i++) {
6196 
6197     post_mesh = _cs_post_meshes + i;
6198 
6199     if (post_mesh->ent_flag[CS_POST_LOCATION_CELL] > 0)
6200       need_doing = true;
6201   }
6202 
6203   if (need_doing == true) {
6204 
6205     /* Prepare renumbering */
6206 
6207     n_elts = mesh->n_cells;
6208 
6209     BFT_MALLOC(renum_ent_parent, n_elts, cs_lnum_t);
6210 
6211     for (icel = 0; icel < mesh->n_cells; icel++)
6212       renum_ent_parent[init_cell_num[icel]] = icel + 1;
6213 
6214     /* Effective modification */
6215 
6216     for (i = 0; i < _cs_post_n_meshes; i++) {
6217 
6218       post_mesh = _cs_post_meshes + i;
6219 
6220       if (   post_mesh->_exp_mesh != NULL
6221           && post_mesh->ent_flag[CS_POST_LOCATION_CELL] > 0) {
6222 
6223         fvm_nodal_change_parent_num(post_mesh->_exp_mesh,
6224                                     renum_ent_parent,
6225                                     3);
6226 
6227       }
6228 
6229     }
6230 
6231     BFT_FREE(renum_ent_parent);
6232 
6233   }
6234 
6235 }
6236 
6237 /*----------------------------------------------------------------------------*/
6238 /*!
6239  * \brief Update references to parent mesh of post-processing meshes in case of
6240  * computational mesh interior and/or boundary faces renumbering.
6241  *
6242  * This function may be called only once, after possible renumbering of faces,
6243  * to update existing post-processing meshes. Post-processing meshes defined
6244  * after renumbering will automatically be based upon the new numbering,
6245  * so this function will not need to be called again.
6246  *
6247  * \param[in]  init_i_face_num  initial interior numbering (new -> old)
6248  * \param[in]  init_b_face_num  initial boundary numbering (new -> old)
6249  */
6250 /*----------------------------------------------------------------------------*/
6251 
6252 void
cs_post_renum_faces(const cs_lnum_t init_i_face_num[],const cs_lnum_t init_b_face_num[])6253 cs_post_renum_faces(const cs_lnum_t  init_i_face_num[],
6254                     const cs_lnum_t  init_b_face_num[])
6255 {
6256   int       i;
6257   cs_lnum_t  ifac;
6258   cs_lnum_t  n_elts;
6259 
6260   cs_lnum_t  *renum_ent_parent = NULL;
6261 
6262   bool  need_doing = false;
6263 
6264   cs_post_mesh_t   *post_mesh;
6265   const cs_mesh_t  *mesh = cs_glob_mesh;
6266 
6267   /* Loop on meshes */
6268 
6269   for (i = 0; i < _cs_post_n_meshes; i++) {
6270 
6271     post_mesh = _cs_post_meshes + i;
6272 
6273     if (   post_mesh->ent_flag[CS_POST_LOCATION_I_FACE] > 0
6274         || post_mesh->ent_flag[CS_POST_LOCATION_B_FACE] > 0) {
6275       need_doing = true;
6276     }
6277 
6278   }
6279 
6280   if (need_doing == true) {
6281 
6282     /* Prepare renumbering */
6283 
6284     n_elts = mesh->n_i_faces + mesh->n_b_faces;
6285 
6286     BFT_MALLOC(renum_ent_parent, n_elts, cs_lnum_t);
6287 
6288     if (init_b_face_num == NULL) {
6289       for (ifac = 0; ifac < mesh->n_b_faces; ifac++)
6290         renum_ent_parent[ifac] = ifac + 1;
6291     }
6292     else {
6293       for (ifac = 0; ifac < mesh->n_b_faces; ifac++)
6294         renum_ent_parent[init_b_face_num[ifac]] = ifac + 1;
6295     }
6296 
6297     if (init_i_face_num == NULL) {
6298       for (ifac = 0, i = mesh->n_b_faces;
6299            ifac < mesh->n_i_faces;
6300            ifac++, i++)
6301         renum_ent_parent[mesh->n_b_faces + ifac]
6302           = mesh->n_b_faces + ifac + 1;
6303     }
6304     else {
6305       for (ifac = 0, i = mesh->n_b_faces;
6306            ifac < mesh->n_i_faces;
6307            ifac++, i++)
6308         renum_ent_parent[mesh->n_b_faces + init_i_face_num[ifac]]
6309           = mesh->n_b_faces + ifac + 1;
6310     }
6311 
6312     /* Effective modification */
6313 
6314     for (i = 0; i < _cs_post_n_meshes; i++) {
6315 
6316       post_mesh = _cs_post_meshes + i;
6317 
6318       if (   post_mesh->_exp_mesh != NULL
6319           && (   post_mesh->ent_flag[CS_POST_LOCATION_I_FACE] > 0
6320               || post_mesh->ent_flag[CS_POST_LOCATION_B_FACE] > 0)) {
6321 
6322         fvm_nodal_change_parent_num(post_mesh->_exp_mesh,
6323                                     renum_ent_parent,
6324                                     2);
6325 
6326       }
6327 
6328     }
6329 
6330     BFT_FREE(renum_ent_parent);
6331   }
6332 }
6333 
6334 /*----------------------------------------------------------------------------*/
6335 /*!
6336  * \brief Configure the post-processing output so that mesh connectivity
6337  * may be automatically updated.
6338  *
6339  * This is done for meshes defined using selection criteria or functions.
6340  * The behavior of Lagrangian meshes is unchanged.
6341  *
6342  * To be effective, this function should be called before defining
6343  * postprocessing meshes.
6344  */
6345 /*----------------------------------------------------------------------------*/
6346 
6347 void
cs_post_set_changing_connectivity(void)6348 cs_post_set_changing_connectivity(void)
6349 {
6350   _cs_post_mod_flag_min = FVM_WRITER_TRANSIENT_CONNECT;
6351 }
6352 
6353 /*----------------------------------------------------------------------------*/
6354 /*!
6355  * \brief Initialize post-processing writers
6356  */
6357 /*----------------------------------------------------------------------------*/
6358 
6359 void
cs_post_init_writers(void)6360 cs_post_init_writers(void)
6361 {
6362   /* Ensure default is defined */
6363 
6364   if (!cs_post_writer_exists(CS_POST_WRITER_DEFAULT))
6365     cs_post_define_writer(CS_POST_WRITER_DEFAULT,   /* writer_id */
6366                           "results",                /* writer name */
6367                           _cs_post_dirname,
6368                           "EnSight Gold",           /* format name */
6369                           "separate_meshes",        /* format options */
6370                           FVM_WRITER_FIXED_MESH,
6371                           false,                    /* output_at_start */
6372                           true,                     /* output at end */
6373                           -1,                       /* time step interval */
6374                           -1.0);                    /* time value interval */
6375 
6376   /* Additional writers for Lagrangian output */
6377 
6378   if (_lagrangian_needed(NULL)) {
6379 
6380     /* Particles */
6381 
6382     if (!cs_post_writer_exists(CS_POST_WRITER_PARTICLES))
6383       cs_post_define_writer(CS_POST_WRITER_PARTICLES,  /* writer_id */
6384                             "particles",            /* writer name */
6385                             _cs_post_dirname,
6386                             "EnSight Gold",         /* format name */
6387                             "",                     /* format options */
6388                             FVM_WRITER_TRANSIENT_CONNECT,
6389                             false,                  /* output_at_start */
6390                             true,                   /* output at end */
6391                             -1,                     /* time step interval */
6392                             -1.0);                  /* time value interval */
6393 
6394     if (!cs_post_writer_exists(CS_POST_WRITER_TRAJECTORIES))
6395       cs_post_define_writer(CS_POST_WRITER_TRAJECTORIES, /* writer_id */
6396                             "trajectories",         /* writer name */
6397                             _cs_post_dirname,
6398                             "EnSight Gold",         /* format name */
6399                             "",                     /* format options */
6400                             FVM_WRITER_FIXED_MESH,
6401                             false,                  /* output_at_start */
6402                             true,                   /* output at end */
6403                             1,                      /* time step interval */
6404                             -1.0);                  /* time value interval */
6405 
6406   }
6407 
6408   /* Additional writers for probe monitoring, profiles */
6409 
6410   if (!cs_post_writer_exists(CS_POST_WRITER_PROBES))
6411     cs_post_define_writer(CS_POST_WRITER_PROBES,    /* writer_id */
6412                           "",                       /* writer name */
6413                           "monitoring",
6414                           "time_plot",              /* format name */
6415                           "",                       /* format options */
6416                           FVM_WRITER_FIXED_MESH,
6417                           false,                    /* output_at_start */
6418                           false,                    /* output at end */
6419                           1,                        /* time step interval */
6420                           -1.0);                    /* time value interval */
6421 
6422   if (!cs_post_writer_exists(CS_POST_WRITER_PROFILES))
6423     cs_post_define_writer(CS_POST_WRITER_PROFILES,  /* writer_id */
6424                           "",                       /* writer name */
6425                           "profiles",
6426                           "plot",                   /* format name */
6427                           "",                       /* format options */
6428                           FVM_WRITER_FIXED_MESH,
6429                           false,                    /* output_at_start */
6430                           true,                     /* output at end */
6431                           -1,                       /* time step interval */
6432                           -1.0);                    /* time value interval */
6433 
6434   /* Additional writers for histograms */
6435 
6436   if (!cs_post_writer_exists(CS_POST_WRITER_HISTOGRAMS))
6437     cs_post_define_writer(CS_POST_WRITER_HISTOGRAMS,  /* writer_id */
6438                           "histograms",               /* writer name */
6439                           "histograms",
6440                           "histogram",                /* format name */
6441                           "txt",                      /* format options */
6442                           FVM_WRITER_FIXED_MESH,
6443                           false,                      /* output_at_start */
6444                           true,                       /* output at end */
6445                           -1,                         /* time step interval */
6446                           -1.0);                      /* time value interval */
6447 
6448   /* Print info on writers */
6449 
6450   _writer_info();
6451 }
6452 
6453 /*----------------------------------------------------------------------------*/
6454 /*!
6455  * \brief Initialize main post-processing meshes
6456  *
6457  * The check_flag variable is a mask, used for additionnal post-processing:
6458  *
6459  *  - If (check_flag & 1), volume submeshes are output by groups if more
6460  *    than one group is present and the default writer uses the EnSight format.
6461  *
6462  *  - If (check_flag & 2), boundary submeshes are output by groups if more
6463  *    than one group is present and the default writer uses the EnSight format.
6464  *
6465  * It is recommended that post-processing meshes be defined before calling
6466  * this function, though specific "automatic" meshes (for example those
6467  * related to couplings) may be defined between this call and a time loop.
6468  *
6469  * \param[in]  check_mask  mask used for additional output
6470  */
6471 /*----------------------------------------------------------------------------*/
6472 
6473 void
cs_post_init_meshes(int check_mask)6474 cs_post_init_meshes(int check_mask)
6475 {
6476   { /* Definition of default post-processing meshes if this has not been
6477        done yet */
6478 
6479     const int  writer_ids[] = {CS_POST_WRITER_DEFAULT};
6480 
6481     if (!cs_post_mesh_exists(CS_POST_MESH_VOLUME))
6482       cs_post_define_volume_mesh(CS_POST_MESH_VOLUME,
6483                                  _("Fluid domain"),
6484                                  "all[]",
6485                                  true,
6486                                  true,
6487                                  1,
6488                                  writer_ids);
6489 
6490     if (!cs_post_mesh_exists(CS_POST_MESH_BOUNDARY))
6491       cs_post_define_surface_mesh(CS_POST_MESH_BOUNDARY,
6492                                   _("Boundary"),
6493                                   NULL,
6494                                   "all[]",
6495                                   true,
6496                                   true,
6497                                   1,
6498                                   writer_ids);
6499 
6500   }
6501 
6502   /* Additional writers for Lagrangian output */
6503 
6504   if (_lagrangian_needed(NULL)) {
6505     if (!cs_post_mesh_exists(CS_POST_MESH_PARTICLES)) {
6506       const int writer_ids[] = {CS_POST_WRITER_PARTICLES};
6507       cs_post_define_particles_mesh(CS_POST_MESH_PARTICLES,
6508                                     _("Particles"),
6509                                     "all[]",
6510                                     1.0,    /* density */
6511                                     false,  /* trajectory */
6512                                     true,   /* auto_variables */
6513                                     1,
6514                                     writer_ids);
6515     }
6516   }
6517 
6518   /* Define probe meshes if needed */
6519 
6520   int n_probe_sets = cs_probe_get_n_sets();
6521 
6522   for (int pset_id = 0; pset_id < n_probe_sets; pset_id++) {
6523 
6524     bool  time_varying, is_profile, on_boundary, auto_variables;
6525 
6526     int  n_writers = 0;
6527     int  *writer_ids = NULL;
6528     cs_probe_set_t  *pset = cs_probe_set_get_by_id(pset_id);
6529     int  post_mesh_id = cs_post_get_free_mesh_id();
6530 
6531     cs_probe_set_get_post_info(pset,
6532                                &time_varying,
6533                                &on_boundary,
6534                                &is_profile,
6535                                &auto_variables,
6536                                NULL,
6537                                NULL,
6538                                &n_writers,
6539                                &writer_ids);
6540 
6541     if (is_profile) { /* User has to define an associated writer */
6542 
6543       _cs_post_define_probe_mesh(post_mesh_id,
6544                                  pset,
6545                                  time_varying,
6546                                  is_profile,
6547                                  on_boundary,
6548                                  auto_variables,
6549                                  n_writers,
6550                                  writer_ids);
6551 
6552     }
6553     else { /* Monitoring points and profiles*/
6554 
6555       /* Handle default writer assignment */
6556 
6557       if (n_writers < 0) {
6558 
6559         if (! is_profile) {
6560           const int  default_writer_ids[] = {CS_POST_WRITER_PROBES};
6561           cs_probe_set_associate_writers(pset, 1, default_writer_ids);
6562         }
6563 
6564         else
6565           cs_probe_set_associate_writers(pset, 0, NULL);
6566 
6567         cs_probe_set_get_post_info(pset, NULL, NULL, NULL,
6568                                    NULL, NULL, NULL,
6569                                    &n_writers, &writer_ids);
6570 
6571       }
6572 
6573       /* Now define associated mesh if active */
6574 
6575       if (n_writers > 0)
6576         _cs_post_define_probe_mesh(post_mesh_id,
6577                                    pset,
6578                                    time_varying,
6579                                    is_profile,
6580                                    on_boundary,
6581                                    auto_variables,
6582                                    n_writers,
6583                                    writer_ids);
6584 
6585     }
6586 
6587   } /* Loop on sets of probes */
6588 
6589   /* Remove meshes which are associated with no writer */
6590 
6591   _clear_unused_meshes();
6592 
6593   /* Add group parts if necessary (EnSight format) */
6594 
6595   if (check_mask & 1) {
6596     const char *fmt_name = fvm_writer_format_name(_cs_post_default_format_id);
6597     if (!strcmp(fmt_name, "EnSight Gold")) {
6598       for (int id = 0; id < _cs_post_n_meshes; id++) {
6599         if ((_cs_post_meshes + id)->id == CS_POST_MESH_VOLUME)
6600           _vol_submeshes_by_group(cs_glob_mesh,
6601                                   fmt_name,
6602                                   _cs_post_default_format_options);
6603         if ((_cs_post_meshes + id)->id == CS_POST_MESH_BOUNDARY)
6604           _boundary_submeshes_by_group(cs_glob_mesh,
6605                                        fmt_name,
6606                                        _cs_post_default_format_options);
6607       }
6608     }
6609   }
6610 
6611 #if 0
6612   /* Compute connectivity if not already done for delayed definitions */
6613 
6614   for (i = 0; i < _cs_post_n_meshes; i++) {
6615     cs_post_mesh_t  *post_mesh = _cs_post_meshes + i;
6616     if (post_mesh->exp_mesh == NULL)
6617       _define_mesh(post_mesh, NULL);
6618   }
6619 #endif
6620 
6621   /* Initial output */
6622 
6623   cs_post_write_meshes(NULL);
6624 }
6625 
6626 /*----------------------------------------------------------------------------*/
6627 /*!
6628  * \brief Check if post-processing is activated and then update post-processing
6629  *        of meshes if there is a need to update time-dependent meshes
6630  *
6631  * \param[in]  ts  time step status structure, or NULL
6632  */
6633 /*----------------------------------------------------------------------------*/
6634 
6635 void
cs_post_time_step_begin(const cs_time_step_t * ts)6636 cs_post_time_step_begin(const cs_time_step_t  *ts)
6637 {
6638   assert(ts != NULL); /* Sanity check */
6639 
6640   /* Activation or not of each writer according to the time step */
6641 
6642   cs_post_activate_by_time_step(ts);
6643 
6644   /* User-defined activation of writers for a fine-grained control */
6645 
6646   cs_user_postprocess_activate(ts->nt_max,
6647                                ts->nt_cur,
6648                                ts->t_cur);
6649 
6650   /* Possible modification of post-processing meshes */
6651   /*-------------------------------------------------*/
6652 
6653   _update_meshes(ts);
6654 }
6655 
6656 /*----------------------------------------------------------------------------*/
6657 /*!
6658  * \brief Loop on post-processing meshes to output variables.
6659  *
6660  * This handles all default fields output, as well as all
6661  * registered output functions and outputs defined in
6662  * \ref cs_user_postprocess_values
6663  *
6664  * \param[in]  ts  time step status structure, or NULL
6665  */
6666 /*----------------------------------------------------------------------------*/
6667 
6668 void
cs_post_time_step_output(const cs_time_step_t * ts)6669 cs_post_time_step_output(const cs_time_step_t  *ts)
6670 {
6671   int  j;
6672   bool  active;
6673 
6674   /* Loop on writers to check if something must be done */
6675   /*----------------------------------------------------*/
6676 
6677   for (j = 0; j < _cs_post_n_writers; j++) {
6678     cs_post_writer_t  *writer = _cs_post_writers + j;
6679     if (writer->active == 1)
6680       break;
6681   }
6682   if (j == _cs_post_n_writers)
6683     return;
6684 
6685   int t_top_id = cs_timer_stats_switch(_post_out_stat_id);
6686 
6687   /* Output of variables by registered function instances */
6688   /*------------------------------------------------------*/
6689 
6690   for (int i = 0; i < _cs_post_n_output_tp; i++)
6691     _cs_post_f_output_tp[i](_cs_post_i_output_tp[i], ts);
6692 
6693   /* Output of variables associated with post-processing meshes */
6694   /*------------------------------------------------------------*/
6695 
6696   /* n_elts_max already initialized before and during the
6697      eventual modification of post-processing mesh definitions,
6698      and parent_ids allocated if n_elts_max > 0 */
6699 
6700   cs_lnum_t  *parent_ids = NULL;
6701   cs_lnum_t  n_elts_max = 0;
6702 
6703   /* Main loop on post-processing meshes */
6704 
6705   for (int i = 0; i < _cs_post_n_meshes; i++) {
6706 
6707     cs_post_mesh_t  *post_mesh = _cs_post_meshes + i;
6708 
6709     active = false;
6710 
6711     for (j = 0; j < post_mesh->n_writers; j++) {
6712       cs_post_writer_t  *writer = _cs_post_writers + post_mesh->writer_id[j];
6713       if (writer->active == 1)
6714         active = true;
6715     }
6716 
6717     /* If the mesh is active at this time step */
6718     /*-----------------------------------------*/
6719 
6720     if (active == true) {
6721 
6722       const fvm_nodal_t  *exp_mesh = post_mesh->exp_mesh;
6723 
6724       if (exp_mesh == NULL)
6725         continue;
6726 
6727       int  dim_ent = fvm_nodal_get_max_entity_dim(exp_mesh);
6728       cs_lnum_t  n_elts = fvm_nodal_get_n_entities(exp_mesh, dim_ent);
6729 
6730       if (n_elts > n_elts_max) {
6731         n_elts_max = n_elts;
6732         BFT_REALLOC(parent_ids, n_elts_max, cs_lnum_t);
6733       }
6734 
6735       /* Get corresponding element ids */
6736 
6737       fvm_nodal_get_parent_num(exp_mesh, dim_ent, parent_ids);
6738 
6739       for (cs_lnum_t k = 0; k < n_elts; k++)
6740         parent_ids[k] -= 1;
6741 
6742       /* We can output variables for this time step */
6743       /*--------------------------------------------*/
6744 
6745       cs_lnum_t  n_cells = 0, n_i_faces = 0, n_b_faces = 0;
6746       cs_lnum_t  *cell_ids = NULL, *i_face_ids = NULL, *b_face_ids = NULL;
6747 
6748       /* Here list sizes are adjusted, and we point to the array filled
6749          by fvm_nodal_get_parent_num() if possible. */
6750 
6751       if (dim_ent == 3) {
6752         n_cells = n_elts;
6753         cell_ids = parent_ids;
6754       }
6755 
6756       /* The numbers of "parent" interior faces known by FVM
6757          are shifted by the total number of boundary faces */
6758 
6759       else if (dim_ent == 2 && n_elts > 0) {
6760 
6761         cs_lnum_t  b_f_num_shift = cs_glob_mesh->n_b_faces;
6762 
6763         for (cs_lnum_t ind_fac = 0; ind_fac < n_elts; ind_fac++) {
6764           if (parent_ids[ind_fac] >= b_f_num_shift)
6765             n_i_faces++;
6766           else
6767             n_b_faces++;
6768         }
6769 
6770         /* boundary faces only: parent FVM face numbers unchanged */
6771         if (n_i_faces == 0) {
6772           b_face_ids = parent_ids;
6773         }
6774 
6775         /* interior faces only: parents FVM face numbers shifted */
6776         else if (n_b_faces == 0) {
6777           for (cs_lnum_t ind_fac = 0; ind_fac < n_elts; ind_fac++)
6778             parent_ids[ind_fac] -= b_f_num_shift;
6779           i_face_ids = parent_ids;
6780         }
6781 
6782         /* interior and boundary faces: numbers must be separated */
6783 
6784         else {
6785 
6786           BFT_MALLOC(i_face_ids, n_i_faces, cs_lnum_t);
6787           BFT_MALLOC(b_face_ids, n_b_faces, cs_lnum_t);
6788 
6789           n_i_faces = 0, n_b_faces = 0;
6790 
6791           for (cs_lnum_t ind_fac = 0; ind_fac < n_elts; ind_fac++) {
6792             if (parent_ids[ind_fac] >= b_f_num_shift)
6793               i_face_ids[n_i_faces++] = parent_ids[ind_fac] - b_f_num_shift;
6794             else
6795               b_face_ids[n_b_faces++] = parent_ids[ind_fac];
6796           }
6797 
6798         }
6799 
6800         /* In all cases, update the number of interior and boundary faces
6801            (useful in case of splitting of FVM mesh elements) for functions
6802            called by this one */
6803 
6804         post_mesh->n_i_faces = n_i_faces;
6805         post_mesh->n_b_faces = n_b_faces;
6806 
6807       }
6808 
6809       /* Output of zone information if necessary */
6810 
6811       _cs_post_write_transient_zone_info(post_mesh, ts);
6812 
6813       /* Standard post-processing */
6814 
6815       if (post_mesh->sel_input[4] != NULL)
6816         _cs_post_output_profile_coords(post_mesh, ts);
6817 
6818       if (post_mesh->cat_id < 0)
6819         _cs_post_output_fields(post_mesh, ts);
6820 
6821       if (post_mesh->n_a_fields > 0)
6822         _cs_post_output_attached_fields(post_mesh, ts);
6823 
6824       /* Output of variables by registered function instances */
6825 
6826       for (j = 0; j < _cs_post_n_output_mtp; j++)
6827         _cs_post_f_output_mtp[j](_cs_post_i_output_mtp[j],
6828                                  post_mesh->id,
6829                                  post_mesh->cat_id,
6830                                  post_mesh->ent_flag,
6831                                  n_cells,
6832                                  n_i_faces,
6833                                  n_b_faces,
6834                                  cell_ids,
6835                                  i_face_ids,
6836                                  b_face_ids,
6837                                  ts);
6838 
6839       /* User-defined output */
6840 
6841       cs_lnum_t  n_vertices = cs_post_mesh_get_n_vertices(post_mesh->id);
6842 
6843       if (post_mesh->sel_input[4] == NULL) {
6844 
6845         cs_lnum_t *vertex_ids;
6846         BFT_MALLOC(vertex_ids, n_vertices, cs_lnum_t);
6847         cs_post_mesh_get_vertex_ids(post_mesh->id, vertex_ids);
6848 
6849         cs_user_postprocess_values(post_mesh->name,
6850                                    post_mesh->id,
6851                                    post_mesh->cat_id,
6852                                    NULL,
6853                                    n_cells,
6854                                    n_i_faces,
6855                                    n_b_faces,
6856                                    n_vertices,
6857                                    cell_ids,
6858                                    i_face_ids,
6859                                    b_face_ids,
6860                                    vertex_ids,
6861                                    ts);
6862 
6863         BFT_FREE(vertex_ids);
6864 
6865         /* In case of mixed interior and boundary faces, free
6866            additional arrays */
6867 
6868         if (i_face_ids != NULL && b_face_ids != NULL) {
6869           BFT_FREE(i_face_ids);
6870           BFT_FREE(b_face_ids);
6871         }
6872 
6873       }
6874 
6875       else { /* if (post_mesh->sel_input[4] != NULL) */
6876 
6877         bool on_boundary = false;
6878         const cs_lnum_t *_cell_ids = NULL, *_b_face_ids = NULL;
6879         const cs_lnum_t *vertex_ids = NULL;
6880         cs_probe_set_t  *pset = (cs_probe_set_t *)post_mesh->sel_input[4];
6881         const char *mesh_name = cs_probe_set_get_name(pset);
6882 
6883         cs_probe_set_get_post_info(pset,
6884                                    NULL,
6885                                    &on_boundary,
6886                                    NULL,
6887                                    NULL,
6888                                    NULL,
6889                                    NULL,
6890                                    NULL,
6891                                    NULL);
6892 
6893         if (on_boundary) {
6894           n_b_faces = n_vertices; /* n_probes */
6895           _b_face_ids = cs_probe_set_get_elt_ids(pset,
6896                                                  CS_MESH_LOCATION_BOUNDARY_FACES);
6897         }
6898         else {
6899           n_cells = n_vertices; /* n_probes */
6900           _cell_ids = cs_probe_set_get_elt_ids(pset,
6901                                                CS_MESH_LOCATION_CELLS);
6902         }
6903         vertex_ids = cs_probe_set_get_elt_ids(pset,
6904                                               CS_MESH_LOCATION_VERTICES);
6905 
6906         cs_user_postprocess_values(mesh_name,
6907                                    post_mesh->id,
6908                                    post_mesh->cat_id,
6909                                    pset,
6910                                    n_cells,
6911                                    0,
6912                                    n_b_faces,
6913                                    n_vertices,
6914                                    _cell_ids,
6915                                    NULL,
6916                                    _b_face_ids,
6917                                    vertex_ids,
6918                                    ts);
6919 
6920       }
6921 
6922     }
6923 
6924   }
6925 
6926   /* Free memory */
6927 
6928   BFT_FREE(parent_ids);
6929 
6930   cs_timer_stats_switch(t_top_id);
6931 }
6932 
6933 /*----------------------------------------------------------------------------*/
6934 /*!
6935  * \brief Flush writers and free time-varying and Lagragian mesh if needed
6936  *        of meshes if there is a time-dependent mesh
6937  */
6938 /*----------------------------------------------------------------------------*/
6939 
6940 void
cs_post_time_step_end(void)6941 cs_post_time_step_end(void)
6942 {
6943   int t_top_id = cs_timer_stats_switch(_post_out_stat_id);
6944 
6945   /* Flush writers if necessary */
6946 
6947   for (int i = 0; i < _cs_post_n_writers; i++) {
6948     cs_post_writer_t  *writer = _cs_post_writers + i;
6949     if (writer->active == 1) {
6950       if (writer->writer != NULL)
6951         fvm_writer_flush(writer->writer);
6952     }
6953   }
6954 
6955   /* Free time-varying and Lagrangian meshes unless they
6956      are mapped to an existing mesh */
6957 
6958   for (int i = 0; i < _cs_post_n_meshes; i++) {
6959     cs_post_mesh_t  *post_mesh = _cs_post_meshes + i;
6960     if (post_mesh->_exp_mesh != NULL) {
6961       if (   post_mesh->ent_flag[3]
6962           || post_mesh->mod_flag_min == FVM_WRITER_TRANSIENT_CONNECT) {
6963         post_mesh->exp_mesh = NULL;
6964         post_mesh->_exp_mesh = fvm_nodal_destroy(post_mesh->_exp_mesh);
6965       }
6966     }
6967   }
6968 
6969   cs_timer_stats_switch(t_top_id);
6970 }
6971 
6972 /*----------------------------------------------------------------------------*/
6973 /*!
6974  * \brief Loop on post-processing meshes to output variables.
6975  *
6976  * This handles all default fields output, as well as all
6977  * registered output functions and outputs defined in
6978  * \ref cs_user_postprocess_values
6979  *
6980  * \param[in]  ts  time step status structure, or NULL
6981  */
6982 /*----------------------------------------------------------------------------*/
6983 
6984 void
cs_post_write_vars(const cs_time_step_t * ts)6985 cs_post_write_vars(const cs_time_step_t  *ts)
6986 {
6987   /* Output meshes if needed */
6988 
6989   _update_meshes(ts);
6990 
6991   /* Loop on post-processing meshes to output variables */
6992 
6993   cs_post_time_step_output(ts);
6994 
6995   /* Flush writers and free time-varying and Lagragian mesh if needed */
6996 
6997   cs_post_time_step_end();
6998 }
6999 
7000 /*----------------------------------------------------------------------------*/
7001 /*!
7002  * \brief Destroy all structures associated with post-processing
7003  */
7004 /*----------------------------------------------------------------------------*/
7005 
7006 void
cs_post_finalize(void)7007 cs_post_finalize(void)
7008 {
7009   int i, j;
7010   cs_post_mesh_t  *post_mesh = NULL;
7011 
7012   /* Timings */
7013 
7014   for (i = 0; i < _cs_post_n_writers; i++) {
7015     cs_timer_counter_t m_time, f_time, a_time;
7016     fvm_writer_t *writer = (_cs_post_writers + i)->writer;
7017     CS_TIMER_COUNTER_INIT(m_time);
7018     CS_TIMER_COUNTER_INIT(f_time);
7019     CS_TIMER_COUNTER_INIT(a_time);
7020     if (writer != NULL) {
7021       fvm_writer_get_times(writer,
7022                            &m_time, &f_time, &a_time);
7023       cs_log_printf(CS_LOG_PERFORMANCE,
7024                     _("\n"
7025                       "Writing of \"%s\" (%s) summary:\n"
7026                       "\n"
7027                       "  Elapsed time for meshes:          %12.3f\n"
7028                       "  Elapsed time for variables:       %12.3f\n"
7029                       "  Elapsed time forcing output:      %12.3f\n"),
7030                     fvm_writer_get_name(writer),
7031                     fvm_writer_get_format(writer),
7032                     m_time.nsec*1e-9,
7033                     f_time.nsec*1e-9,
7034                     a_time.nsec*1e-9);
7035     }
7036   }
7037 
7038   cs_log_printf(CS_LOG_PERFORMANCE, "\n");
7039   cs_log_separator(CS_LOG_PERFORMANCE);
7040 
7041   /* Exportable meshes */
7042 
7043   for (i = 0; i < _cs_post_n_meshes; i++) {
7044     post_mesh = _cs_post_meshes + i;
7045     if (post_mesh->_exp_mesh != NULL)
7046       fvm_nodal_destroy(post_mesh->_exp_mesh);
7047     BFT_FREE(post_mesh->name);
7048     for (j = 0; j < 4; j++)
7049       BFT_FREE(post_mesh->criteria[j]);
7050     BFT_FREE(post_mesh->writer_id);
7051     BFT_FREE(post_mesh->a_field_info);
7052   }
7053 
7054   BFT_FREE(_cs_post_meshes);
7055 
7056   _cs_post_min_mesh_id = _MIN_RESERVED_MESH_ID;
7057   _cs_post_n_meshes = 0;
7058   _cs_post_n_meshes_max = 0;
7059 
7060   /* Writers */
7061 
7062   for (i = 0; i < _cs_post_n_writers; i++) {
7063     cs_post_writer_t  *writer = _cs_post_writers + i;
7064     if (writer->ot != NULL)
7065       _free_writer_times(writer);
7066     if (writer->wd != NULL)
7067       _destroy_writer_def(writer);
7068     if (writer->writer != NULL)
7069       fvm_writer_finalize((_cs_post_writers + i)->writer);
7070   }
7071 
7072   BFT_FREE(_cs_post_writers);
7073 
7074   _cs_post_n_writers = 0;
7075   _cs_post_n_writers_max = 0;
7076 
7077   /* Registered processings if necessary */
7078 
7079   if (_cs_post_n_output_tp_max > 0) {
7080     BFT_FREE(_cs_post_f_output_tp);
7081     BFT_FREE(_cs_post_i_output_tp);
7082   }
7083 
7084   if (_cs_post_n_output_mtp_max > 0) {
7085     BFT_FREE(_cs_post_f_output_mtp);
7086     BFT_FREE(_cs_post_i_output_mtp);
7087   }
7088 
7089   /* Options */
7090 
7091   BFT_FREE(_cs_post_default_format_options);
7092 }
7093 
7094 /*----------------------------------------------------------------------------*/
7095 /*!
7096  * \brief Postprocess free (isolated) faces of the current global mesh
7097  */
7098 /*----------------------------------------------------------------------------*/
7099 
7100 void
cs_post_add_free_faces(void)7101 cs_post_add_free_faces(void)
7102 {
7103   cs_lnum_t i, j;
7104   cs_lnum_t n_f_faces = 0;
7105   cs_gnum_t n_no_group = 0;
7106   int max_null_family = 0;
7107   cs_lnum_t *f_face_list = NULL;
7108 
7109   fvm_writer_t *writer = NULL;
7110   fvm_nodal_t *exp_mesh = NULL;
7111 
7112   bool  generate_submeshes = false;
7113   cs_mesh_t *mesh = cs_glob_mesh;
7114   const char *fmt_name = fvm_writer_format_name(_cs_post_default_format_id);
7115 
7116   if (mesh->n_g_free_faces == 0)
7117     return;
7118 
7119   /* Create default writer */
7120 
7121   writer = fvm_writer_init("isolated_faces",
7122                            _cs_post_dirname,
7123                            fmt_name,
7124                            _cs_post_default_format_options,
7125                            FVM_WRITER_FIXED_MESH);
7126 
7127   /* Build list of faces to extract */
7128 
7129   BFT_MALLOC(f_face_list, mesh->n_b_faces, cs_lnum_t);
7130 
7131   for (i = 0; i < mesh->n_b_faces; i++) {
7132     if (mesh->b_face_cells[i] < 0)
7133       f_face_list[n_f_faces++] = i + 1;
7134   }
7135 
7136   /* Extract and output mesh of isolated faces */
7137 
7138   exp_mesh = cs_mesh_connect_faces_to_nodal(cs_glob_mesh,
7139                                             "isolated faces",
7140                                             true,
7141                                             0,
7142                                             n_f_faces,
7143                                             NULL,
7144                                             f_face_list);
7145 
7146   if (fvm_writer_needs_tesselation(writer, exp_mesh, FVM_FACE_POLY) > 0)
7147     fvm_nodal_tesselate(exp_mesh, FVM_FACE_POLY, NULL);
7148 
7149   fvm_writer_set_mesh_time(writer, -1, 0);
7150   fvm_writer_export_nodal(writer, exp_mesh);
7151 
7152   exp_mesh = fvm_nodal_destroy(exp_mesh);
7153 
7154   /* Now check if we should generate additional meshes (EnSight Gold format) */
7155 
7156   if (!strcmp(fmt_name, "EnSight Gold") && mesh->n_families > 0) {
7157 
7158     generate_submeshes = true;
7159 
7160     /* Families should be sorted, so if a nonzero family is empty,
7161        it is family 1 */
7162     if (mesh->family_item[0] == 0)
7163       max_null_family = 1;
7164     if (mesh->n_families <= max_null_family)
7165       generate_submeshes = false;
7166 
7167     /* Check how many boundary faces belong to no group */
7168 
7169     if (mesh->b_face_family != NULL) {
7170       for (j = 0; j < n_f_faces; j++) {
7171         if (mesh->b_face_family[f_face_list[j] - 1] <= max_null_family)
7172           n_no_group += 1;
7173       }
7174     }
7175     else
7176       n_no_group = n_f_faces;
7177 
7178     cs_parall_counter(&n_no_group, 1);
7179 
7180     if (n_no_group == mesh->n_g_free_faces)
7181       generate_submeshes = false;
7182   }
7183 
7184   /* Generate submeshes if necessary */
7185 
7186   if (generate_submeshes) {
7187 
7188     cs_lnum_t n_b_faces;
7189     int *fam_flag = NULL;
7190     char *group_flag = NULL;
7191     cs_lnum_t *b_face_list = NULL;
7192     char part_name[81];
7193 
7194     /* Now detect which groups may be referenced */
7195 
7196     BFT_MALLOC(fam_flag, mesh->n_families + 1, int);
7197     memset(fam_flag, 0, (mesh->n_families + 1)*sizeof(int));
7198 
7199     if (mesh->b_face_family != NULL) {
7200       for (i = 0; i < n_f_faces; i++)
7201         fam_flag[mesh->b_face_family[f_face_list[i] - 1]] = 1;
7202     }
7203 
7204     group_flag = _build_group_flag(mesh, fam_flag);
7205 
7206     /* Now extract isolated faces by groups.
7207        Selector structures may not have been initialized yet,
7208        so we use a direct selection here. */
7209 
7210     BFT_REALLOC(fam_flag, mesh->n_families, int);
7211 
7212     BFT_MALLOC(b_face_list, mesh->n_b_faces, cs_lnum_t);
7213 
7214     for (i = 0; i < mesh->n_groups; i++) {
7215 
7216       if (group_flag[i] != 0) {
7217 
7218         const char *g_name = mesh->group + mesh->group_idx[i];
7219 
7220         _set_fam_flags(mesh, i, fam_flag);
7221 
7222         n_b_faces = 0;
7223         if (mesh->b_face_family != NULL) {
7224           for (j = 0; j < n_f_faces; j++) {
7225             cs_lnum_t face_id = f_face_list[j] - 1;
7226             int fam_id = mesh->b_face_family[face_id];
7227             if (fam_id > 0 && fam_flag[fam_id - 1])
7228               b_face_list[n_b_faces++] = face_id + 1;
7229           }
7230         }
7231 
7232         strcpy(part_name, "isolated: ");
7233         strncat(part_name, g_name, 80 - strlen(part_name));
7234 
7235         exp_mesh = cs_mesh_connect_faces_to_nodal(cs_glob_mesh,
7236                                                   part_name,
7237                                                   false,
7238                                                   0,
7239                                                   n_b_faces,
7240                                                   NULL,
7241                                                   b_face_list);
7242 
7243         if (fvm_writer_needs_tesselation(writer, exp_mesh, FVM_FACE_POLY) > 0)
7244           fvm_nodal_tesselate(exp_mesh, FVM_FACE_POLY, NULL);
7245 
7246         fvm_writer_set_mesh_time(writer, -1, 0);
7247         fvm_writer_export_nodal(writer, exp_mesh);
7248 
7249         exp_mesh = fvm_nodal_destroy(exp_mesh);
7250       }
7251 
7252     }
7253 
7254     /* Output boundary faces belonging to no group */
7255 
7256     if (n_no_group > 0) {
7257 
7258       if (mesh->b_face_family != NULL) {
7259         for (j = 0, n_b_faces = 0; j < n_f_faces; j++) {
7260           cs_lnum_t face_id = f_face_list[j] - 1;
7261           if (mesh->b_face_family[face_id] <= max_null_family)
7262             b_face_list[n_b_faces++] = face_id + 1;
7263         }
7264       }
7265       else {
7266         for (j = 0, n_b_faces = 0; j < n_f_faces; j++) {
7267           cs_lnum_t face_id = f_face_list[j] - 1;
7268           b_face_list[n_b_faces++] = face_id + 1;
7269         }
7270       }
7271 
7272       exp_mesh = cs_mesh_connect_faces_to_nodal(cs_glob_mesh,
7273                                                 "isolated: no_group",
7274                                                 false,
7275                                                 0,
7276                                                 n_b_faces,
7277                                                 NULL,
7278                                                 b_face_list);
7279 
7280       if (fvm_writer_needs_tesselation(writer, exp_mesh, FVM_FACE_POLY) > 0)
7281         fvm_nodal_tesselate(exp_mesh, FVM_FACE_POLY, NULL);
7282 
7283       fvm_writer_set_mesh_time(writer, -1, 0);
7284       fvm_writer_export_nodal(writer, exp_mesh);
7285 
7286       exp_mesh = fvm_nodal_destroy(exp_mesh);
7287     }
7288 
7289     BFT_FREE(b_face_list);
7290 
7291     BFT_FREE(fam_flag);
7292     BFT_FREE(group_flag);
7293 
7294   } /* End of submeshes generation */
7295 
7296   /* Free memory */
7297 
7298   writer = fvm_writer_finalize(writer);
7299 
7300   BFT_FREE(f_face_list);
7301 }
7302 
7303 /*----------------------------------------------------------------------------*/
7304 /*!
7305  * \brief Initialize post-processing writer with same format and associated
7306  * options as default writer, but no time dependency, intended to
7307  * troubleshoot errors.
7308  */
7309 /*----------------------------------------------------------------------------*/
7310 
7311 void
cs_post_init_error_writer(void)7312 cs_post_init_error_writer(void)
7313 {
7314   /* Default values */
7315 
7316   int writer_id = CS_POST_WRITER_ERRORS;
7317   if (cs_post_writer_exists(writer_id))
7318     return;
7319 
7320   /* Create default writer */
7321 
7322   int default_format_id = _cs_post_default_format_id;
7323   const char *default_format_options = _cs_post_default_format_options;
7324   const char null_str[] = "";
7325 
7326   /* Special case for Catalyst: if matching co-processing script is
7327      not available, revert to EnSight Gold format */
7328 
7329   if (default_format_id == fvm_writer_get_format_id("Catalyst")) {
7330     if (! cs_file_isreg("error.py")) {
7331       default_format_id = fvm_writer_get_format_id("EnSight Gold");
7332       default_format_options = null_str;
7333     }
7334   }
7335 
7336   cs_post_define_writer(writer_id,
7337                         "error",
7338                         _cs_post_dirname,
7339                         fvm_writer_format_name(default_format_id),
7340                         default_format_options,
7341                         FVM_WRITER_FIXED_MESH, /* No time dependency here */
7342                         false,
7343                         true,
7344                         -1,
7345                         -1.0);
7346 }
7347 
7348 /*----------------------------------------------------------------------------*/
7349 /*!
7350  * \brief Initialize post-processing writer with same format and associated
7351  * options as default writer, but no time dependency, and associate
7352  * and output global volume mesh.
7353  *
7354  * This is intended to help troubleshoot errors using fields based
7355  * on cells.
7356  *
7357  * \return  id of error output mesh (< 0), or 0 if all writers are deactivated
7358  */
7359 /*----------------------------------------------------------------------------*/
7360 
7361 int
cs_post_init_error_writer_cells(void)7362 cs_post_init_error_writer_cells(void)
7363 {
7364   int mesh_id = 0;
7365 
7366   const int writer_id = CS_POST_WRITER_ERRORS;
7367   const char *mesh_name = N_("Calculation domain");
7368 
7369   cs_post_init_error_writer();
7370   cs_post_activate_writer(writer_id, 1);
7371 
7372   mesh_id = cs_post_get_free_mesh_id();
7373 
7374   cs_post_define_volume_mesh(mesh_id,
7375                              _(mesh_name),
7376                              "all[]",
7377                              false,
7378                              false,
7379                              1,
7380                              &writer_id);
7381 
7382   cs_post_write_meshes(NULL);
7383 
7384   return mesh_id;
7385 }
7386 
7387 /*----------------------------------------------------------------------------*/
7388 /*!
7389  * \brief Register a processing of time-dependent variables to the call to
7390  * cs_post_write_vars().
7391  *
7392  * Note: if the input pointer is non-NULL, it must point to valid data
7393  * when the output function is called, so either:
7394  * - that value or structure should not be temporary (i.e. local);
7395  * - post-processing output must be ensured using cs_post_write_var()
7396  *   or similar before the data pointed to goes out of scope.
7397  *
7398  * \param[in]       function  function to register
7399  * \param[in, out]  input     pointer to optional (untyped) value or structure
7400  */
7401 /*----------------------------------------------------------------------------*/
7402 
7403 void
cs_post_add_time_dep_output(cs_post_time_dep_output_t * function,void * input)7404 cs_post_add_time_dep_output(cs_post_time_dep_output_t  *function,
7405                             void                       *input)
7406 {
7407   /* Resize array of registered post-processings if necessary */
7408 
7409   if (_cs_post_n_output_tp >= _cs_post_n_output_tp_max) {
7410     if (_cs_post_n_output_tp_max == 0)
7411       _cs_post_n_output_tp_max = 8;
7412     else
7413       _cs_post_n_output_tp_max *= 2;
7414     BFT_REALLOC(_cs_post_f_output_tp,
7415                 _cs_post_n_output_tp_max,
7416                 cs_post_time_dep_output_t *);
7417     BFT_REALLOC(_cs_post_i_output_tp, _cs_post_n_output_tp_max, void *);
7418   }
7419 
7420   /* Add a post-processing */
7421 
7422   _cs_post_f_output_tp[_cs_post_n_output_tp] = function;
7423   _cs_post_i_output_tp[_cs_post_n_output_tp] = input;
7424 
7425   _cs_post_n_output_tp += 1;
7426 }
7427 
7428 /*----------------------------------------------------------------------------*/
7429 /*!
7430  * \brief Register a processing of time-dependent variables than can be output
7431  * on different meshes to the call to cs_post_write_vars().
7432  *
7433  * Note: if the input pointer is non-NULL, it must point to valid data
7434  * when the output function is called, so either:
7435  * - that value or structure should not be temporary (i.e. local);
7436  * - post-processing output must be ensured using cs_post_write_var()
7437  *   or similar before the data pointed to goes out of scope.
7438  *
7439  * \param[in]       function  function to register
7440  * \param[in, out]  input     pointer to optional (untyped) value or structure
7441  */
7442 /*----------------------------------------------------------------------------*/
7443 
7444 void
cs_post_add_time_mesh_dep_output(cs_post_time_mesh_dep_output_t * function,void * input)7445 cs_post_add_time_mesh_dep_output(cs_post_time_mesh_dep_output_t  *function,
7446                                  void                            *input)
7447 {
7448   /* Resize array of registered post-processings if necessary */
7449 
7450   if (_cs_post_n_output_mtp >= _cs_post_n_output_mtp_max) {
7451     if (_cs_post_n_output_mtp_max == 0)
7452       _cs_post_n_output_mtp_max = 8;
7453     else
7454       _cs_post_n_output_mtp_max *= 2;
7455     BFT_REALLOC(_cs_post_f_output_mtp,
7456                 _cs_post_n_output_mtp_max,
7457                 cs_post_time_mesh_dep_output_t *);
7458     BFT_REALLOC(_cs_post_i_output_mtp, _cs_post_n_output_mtp_max, void *);
7459   }
7460 
7461   /* Add a post-processing */
7462 
7463   _cs_post_f_output_mtp[_cs_post_n_output_mtp] = function;
7464   _cs_post_i_output_mtp[_cs_post_n_output_mtp] = input;
7465 
7466   _cs_post_n_output_mtp += 1;
7467 }
7468 
7469 /*----------------------------------------------------------------------------*/
7470 
7471 END_C_DECLS
7472