LCOV - code coverage report
Current view: top level - io - csv_output.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 89.9 % 129 116
Test Date: 2026-03-04 10:22:18 Functions: 87.5 % 8 7

            Line data    Source code
       1              : #include "cfd/io/csv_output.h"
       2              : #include "cfd/core/derived_fields.h"
       3              : #include "cfd/core/grid.h"
       4              : #include "cfd/core/indexing.h"
       5              : #include "cfd/core/logging.h"
       6              : #include "cfd/solvers/navier_stokes_solver.h"
       7              : #include "csv_output_internal.h"
       8              : 
       9              : 
      10              : #include <math.h>
      11              : #include <stdio.h>
      12              : #include <string.h>
      13              : #include <sys/stat.h>
      14              : 
      15              : //=============================================================================
      16              : // UTILITY FUNCTIONS
      17              : //=============================================================================
      18              : 
      19              : // Helper to build filepath
      20            2 : static void build_filepath(char* filepath, size_t filepath_size, const char* run_dir,
      21              :                            const char* filename) {
      22              : #ifdef _WIN32
      23              :     snprintf(filepath, filepath_size, "%s\\%s", run_dir, filename);
      24              : #else
      25            2 :     snprintf(filepath, filepath_size, "%s/%s", run_dir, filename);
      26              : #endif
      27            2 : }
      28              : 
      29              : // Check if file exists
      30            5 : static int file_exists(const char* filename) {
      31            5 :     struct stat buffer;
      32            5 :     return (stat(filename, &buffer) == 0);
      33              : }
      34              : 
      35              : //=============================================================================
      36              : // CSV OUTPUT DISPATCH
      37              : //=============================================================================
      38              : 
      39              : // Function pointer type for CSV output handlers
      40              : typedef void (*csv_output_handler)(const char* run_dir, const char* prefix, int step,
      41              :                                    double current_time, const flow_field* field,
      42              :                                    const derived_fields* derived, const grid* grid,
      43              :                                    const ns_solver_params_t* params, const ns_solver_stats_t* stats);
      44              : 
      45              : // CSV output handler functions
      46            1 : static void write_timeseries_csv(const char* run_dir, const char* prefix, int step,
      47              :                                  double current_time, const flow_field* field,
      48              :                                  const derived_fields* derived, const grid* grid,
      49              :                                  const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
      50            1 :     const char* name = prefix ? prefix : "timeseries";
      51            1 :     char filename[256], filepath[512];
      52              : 
      53            1 :     snprintf(filename, sizeof(filename), "%s.csv", name);
      54              :     build_filepath(filepath, sizeof(filepath), run_dir, filename);
      55              : 
      56            1 :     int create_new = (step == 0);
      57            1 :     write_csv_timeseries(filepath, step, current_time, field, derived, params, stats, grid->nx,
      58            1 :                          grid->ny, create_new);
      59            1 : }
      60              : 
      61            0 : static void write_centerline_csv(const char* run_dir, const char* prefix, int step,
      62              :                                  double current_time, const flow_field* field,
      63              :                                  const derived_fields* derived, const grid* grid,
      64              :                                  const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
      65            0 :     (void)current_time;
      66            0 :     (void)params;
      67            0 :     (void)stats;  // Unused for centerline
      68            0 :     const char* name = prefix ? prefix : "centerline";
      69            0 :     char filename[256], filepath[512];
      70              : 
      71            0 :     snprintf(filename, sizeof(filename), "%s_%03d.csv", name, step);
      72            0 :     build_filepath(filepath, sizeof(filepath), run_dir, filename);
      73              : 
      74            0 :     write_csv_centerline(filepath, field, derived, grid->x, grid->y, grid->nx, grid->ny,
      75              :                          PROFILE_HORIZONTAL);
      76            0 : }
      77              : 
      78            1 : static void write_statistics_csv(const char* run_dir, const char* prefix, int step,
      79              :                                  double current_time, const flow_field* field,
      80              :                                  const derived_fields* derived, const grid* grid,
      81              :                                  const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
      82            1 :     (void)params;
      83            1 :     (void)stats;  // Unused for statistics
      84            1 :     const char* name = prefix ? prefix : "statistics";
      85            1 :     char filename[256], filepath[512];
      86              : 
      87            1 :     snprintf(filename, sizeof(filename), "%s.csv", name);
      88            1 :     build_filepath(filepath, sizeof(filepath), run_dir, filename);
      89              : 
      90            1 :     int create_new = (step == 0);
      91            1 :     write_csv_statistics(filepath, step, current_time, field, derived, grid->nx, grid->ny,
      92              :                          create_new);
      93            1 : }
      94              : 
      95              : // CSV output handler table - indexed by csv_output_type
      96              : static const csv_output_handler csv_output_table[] = {
      97              :     write_timeseries_csv,  // CSV_OUTPUT_TIMESERIES = 0
      98              :     write_centerline_csv,  // CSV_OUTPUT_CENTERLINE = 1
      99              :     write_statistics_csv   // CSV_OUTPUT_STATISTICS = 2
     100              : };
     101              : 
     102              : #define CSV_OUTPUT_TABLE_SIZE (sizeof(csv_output_table) / sizeof(csv_output_table[0]))
     103              : 
     104            2 : void csv_dispatch_output(csv_output_type csv_type, const char* run_dir, const char* prefix,
     105              :                          int step, double current_time, const flow_field* field,
     106              :                          const derived_fields* derived, const grid* grid,
     107              :                          const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
     108              :     // Bounds check and dispatch via function table
     109            2 :     if (csv_type < CSV_OUTPUT_TABLE_SIZE) {
     110            2 :         csv_output_table[csv_type](run_dir, prefix, step, current_time, field, derived, grid,
     111              :                                    params, stats);
     112              :     } else {
     113            0 :         cfd_warning("Unknown CSV output type");
     114              :     }
     115            2 : }
     116              : 
     117              : //=============================================================================
     118              : // CSV TIMESERIES OUTPUT
     119              : //=============================================================================
     120              : 
     121           12 : void write_csv_timeseries(const char* filename, int step, double time, const flow_field* field,
     122              :                           const derived_fields* derived, const ns_solver_params_t* params,
     123              :                           const ns_solver_stats_t* stats, size_t nx, size_t ny, int create_new) {
     124           12 :     (void)field;  // Statistics come from derived
     125           12 :     (void)nx;
     126           12 :     (void)ny;
     127              : 
     128           12 :     if (!filename || !derived || !derived->stats_computed || !params || !stats) {
     129              :         return;
     130              :     }
     131              : 
     132            9 :     int write_header = create_new || !file_exists(filename);
     133            7 :     const char* mode = write_header ? "w" : "a";
     134            7 :     int has_vel_mag = derived->velocity_magnitude != NULL;
     135              : 
     136            7 :     FILE* fp = fopen(filename, mode);
     137            7 :     if (!fp) {
     138            1 :         cfd_warning("Failed to open CSV timeseries file for writing");
     139            1 :         return;
     140              :     }
     141              : 
     142              :     // Write header if new file
     143            6 :     if (write_header) {
     144            4 :         fprintf(fp, "step,time,dt,max_u,max_v,max_w,max_p,avg_u,avg_v,avg_w,avg_p");
     145            4 :         if (has_vel_mag) {
     146            4 :             fprintf(fp, ",max_vel_mag,avg_vel_mag");
     147              :         }
     148            4 :         fprintf(fp, ",iterations,residual,elapsed_ms\n");
     149              :     }
     150              : 
     151              :     // Write data row using pre-computed statistics
     152           12 :     fprintf(fp, "%d,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e", step, time, params->dt,
     153            6 :             derived->u_stats.max_val, derived->v_stats.max_val, derived->w_stats.max_val,
     154            6 :             derived->p_stats.max_val, derived->u_stats.avg_val, derived->v_stats.avg_val,
     155            6 :             derived->w_stats.avg_val, derived->p_stats.avg_val);
     156              : 
     157            6 :     if (has_vel_mag) {
     158            6 :         fprintf(fp, ",%.6e,%.6e", derived->vel_mag_stats.max_val, derived->vel_mag_stats.avg_val);
     159              :     }
     160              : 
     161            6 :     fprintf(fp, ",%d,%.6e,%.2f\n", stats->iterations, stats->residual, stats->elapsed_time_ms);
     162              : 
     163            6 :     fclose(fp);
     164              : }
     165              : 
     166              : //=============================================================================
     167              : // CSV CENTERLINE PROFILE OUTPUT
     168              : //=============================================================================
     169              : 
     170            6 : void write_csv_centerline(const char* filename, const flow_field* field,
     171              :                           const derived_fields* derived, const double* x_coords,
     172              :                           const double* y_coords, size_t nx, size_t ny,
     173              :                           profile_direction direction) {
     174            6 :     if (!filename || !field || !x_coords || !y_coords) {
     175              :         return;
     176              :     }
     177              : 
     178            2 :     int has_vel_mag = derived && derived->velocity_magnitude;
     179              : 
     180            2 :     FILE* fp = fopen(filename, "w");
     181            2 :     if (!fp) {
     182            0 :         cfd_warning("Failed to open CSV centerline file for writing");
     183            0 :         return;
     184              :     }
     185              : 
     186            2 :     if (direction == PROFILE_HORIZONTAL) {
     187              :         // Horizontal centerline: along x at y = ny/2
     188            1 :         size_t j_mid = ny / 2;
     189              : 
     190            1 :         fprintf(fp, "x,u,v,w,p,rho,T");
     191            1 :         if (has_vel_mag) {
     192            1 :             fprintf(fp, ",vel_mag");
     193              :         }
     194            1 :         fprintf(fp, "\n");
     195              : 
     196           11 :         for (size_t i = 0; i < nx; i++) {
     197           10 :             size_t idx = IDX_2D(i, j_mid, nx);
     198           10 :             double w_val = field->w ? field->w[idx] : 0.0;
     199           20 :             fprintf(fp, "%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e", x_coords[i], field->u[idx],
     200           10 :                     field->v[idx], w_val, field->p[idx], field->rho[idx], field->T[idx]);
     201           10 :             if (has_vel_mag) {
     202           10 :                 fprintf(fp, ",%.6e", derived->velocity_magnitude[idx]);
     203              :             }
     204           10 :             fprintf(fp, "\n");
     205              :         }
     206              :     } else {
     207              :         // Vertical centerline: along y at x = nx/2
     208            1 :         size_t i_mid = nx / 2;
     209              : 
     210            1 :         fprintf(fp, "y,u,v,w,p,rho,T");
     211            1 :         if (has_vel_mag) {
     212            1 :             fprintf(fp, ",vel_mag");
     213              :         }
     214            1 :         fprintf(fp, "\n");
     215              : 
     216           11 :         for (size_t j = 0; j < ny; j++) {
     217           10 :             size_t idx = IDX_2D(i_mid, j, nx);
     218           10 :             double w_val = field->w ? field->w[idx] : 0.0;
     219           20 :             fprintf(fp, "%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e", y_coords[j], field->u[idx],
     220           10 :                     field->v[idx], w_val, field->p[idx], field->rho[idx], field->T[idx]);
     221           10 :             if (has_vel_mag) {
     222           10 :                 fprintf(fp, ",%.6e", derived->velocity_magnitude[idx]);
     223              :             }
     224           10 :             fprintf(fp, "\n");
     225              :         }
     226              :     }
     227              : 
     228            2 :     fclose(fp);
     229              : }
     230              : 
     231              : //=============================================================================
     232              : // CSV STATISTICS OUTPUT
     233              : //=============================================================================
     234              : 
     235           10 : void write_csv_statistics(const char* filename, int step, double time, const flow_field* field,
     236              :                           const derived_fields* derived, size_t nx, size_t ny, int create_new) {
     237           10 :     (void)field;  // Statistics come from derived
     238           10 :     (void)nx;
     239           10 :     (void)ny;
     240              : 
     241           10 :     if (!filename || !derived || !derived->stats_computed) {
     242              :         return;
     243              :     }
     244              : 
     245           10 :     int write_header = create_new || !file_exists(filename);
     246            7 :     const char* mode = write_header ? "w" : "a";
     247            7 :     int has_vel_mag = derived->velocity_magnitude != NULL;
     248              : 
     249            7 :     FILE* fp = fopen(filename, mode);
     250            7 :     if (!fp) {
     251            1 :         cfd_warning("Failed to open CSV statistics file for writing");
     252            1 :         return;
     253              :     }
     254              : 
     255              :     // Write header if new file
     256            6 :     if (write_header) {
     257            3 :         fprintf(fp, "step,time,min_u,max_u,avg_u,min_v,max_v,avg_v,min_w,max_w,avg_w,"
     258              :                     "min_p,max_p,avg_p,min_rho,max_rho,avg_rho,min_T,max_T,avg_T");
     259            3 :         if (has_vel_mag) {
     260            3 :             fprintf(fp, ",min_vel_mag,max_vel_mag,avg_vel_mag");
     261              :         }
     262            3 :         fprintf(fp, "\n");
     263              :     }
     264              : 
     265              :     // Write data row using pre-computed statistics
     266           12 :     fprintf(fp,
     267              :             "%d,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,"
     268              :             "%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e",
     269            6 :             step, time, derived->u_stats.min_val, derived->u_stats.max_val,
     270            6 :             derived->u_stats.avg_val, derived->v_stats.min_val, derived->v_stats.max_val,
     271            6 :             derived->v_stats.avg_val, derived->w_stats.min_val, derived->w_stats.max_val,
     272            6 :             derived->w_stats.avg_val, derived->p_stats.min_val, derived->p_stats.max_val,
     273            6 :             derived->p_stats.avg_val, derived->rho_stats.min_val, derived->rho_stats.max_val,
     274            6 :             derived->rho_stats.avg_val, derived->T_stats.min_val, derived->T_stats.max_val,
     275            6 :             derived->T_stats.avg_val);
     276              : 
     277            6 :     if (has_vel_mag) {
     278            6 :         fprintf(fp, ",%.6e,%.6e,%.6e", derived->vel_mag_stats.min_val,
     279            6 :                 derived->vel_mag_stats.max_val, derived->vel_mag_stats.avg_val);
     280              :     }
     281              : 
     282            6 :     fprintf(fp, "\n");
     283              : 
     284            6 :     fclose(fp);
     285              : }
        

Generated by: LCOV version 2.0-1