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