LCOV - code coverage report
Current view: top level - core - derived_fields.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 83.5 % 79 66
Test Date: 2026-03-04 10:22:18 Functions: 100.0 % 6 6

            Line data    Source code
       1              : #include "cfd/core/derived_fields.h"
       2              : #include "cfd/core/memory.h"
       3              : #include "cfd/solvers/navier_stokes_solver.h"
       4              : 
       5              : #include <math.h>
       6              : #include <stddef.h>
       7              : 
       8              : #ifdef CFD_ENABLE_OPENMP
       9              : #include <omp.h>
      10              : #endif
      11              : 
      12              : /* OpenMP version-gated pragmas */
      13              : #if defined(_OPENMP) && (_OPENMP >= 201307)  /* OMP 4.0 */
      14              : #  define OMP_FOR_SIMD _Pragma("omp parallel for simd")
      15              : #else
      16              : #  define OMP_FOR_SIMD _Pragma("omp parallel for")
      17              : #endif
      18              : 
      19              : // Minimum grid size to benefit from parallelization
      20              : // Below this threshold, thread overhead exceeds benefit
      21              : #define OMP_THRESHOLD 1000
      22              : 
      23              : //=============================================================================
      24              : // DERIVED FIELDS LIFECYCLE
      25              : //=============================================================================
      26              : 
      27           37 : derived_fields* derived_fields_create(size_t nx, size_t ny, size_t nz) {
      28           37 :     derived_fields* derived = (derived_fields*)cfd_calloc(1, sizeof(derived_fields));
      29           37 :     if (derived) {
      30           37 :         derived->nx = nx;
      31           37 :         derived->ny = ny;
      32           37 :         derived->nz = nz;
      33           37 :         derived->velocity_magnitude = NULL;
      34              :     }
      35           37 :     return derived;
      36              : }
      37              : 
      38           38 : void derived_fields_destroy(derived_fields* derived) {
      39           38 :     if (!derived) {
      40              :         return;
      41              :     }
      42              : 
      43           37 :     derived_fields_clear(derived);
      44           37 :     cfd_free(derived);
      45              : }
      46              : 
      47           39 : void derived_fields_clear(derived_fields* derived) {
      48           39 :     if (!derived) {
      49              :         return;
      50              :     }
      51              : 
      52           38 :     if (derived->velocity_magnitude) {
      53           28 :         cfd_free(derived->velocity_magnitude);
      54           28 :         derived->velocity_magnitude = NULL;
      55              :     }
      56              : 
      57              :     // Reset statistics
      58           38 :     derived->stats_computed = 0;
      59              : }
      60              : 
      61              : //=============================================================================
      62              : // FIELD STATISTICS
      63              : //=============================================================================
      64              : 
      65          146 : field_stats calculate_field_statistics(const double* data, size_t count) {
      66          146 :     field_stats stats = {0};
      67              : 
      68          146 :     if (!data || count == 0) {
      69            0 :         return stats;
      70              :     }
      71              : 
      72              : #ifdef CFD_ENABLE_OPENMP
      73          146 :     if (count >= OMP_THRESHOLD) {
      74              :         // Parallel reduction for min, max, and sum
      75            0 :         double min_val = data[0];
      76            0 :         double max_val = data[0];
      77            0 :         double sum_val = 0.0;
      78              : 
      79              :         // Use signed type for OpenMP compatibility with MSVC
      80            0 :         long long nn = (long long)count;
      81            0 :         long long i;
      82              : #if _OPENMP >= 201107  /* OMP 3.1: min/max reductions */
      83            0 :         #pragma omp parallel for reduction(min:min_val) reduction(max:max_val) reduction(+:sum_val)
      84              :         for (i = 0; i < nn; i++) {
      85              :             double val = data[i];
      86              :             if (val < min_val) min_val = val;
      87              :             if (val > max_val) max_val = val;
      88              :             sum_val += val;
      89              :         }
      90              : #else
      91              :         /* OMP < 3.1: only + reduction available; compute min/max serially */
      92              :         #pragma omp parallel for reduction(+:sum_val)
      93              :         for (i = 0; i < nn; i++) {
      94              :             sum_val += data[i];
      95              :         }
      96              :         for (i = 0; i < nn; i++) {
      97              :             if (data[i] < min_val) min_val = data[i];
      98              :             if (data[i] > max_val) max_val = data[i];
      99              :         }
     100              : #endif
     101              : 
     102            0 :         stats.min_val = min_val;
     103            0 :         stats.max_val = max_val;
     104            0 :         stats.sum_val = sum_val;
     105              :     } else {
     106              :         // Sequential for small arrays
     107          146 :         stats.min_val = data[0];
     108          146 :         stats.max_val = data[0];
     109          146 :         stats.sum_val = 0.0;
     110              : 
     111        14338 :         for (size_t i = 0; i < count; i++) {
     112        14192 :             double val = data[i];
     113        14192 :             if (val < stats.min_val) {
     114          344 :                 stats.min_val = val;
     115              :             }
     116        14192 :             if (val > stats.max_val) {
     117          552 :                 stats.max_val = val;
     118              :             }
     119        14192 :             stats.sum_val += val;
     120              :         }
     121              :     }
     122              : #else
     123              :     // Sequential implementation when OpenMP not available
     124              :     stats.min_val = data[0];
     125              :     stats.max_val = data[0];
     126              :     stats.sum_val = 0.0;
     127              : 
     128              :     for (size_t i = 0; i < count; i++) {
     129              :         double val = data[i];
     130              :         if (val < stats.min_val)
     131              :             stats.min_val = val;
     132              :         if (val > stats.max_val)
     133              :             stats.max_val = val;
     134              :         stats.sum_val += val;
     135              :     }
     136              : #endif
     137              : 
     138          146 :     stats.avg_val = stats.sum_val / (double)count;
     139          146 :     return stats;
     140              : }
     141              : 
     142              : //=============================================================================
     143              : // DERIVED FIELD COMPUTATION
     144              : //=============================================================================
     145              : 
     146           33 : void derived_fields_compute_velocity_magnitude(derived_fields* derived, const flow_field* field) {
     147           33 :     if (!derived || !field || !field->u || !field->v) {
     148              :         return;
     149              :     }
     150              : 
     151           29 :     size_t n = derived->nx * derived->ny * derived->nz;
     152              : 
     153              :     // Allocate if needed
     154           29 :     if (!derived->velocity_magnitude) {
     155           28 :         derived->velocity_magnitude = (double*)cfd_malloc(n * sizeof(double));
     156           28 :         if (!derived->velocity_magnitude) {
     157              :             return;
     158              :         }
     159              :     }
     160              : 
     161              :     // Compute velocity magnitude (embarrassingly parallel)
     162           29 :     const double* u = field->u;
     163           29 :     const double* v = field->v;
     164           29 :     const double* w = field->w;
     165           29 :     double* vel_mag = derived->velocity_magnitude;
     166              : 
     167              : #ifdef CFD_ENABLE_OPENMP
     168           29 :     if (n >= OMP_THRESHOLD) {
     169              :         // Use signed type for OpenMP compatibility with MSVC
     170            0 :         long long nn = (long long)n;
     171            0 :         long long i;
     172            0 :         OMP_FOR_SIMD
     173              :         for (i = 0; i < nn; i++) {
     174              :             double w_i = w ? w[i] : 0.0;
     175              :             vel_mag[i] = sqrt((u[i] * u[i]) + (v[i] * v[i]) + (w_i * w_i));
     176              :         }
     177              :     } else {
     178         2777 :         for (size_t i = 0; i < n; i++) {
     179         2748 :             double w_i = w ? w[i] : 0.0;
     180         2748 :             vel_mag[i] = sqrt((u[i] * u[i]) + (v[i] * v[i]) + (w_i * w_i));
     181              :         }
     182              :     }
     183              : #else
     184              :     for (size_t i = 0; i < n; i++) {
     185              :         double w_i = w ? w[i] : 0.0;
     186              :         vel_mag[i] = sqrt(u[i] * u[i] + v[i] * v[i] + w_i * w_i);
     187              :     }
     188              : #endif
     189              : }
     190              : 
     191           21 : void derived_fields_compute_statistics(derived_fields* derived, const flow_field* field) {
     192           21 :     if (!derived || !field) {
     193              :         return;
     194              :     }
     195              : 
     196           21 :     size_t count = derived->nx * derived->ny * derived->nz;
     197              : 
     198              :     // Compute statistics for primary fields
     199           21 :     if (field->u) {
     200           21 :         derived->u_stats = calculate_field_statistics(field->u, count);
     201              :     }
     202           21 :     if (field->v) {
     203           21 :         derived->v_stats = calculate_field_statistics(field->v, count);
     204              :     }
     205           21 :     if (field->w) {
     206           21 :         derived->w_stats = calculate_field_statistics(field->w, count);
     207              :     }
     208           21 :     if (field->p) {
     209           21 :         derived->p_stats = calculate_field_statistics(field->p, count);
     210              :     }
     211           21 :     if (field->rho) {
     212           21 :         derived->rho_stats = calculate_field_statistics(field->rho, count);
     213              :     }
     214           21 :     if (field->T) {
     215           21 :         derived->T_stats = calculate_field_statistics(field->T, count);
     216              :     }
     217              : 
     218              :     // Compute velocity magnitude statistics if available
     219           21 :     if (derived->velocity_magnitude) {
     220           20 :         derived->vel_mag_stats = calculate_field_statistics(derived->velocity_magnitude, count);
     221              :     }
     222              : 
     223           21 :     derived->stats_computed = 1;
     224              : }
        

Generated by: LCOV version 2.0-1