LCOV - code coverage report
Current view: top level - api - output_registry.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 83.3 % 78 65
Test Date: 2026-03-04 10:22:18 Functions: 85.7 % 14 12

            Line data    Source code
       1              : #include "cfd/io/output_registry.h"
       2              : #include "../io/csv_output_internal.h"
       3              : #include "../io/vtk_output_internal.h"
       4              : #include "cfd/api/simulation_api.h"
       5              : #include "cfd/core/filesystem.h"
       6              : #include "cfd/core/logging.h"
       7              : #include "cfd/core/memory.h"
       8              : 
       9              : 
      10              : #include <string.h>
      11              : 
      12              : //=============================================================================
      13              : // OUTPUT REGISTRY STRUCTURE
      14              : //=============================================================================
      15              : 
      16              : #define MAX_OUTPUT_CONFIGS 16
      17              : 
      18              : struct OutputRegistry {
      19              :     output_config configs[MAX_OUTPUT_CONFIGS];
      20              :     int count;
      21              :     char run_dir[512];    // Cached run directory path
      22              :     int run_dir_created;  // Flag to track if run directory was created
      23              : };
      24              : 
      25              : //=============================================================================
      26              : // OUTPUT REGISTRY LIFECYCLE
      27              : //=============================================================================
      28              : 
      29           75 : output_registry* output_registry_create(void) {
      30           75 :     output_registry* reg = (output_registry*)cfd_calloc(1, sizeof(struct OutputRegistry));
      31           75 :     if (reg) {
      32           75 :         reg->count = 0;
      33           75 :         reg->run_dir[0] = '\0';
      34           75 :         reg->run_dir_created = 0;
      35              :     }
      36           75 :     return reg;
      37              : }
      38              : 
      39           76 : void output_registry_destroy(output_registry* reg) {
      40           76 :     if (reg) {
      41           75 :         cfd_free(reg);
      42              :     }
      43           76 : }
      44              : 
      45              : //=============================================================================
      46              : // OUTPUT REGISTRY CONFIGURATION
      47              : //=============================================================================
      48              : 
      49           19 : void output_registry_add(output_registry* reg, output_field_type field_type, int interval,
      50              :                          const char* prefix) {
      51           19 :     if (!reg) {
      52              :         return;
      53              :     }
      54              : 
      55              :     // Check if we have space
      56           18 :     if (reg->count >= MAX_OUTPUT_CONFIGS) {
      57            0 :         cfd_warning("Maximum number of output configurations reached");
      58            0 :         return;
      59              :     }
      60              : 
      61              :     // Add new configuration
      62           18 :     reg->configs[reg->count].field_type = field_type;
      63           18 :     reg->configs[reg->count].interval = interval;
      64           18 :     reg->configs[reg->count].prefix = prefix;
      65           18 :     reg->count++;
      66              : }
      67              : 
      68            9 : void output_registry_clear(output_registry* reg) {
      69            9 :     if (reg) {
      70            8 :         reg->count = 0;
      71              :     }
      72            9 : }
      73              : 
      74            7 : int output_registry_count(const output_registry* reg) {
      75            7 :     return reg ? reg->count : 0;
      76              : }
      77              : 
      78           51 : int output_registry_has_type(const output_registry* reg, output_field_type field_type) {
      79           51 :     if (!reg) {
      80              :         return 0;
      81              :     }
      82              : 
      83           80 :     for (int i = 0; i < reg->count; i++) {
      84           44 :         if (reg->configs[i].field_type == field_type) {
      85              :             return 1;
      86              :         }
      87              :     }
      88              :     return 0;
      89              : }
      90              : 
      91              : //=============================================================================
      92              : // RUN DIRECTORY MANAGEMENT
      93              : //=============================================================================
      94              : 
      95            9 : const char* output_registry_get_run_dir(output_registry* reg, const char* base_dir,
      96              :                                         const char* run_prefix, size_t nx, size_t ny) {
      97            9 :     if (!reg) {
      98              :         return NULL;
      99              :     }
     100              : 
     101              :     // Only create once
     102            9 :     if (reg->run_dir_created) {
     103            4 :         return reg->run_dir;
     104              :     }
     105              : 
     106              :     // Create run directory with prefix using custom base directory (re-entrant)
     107            5 :     const char* prefix = run_prefix ? run_prefix : "sim";
     108            5 :     cfd_create_run_directory_ex_with_base(reg->run_dir, sizeof(reg->run_dir), base_dir, prefix, nx,
     109              :                                           ny);
     110              : 
     111            5 :     reg->run_dir_created = 1;
     112            5 :     return reg->run_dir;
     113              : }
     114              : 
     115              : //=============================================================================
     116              : // OUTPUT DISPATCH
     117              : //=============================================================================
     118              : 
     119              : // Function pointer type for output dispatchers (with derived fields)
     120              : typedef void (*output_dispatch_func)(const char* run_dir, const char* prefix, int step,
     121              :                                      double current_time, const flow_field* field,
     122              :                                      const derived_fields* derived, const grid* grid,
     123              :                                      const ns_solver_params_t* params, const ns_solver_stats_t* stats);
     124              : 
     125              : // VTK output wrappers
     126            1 : static void dispatch_vtk_velocity_magnitude(const char* run_dir, const char* prefix, int step,
     127              :                                             double current_time, const flow_field* field,
     128              :                                             const derived_fields* derived, const grid* grid,
     129              :                                             const ns_solver_params_t* params,
     130              :                                             const ns_solver_stats_t* stats) {
     131            1 :     (void)current_time;
     132            1 :     (void)field;
     133            1 :     (void)params;
     134            1 :     (void)stats;
     135              :     // Use pre-computed velocity magnitude from derived fields
     136            1 :     if (derived && derived->velocity_magnitude) {
     137            1 :         vtk_write_scalar_field(run_dir, prefix, step, "velocity_magnitude",
     138              :                                derived->velocity_magnitude, grid);
     139              :     }
     140            1 : }
     141              : 
     142            1 : static void dispatch_vtk_velocity(const char* run_dir, const char* prefix, int step,
     143              :                                   double current_time, const flow_field* field,
     144              :                                   const derived_fields* derived, const grid* grid,
     145              :                                   const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
     146            1 :     (void)current_time;
     147            1 :     (void)derived;
     148            1 :     (void)params;
     149            1 :     (void)stats;
     150            1 :     vtk_dispatch_output(VTK_OUTPUT_VELOCITY, run_dir, prefix, step, field, grid);
     151            1 : }
     152              : 
     153            0 : static void dispatch_vtk_full_field(const char* run_dir, const char* prefix, int step,
     154              :                                     double current_time, const flow_field* field,
     155              :                                     const derived_fields* derived, const grid* grid,
     156              :                                     const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
     157            0 :     (void)current_time;
     158            0 :     (void)derived;
     159            0 :     (void)params;
     160            0 :     (void)stats;
     161            0 :     vtk_dispatch_output(VTK_OUTPUT_FULL_FIELD, run_dir, prefix, step, field, grid);
     162            0 : }
     163              : 
     164              : // CSV output wrappers (pass derived fields)
     165            1 : static void dispatch_csv_timeseries(const char* run_dir, const char* prefix, int step,
     166              :                                     double current_time, const flow_field* field,
     167              :                                     const derived_fields* derived, const grid* grid,
     168              :                                     const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
     169            1 :     csv_dispatch_output(CSV_OUTPUT_TIMESERIES, run_dir, prefix, step, current_time, field, derived,
     170              :                         grid, params, stats);
     171            1 : }
     172              : 
     173            0 : static void dispatch_csv_centerline(const char* run_dir, const char* prefix, int step,
     174              :                                     double current_time, const flow_field* field,
     175              :                                     const derived_fields* derived, const grid* grid,
     176              :                                     const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
     177            0 :     csv_dispatch_output(CSV_OUTPUT_CENTERLINE, run_dir, prefix, step, current_time, field, derived,
     178              :                         grid, params, stats);
     179            0 : }
     180              : 
     181            1 : static void dispatch_csv_statistics(const char* run_dir, const char* prefix, int step,
     182              :                                     double current_time, const flow_field* field,
     183              :                                     const derived_fields* derived, const grid* grid,
     184              :                                     const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
     185            1 :     csv_dispatch_output(CSV_OUTPUT_STATISTICS, run_dir, prefix, step, current_time, field, derived,
     186              :                         grid, params, stats);
     187            1 : }
     188              : 
     189              : // Output dispatch table - indexed by output_field_type
     190              : // This provides O(1) lookup with no branch prediction issues
     191              : static const output_dispatch_func output_dispatch_table[] = {
     192              :     dispatch_vtk_velocity_magnitude,  // OUTPUT_VELOCITY_MAGNITUDE = 0
     193              :     dispatch_vtk_velocity,            // OUTPUT_VELOCITY = 1
     194              :     dispatch_vtk_full_field,          // OUTPUT_FULL_FIELD = 2
     195              :     dispatch_csv_timeseries,          // OUTPUT_CSV_TIMESERIES = 3
     196              :     dispatch_csv_centerline,          // OUTPUT_CSV_CENTERLINE = 4
     197              :     dispatch_csv_statistics           // OUTPUT_CSV_STATISTICS = 5
     198              : };
     199              : 
     200              : #define OUTPUT_DISPATCH_TABLE_SIZE \
     201              :     (sizeof(output_dispatch_table) / sizeof(output_dispatch_table[0]))
     202              : 
     203              : //=============================================================================
     204              : // OUTPUT WRITING
     205              : //=============================================================================
     206              : 
     207            9 : void output_registry_write_outputs(output_registry* reg, const char* run_dir, int step,
     208              :                                    double current_time, const flow_field* field,
     209              :                                    const derived_fields* derived, const grid* grid,
     210              :                                    const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
     211            9 :     if (!reg || !run_dir) {
     212              :         return;
     213              :     }
     214              : 
     215              :     // Process each registered output
     216           17 :     for (int i = 0; i < reg->count; i++) {
     217            8 :         output_config* config = &reg->configs[i];
     218              : 
     219              :         // Skip if not time to output
     220            8 :         if (config->interval <= 0 || step % config->interval != 0) {
     221            4 :             continue;
     222              :         }
     223              : 
     224              :         // Bounds check and dispatch via function table
     225              :         // This is cacheable and avoids branch prediction issues
     226            4 :         if (config->field_type < OUTPUT_DISPATCH_TABLE_SIZE) {
     227            4 :             output_dispatch_table[config->field_type](run_dir, config->prefix, step, current_time,
     228              :                                                       field, derived, grid, params, stats);
     229              :         } else {
     230            0 :             cfd_warning("Unknown output type, skipping");
     231              :         }
     232              :     }
     233              : }
        

Generated by: LCOV version 2.0-1