LCOV - code coverage report
Current view: top level - solvers/navier_stokes/omp - solver_rk2_omp.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 88.9 % 90 80
Test Date: 2026-06-23 13:41:07 Functions: 100.0 % 2 2

            Line data    Source code
       1              : /**
       2              :  * @file solver_rk2_omp.c
       3              :  * @brief RK2 (Heun's method) time integration - OpenMP parallelized
       4              :  *
       5              :  * Same algorithm as scalar RK2 but with OpenMP-parallelized loops.
       6              :  * Achieves O(dt^2) temporal accuracy with multi-threaded execution.
       7              :  *
       8              :  * Branch-free 3D: when nz==1, stride_z=0 and inv_2dz/inv_dz2=0.0 cause all
       9              :  * z-terms to vanish, producing bit-identical results to the 2D code path.
      10              :  */
      11              : 
      12              : #include "cfd/core/cfd_status.h"
      13              : #include "cfd/core/grid.h"
      14              : #include "cfd/core/indexing.h"
      15              : #include "cfd/core/memory.h"
      16              : #include "cfd/solvers/navier_stokes_solver.h"
      17              : #include "cfd/solvers/energy_solver.h"
      18              : #include "../../energy/energy_solver_internal.h"
      19              : 
      20              : #include <math.h>
      21              : #include <stddef.h>
      22              : #include <string.h>
      23              : 
      24              : #ifdef CFD_ENABLE_OPENMP
      25              : 
      26              : #include <omp.h>
      27              : 
      28              : /* OpenMP version-gated pragmas */
      29              : #if _OPENMP >= 201307  /* OMP 4.0 */
      30              : #  define OMP_FOR_SIMD _Pragma("omp parallel for simd schedule(static)")
      31              : #else
      32              : #  define OMP_FOR_SIMD _Pragma("omp parallel for schedule(static)")
      33              : #endif
      34              : 
      35              : #ifndef M_PI
      36              : #define M_PI 3.14159265358979323846
      37              : #endif
      38              : 
      39              : /* Physical stability limits (same as scalar RK2) */
      40              : #define MAX_DERIVATIVE_LIMIT        100.0
      41              : #define MAX_SECOND_DERIVATIVE_LIMIT 1000.0
      42              : #define MAX_VELOCITY_LIMIT          100.0
      43              : #define MAX_DIVERGENCE_LIMIT        10.0
      44              : #define PRESSURE_UPDATE_FACTOR      0.1
      45              : 
      46              : /* ============================================================================
      47              :  * RHS EVALUATION (OpenMP parallelized)
      48              :  * ============================================================================ */
      49              : 
      50          464 : static void compute_rhs_omp(const double* u, const double* v, const double* w,
      51              :                              const double* p, const double* rho, const double* T,
      52              :                              double* rhs_u, double* rhs_v, double* rhs_w,
      53              :                              double* rhs_p,
      54              :                              const grid* grid, const ns_solver_params_t* params,
      55              :                              size_t nx, size_t ny, size_t nz,
      56              :                              size_t stride_z, size_t k_start, size_t k_end,
      57              :                              double inv_2dz, double inv_dz2,
      58              :                              int iter, double dt) {
      59          464 :     ptrdiff_t ny_int = (ptrdiff_t)ny;
      60          464 :     ptrdiff_t nx_int = (ptrdiff_t)nx;
      61              : 
      62          948 :     for (size_t k = k_start; k < k_end; k++) {
      63          484 :         ptrdiff_t j;
      64          484 : #pragma omp parallel for schedule(static)
      65              :         for (j = 1; j < ny_int - 1; j++) {
      66              :             for (ptrdiff_t i = 1; i < nx_int - 1; i++) {
      67              :                 size_t idx = k * stride_z + IDX_2D((size_t)i, (size_t)j, nx);
      68              : 
      69              :                 /* Safety checks */
      70              :                 if (rho[idx] <= 1e-10) {
      71              :                     rhs_u[idx] = 0.0;
      72              :                     rhs_v[idx] = 0.0;
      73              :                     rhs_w[idx] = 0.0;
      74              :                     rhs_p[idx] = 0.0;
      75              :                     continue;
      76              :                 }
      77              :                 if (fabs(grid->dx[i]) < 1e-10 || fabs(grid->dy[j]) < 1e-10) {
      78              :                     rhs_u[idx] = 0.0;
      79              :                     rhs_v[idx] = 0.0;
      80              :                     rhs_w[idx] = 0.0;
      81              :                     rhs_p[idx] = 0.0;
      82              :                     continue;
      83              :                 }
      84              : 
      85              :                 /* Periodic stencil indices in x and y — avoids relying on ghost
      86              :                  * cells, critical for preserving RK2 temporal order. */
      87              :                 size_t il = ((size_t)i > 1)      ? idx - 1  : k * stride_z + IDX_2D(nx - 2, (size_t)j, nx);
      88              :                 size_t ir = ((size_t)i < nx - 2) ? idx + 1  : k * stride_z + IDX_2D(1, (size_t)j, nx);
      89              :                 size_t jd = ((size_t)j > 1)      ? idx - nx : k * stride_z + IDX_2D((size_t)i, ny - 2, nx);
      90              :                 size_t ju = ((size_t)j < ny - 2) ? idx + nx : k * stride_z + IDX_2D((size_t)i, 1, nx);
      91              : 
      92              :                 /* Periodic stencil indices in z.
      93              :                  * When nz==1: k=0, stride_z=0, so kd=ku=idx → z-terms vanish. */
      94              :                 size_t kd = (k > 1)      ? idx - stride_z
      95              :                                          : (nz - 2) * stride_z + IDX_2D((size_t)i, (size_t)j, nx);
      96              :                 size_t ku = (k < nz - 2) ? idx + stride_z
      97              :                                          : 1 * stride_z + IDX_2D((size_t)i, (size_t)j, nx);
      98              : 
      99              :                 /* First derivatives (central differences) */
     100              :                 double du_dx = (u[ir] - u[il]) / (2.0 * grid->dx[i]);
     101              :                 double du_dy = (u[ju] - u[jd]) / (2.0 * grid->dy[j]);
     102              :                 double du_dz = (u[ku] - u[kd]) * inv_2dz;
     103              : 
     104              :                 double dv_dx = (v[ir] - v[il]) / (2.0 * grid->dx[i]);
     105              :                 double dv_dy = (v[ju] - v[jd]) / (2.0 * grid->dy[j]);
     106              :                 double dv_dz = (v[ku] - v[kd]) * inv_2dz;
     107              : 
     108              :                 double dw_dx = (w[ir] - w[il]) / (2.0 * grid->dx[i]);
     109              :                 double dw_dy = (w[ju] - w[jd]) / (2.0 * grid->dy[j]);
     110              :                 double dw_dz = (w[ku] - w[kd]) * inv_2dz;
     111              : 
     112              :                 /* Pressure gradients */
     113              :                 double dp_dx = (p[ir] - p[il]) / (2.0 * grid->dx[i]);
     114              :                 double dp_dy = (p[ju] - p[jd]) / (2.0 * grid->dy[j]);
     115              :                 double dp_dz = (p[ku] - p[kd]) * inv_2dz;
     116              : 
     117              :                 /* Second derivatives (viscous terms) */
     118              :                 double d2u_dx2 = (u[ir] - 2.0 * u[idx] + u[il]) / (grid->dx[i] * grid->dx[i]);
     119              :                 double d2u_dy2 = (u[ju] - 2.0 * u[idx] + u[jd]) / (grid->dy[j] * grid->dy[j]);
     120              :                 double d2u_dz2 = (u[ku] - 2.0 * u[idx] + u[kd]) * inv_dz2;
     121              : 
     122              :                 double d2v_dx2 = (v[ir] - 2.0 * v[idx] + v[il]) / (grid->dx[i] * grid->dx[i]);
     123              :                 double d2v_dy2 = (v[ju] - 2.0 * v[idx] + v[jd]) / (grid->dy[j] * grid->dy[j]);
     124              :                 double d2v_dz2 = (v[ku] - 2.0 * v[idx] + v[kd]) * inv_dz2;
     125              : 
     126              :                 double d2w_dx2 = (w[ir] - 2.0 * w[idx] + w[il]) / (grid->dx[i] * grid->dx[i]);
     127              :                 double d2w_dy2 = (w[ju] - 2.0 * w[idx] + w[jd]) / (grid->dy[j] * grid->dy[j]);
     128              :                 double d2w_dz2 = (w[ku] - 2.0 * w[idx] + w[kd]) * inv_dz2;
     129              : 
     130              :                 /* Kinematic viscosity */
     131              :                 double nu = params->mu / fmax(rho[idx], 1e-10);
     132              :                 nu = fmin(nu, 1.0);
     133              : 
     134              :                 /* Clamp first derivatives */
     135              :                 du_dx = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, du_dx));
     136              :                 du_dy = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, du_dy));
     137              :                 du_dz = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, du_dz));
     138              :                 dv_dx = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, dv_dx));
     139              :                 dv_dy = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, dv_dy));
     140              :                 dv_dz = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, dv_dz));
     141              :                 dw_dx = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, dw_dx));
     142              :                 dw_dy = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, dw_dy));
     143              :                 dw_dz = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, dw_dz));
     144              :                 dp_dx = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, dp_dx));
     145              :                 dp_dy = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, dp_dy));
     146              :                 dp_dz = fmax(-MAX_DERIVATIVE_LIMIT, fmin(MAX_DERIVATIVE_LIMIT, dp_dz));
     147              : 
     148              :                 /* Clamp second derivatives */
     149              :                 d2u_dx2 = fmax(-MAX_SECOND_DERIVATIVE_LIMIT, fmin(MAX_SECOND_DERIVATIVE_LIMIT, d2u_dx2));
     150              :                 d2u_dy2 = fmax(-MAX_SECOND_DERIVATIVE_LIMIT, fmin(MAX_SECOND_DERIVATIVE_LIMIT, d2u_dy2));
     151              :                 d2u_dz2 = fmax(-MAX_SECOND_DERIVATIVE_LIMIT, fmin(MAX_SECOND_DERIVATIVE_LIMIT, d2u_dz2));
     152              :                 d2v_dx2 = fmax(-MAX_SECOND_DERIVATIVE_LIMIT, fmin(MAX_SECOND_DERIVATIVE_LIMIT, d2v_dx2));
     153              :                 d2v_dy2 = fmax(-MAX_SECOND_DERIVATIVE_LIMIT, fmin(MAX_SECOND_DERIVATIVE_LIMIT, d2v_dy2));
     154              :                 d2v_dz2 = fmax(-MAX_SECOND_DERIVATIVE_LIMIT, fmin(MAX_SECOND_DERIVATIVE_LIMIT, d2v_dz2));
     155              :                 d2w_dx2 = fmax(-MAX_SECOND_DERIVATIVE_LIMIT, fmin(MAX_SECOND_DERIVATIVE_LIMIT, d2w_dx2));
     156              :                 d2w_dy2 = fmax(-MAX_SECOND_DERIVATIVE_LIMIT, fmin(MAX_SECOND_DERIVATIVE_LIMIT, d2w_dy2));
     157              :                 d2w_dz2 = fmax(-MAX_SECOND_DERIVATIVE_LIMIT, fmin(MAX_SECOND_DERIVATIVE_LIMIT, d2w_dz2));
     158              : 
     159              :                 /* Source terms */
     160              :                 double source_u = 0.0, source_v = 0.0, source_w = 0.0;
     161              :                 double z_coord = (nz > 1 && grid->z) ? grid->z[k] : 0.0;
     162              :                 compute_source_terms(grid->x[i], grid->y[j], z_coord, iter, dt,
     163              :                                      params, &source_u, &source_v, &source_w);
     164              : 
     165              :                 /* Boussinesq buoyancy source */
     166              :                 if (T) {
     167              :                     energy_compute_buoyancy(T[idx], params,
     168              :                                             &source_u, &source_v, &source_w);
     169              :                 }
     170              : 
     171              :                 /* RHS for u-momentum */
     172              :                 rhs_u[idx] = -u[idx] * du_dx - v[idx] * du_dy - w[idx] * du_dz
     173              :                              - dp_dx / rho[idx]
     174              :                              + nu * (d2u_dx2 + d2u_dy2 + d2u_dz2)
     175              :                              + source_u;
     176              : 
     177              :                 /* RHS for v-momentum */
     178              :                 rhs_v[idx] = -u[idx] * dv_dx - v[idx] * dv_dy - w[idx] * dv_dz
     179              :                              - dp_dy / rho[idx]
     180              :                              + nu * (d2v_dx2 + d2v_dy2 + d2v_dz2)
     181              :                              + source_v;
     182              : 
     183              :                 /* RHS for w-momentum */
     184              :                 rhs_w[idx] = -u[idx] * dw_dx - v[idx] * dw_dy - w[idx] * dw_dz
     185              :                              - dp_dz / rho[idx]
     186              :                              + nu * (d2w_dx2 + d2w_dy2 + d2w_dz2)
     187              :                              + source_w;
     188              : 
     189              :                 /* Simplified pressure RHS (divergence-based) */
     190              :                 double divergence = du_dx + dv_dy + dw_dz;
     191              :                 divergence = fmax(-MAX_DIVERGENCE_LIMIT,
     192              :                                   fmin(MAX_DIVERGENCE_LIMIT, divergence));
     193              :                 rhs_p[idx] = -PRESSURE_UPDATE_FACTOR * rho[idx] * divergence;
     194              :             }
     195              :         }
     196              :     }
     197          464 : }
     198              : 
     199              : /* ============================================================================
     200              :  * RK2 OMP SOLVER
     201              :  * ============================================================================ */
     202              : 
     203          213 : cfd_status_t rk2_omp_impl(flow_field* field, const grid* grid,
     204              :                             const ns_solver_params_t* params) {
     205          213 :     if (field->nx < 3 || field->ny < 3 || (field->nz > 1 && field->nz < 3)) {
     206              :         return CFD_ERROR_INVALID;
     207              :     }
     208              : 
     209          213 :     size_t nx = field->nx;
     210          213 :     size_t ny = field->ny;
     211          213 :     size_t nz = field->nz;
     212              : 
     213              :     /* Reject non-uniform z-spacing (solver uses constant inv_2dz/inv_dz2) */
     214          213 :     if (nz > 1 && grid->dz) {
     215           14 :         for (size_t k = 1; k < nz - 1; k++) {
     216           12 :             if (fabs(grid->dz[k] - grid->dz[0]) > 1e-14) {
     217              :                 return CFD_ERROR_INVALID;
     218              :             }
     219              :         }
     220              :     }
     221              : 
     222          213 :     size_t plane = nx * ny;
     223          213 :     size_t total = plane * nz;
     224          213 :     size_t bytes = total * sizeof(double);
     225              : 
     226              :     /* Branch-free 3D constants */
     227          213 :     size_t stride_z = (nz > 1) ? plane : 0;
     228          213 :     size_t k_start  = (nz > 1) ? 1 : 0;
     229          213 :     size_t k_end    = (nz > 1) ? (nz - 1) : 1;
     230          213 :     double inv_2dz  = (nz > 1 && grid->dz) ? 1.0 / (2.0 * grid->dz[0]) : 0.0;
     231            2 :     double inv_dz2  = (nz > 1 && grid->dz) ? 1.0 / (grid->dz[0] * grid->dz[0]) : 0.0;
     232              : 
     233              :     /* Allocate working arrays:
     234              :      *   k1_u/v/w/p : Stage 1 derivatives
     235              :      *   k2_u/v/w/p : Stage 2 derivatives
     236              :      *   u0/v0/w0/p0 : Saved state Q^n
     237              :      */
     238          213 :     double* k1_u = (double*)cfd_calloc(total, sizeof(double));
     239          213 :     double* k1_v = (double*)cfd_calloc(total, sizeof(double));
     240          213 :     double* k1_w = (double*)cfd_calloc(total, sizeof(double));
     241          213 :     double* k1_p = (double*)cfd_calloc(total, sizeof(double));
     242          213 :     double* k2_u = (double*)cfd_calloc(total, sizeof(double));
     243          213 :     double* k2_v = (double*)cfd_calloc(total, sizeof(double));
     244          213 :     double* k2_w = (double*)cfd_calloc(total, sizeof(double));
     245          213 :     double* k2_p = (double*)cfd_calloc(total, sizeof(double));
     246          213 :     double* u0 = (double*)cfd_calloc(total, sizeof(double));
     247          213 :     double* v0 = (double*)cfd_calloc(total, sizeof(double));
     248          213 :     double* w0 = (double*)cfd_calloc(total, sizeof(double));
     249          213 :     double* p0 = (double*)cfd_calloc(total, sizeof(double));
     250          213 :     int needs_T_ws = (params->alpha > 0.0 || params->beta != 0.0);
     251          213 :     double* T_energy_ws = needs_T_ws
     252            1 :         ? (double*)cfd_calloc(total, sizeof(double)) : NULL;
     253              : 
     254          213 :     if (!k1_u || !k1_v || !k1_w || !k1_p ||
     255          213 :         !k2_u || !k2_v || !k2_w || !k2_p ||
     256          213 :         !u0 || !v0 || !w0 || !p0 ||
     257          213 :         (needs_T_ws && !T_energy_ws)) {
     258            0 :         cfd_free(k1_u); cfd_free(k1_v); cfd_free(k1_w); cfd_free(k1_p);
     259            0 :         cfd_free(k2_u); cfd_free(k2_v); cfd_free(k2_w); cfd_free(k2_p);
     260            0 :         cfd_free(u0); cfd_free(v0); cfd_free(w0); cfd_free(p0);
     261            0 :         cfd_free(T_energy_ws);
     262            0 :         return CFD_ERROR_NOMEM;
     263              :     }
     264              : 
     265          213 :     double dt = params->dt;
     266          213 :     ptrdiff_t total_int = (ptrdiff_t)total;
     267          213 :     cfd_status_t status = CFD_SUCCESS;
     268              : 
     269          445 :     for (int iter = 0; iter < params->max_iter; iter++) {
     270              :         /* Save Q^n */
     271          232 :         memcpy(u0, field->u, bytes);
     272          232 :         memcpy(v0, field->v, bytes);
     273          232 :         memcpy(w0, field->w, bytes);
     274          232 :         memcpy(p0, field->p, bytes);
     275              : 
     276              :         /* ---- Stage 1: k1 = RHS(Q^n) ---- */
     277          232 :         memset(k1_u, 0, bytes);
     278          232 :         memset(k1_v, 0, bytes);
     279          232 :         memset(k1_w, 0, bytes);
     280          232 :         memset(k1_p, 0, bytes);
     281              : 
     282          232 :         compute_rhs_omp(field->u, field->v, field->w, field->p, field->rho, field->T,
     283              :                          k1_u, k1_v, k1_w, k1_p,
     284              :                          grid, params, nx, ny, nz,
     285              :                          stride_z, k_start, k_end, inv_2dz, inv_dz2,
     286              :                          iter, dt);
     287              : 
     288              :         /* ---- Intermediate: field = Q^n + dt * k1 ---- */
     289              :         {
     290          232 :             ptrdiff_t kk;
     291          232 :             OMP_FOR_SIMD
     292              :             for (kk = 0; kk < total_int; kk++) {
     293              :                 field->u[kk] = u0[kk] + dt * k1_u[kk];
     294              :                 field->v[kk] = v0[kk] + dt * k1_v[kk];
     295              :                 field->w[kk] = w0[kk] + dt * k1_w[kk];
     296              :                 field->p[kk] = p0[kk] + dt * k1_p[kk];
     297              : 
     298              :                 field->u[kk] = fmax(-MAX_VELOCITY_LIMIT, fmin(MAX_VELOCITY_LIMIT, field->u[kk]));
     299              :                 field->v[kk] = fmax(-MAX_VELOCITY_LIMIT, fmin(MAX_VELOCITY_LIMIT, field->v[kk]));
     300              :                 field->w[kk] = fmax(-MAX_VELOCITY_LIMIT, fmin(MAX_VELOCITY_LIMIT, field->w[kk]));
     301              :             }
     302              :         }
     303              : 
     304              :         /* NOTE: Do NOT apply BCs between RK stages. The ghost cells carry
     305              :          * zero-derivative evolution (k1[ghost]=0), which is consistent with
     306              :          * the semi-discrete ODE system. Applying BCs here would reduce RK2
     307              :          * to first-order temporal accuracy. */
     308              : 
     309              :         /* ---- Stage 2: k2 = RHS(Q_pred) ---- */
     310          232 :         memset(k2_u, 0, bytes);
     311          232 :         memset(k2_v, 0, bytes);
     312          232 :         memset(k2_w, 0, bytes);
     313          232 :         memset(k2_p, 0, bytes);
     314              : 
     315          232 :         compute_rhs_omp(field->u, field->v, field->w, field->p, field->rho, field->T,
     316              :                          k2_u, k2_v, k2_w, k2_p,
     317              :                          grid, params, nx, ny, nz,
     318              :                          stride_z, k_start, k_end, inv_2dz, inv_dz2,
     319              :                          iter, dt);
     320              : 
     321              :         /* ---- Final update: Q^{n+1} = Q^n + (dt/2)*(k1 + k2) ---- */
     322              :         {
     323          232 :             double half_dt = 0.5 * dt;
     324          232 :             ptrdiff_t kk;
     325          232 :             OMP_FOR_SIMD
     326              :             for (kk = 0; kk < total_int; kk++) {
     327              :                 field->u[kk] = u0[kk] + half_dt * (k1_u[kk] + k2_u[kk]);
     328              :                 field->v[kk] = v0[kk] + half_dt * (k1_v[kk] + k2_v[kk]);
     329              :                 field->w[kk] = w0[kk] + half_dt * (k1_w[kk] + k2_w[kk]);
     330              :                 field->p[kk] = p0[kk] + half_dt * (k1_p[kk] + k2_p[kk]);
     331              : 
     332              :                 field->u[kk] = fmax(-MAX_VELOCITY_LIMIT, fmin(MAX_VELOCITY_LIMIT, field->u[kk]));
     333              :                 field->v[kk] = fmax(-MAX_VELOCITY_LIMIT, fmin(MAX_VELOCITY_LIMIT, field->v[kk]));
     334              :                 field->w[kk] = fmax(-MAX_VELOCITY_LIMIT, fmin(MAX_VELOCITY_LIMIT, field->w[kk]));
     335              :             }
     336              :         }
     337              : 
     338              :         /* Energy equation: advance temperature after RK2 velocity update */
     339              :         {
     340          232 :             cfd_status_t energy_status = energy_step_explicit_omp_with_workspace(
     341              :                 field, grid, params, dt, iter * dt, T_energy_ws, total);
     342          232 :             if (energy_status != CFD_SUCCESS) {
     343            0 :                 status = energy_status;
     344            0 :                 goto cleanup;
     345              :             }
     346              :         }
     347              : 
     348              :         /* Apply BCs to final state only (after the full RK2 step).
     349              :          * Then apply configured thermal BCs (overwrites periodic T values). */
     350          232 :         apply_boundary_conditions(field, grid);
     351          232 :         status = energy_apply_thermal_bcs(field, params);
     352          232 :         if (status != CFD_SUCCESS) {
     353            0 :             goto cleanup;
     354              :         }
     355              : 
     356              :         /* NaN / Inf check (parallelized) */
     357              :         {
     358          232 :             int has_nan = 0;
     359          232 :             ptrdiff_t kk;
     360          232 : #pragma omp parallel for reduction(| : has_nan) schedule(static)
     361              :             for (kk = 0; kk < total_int; kk++) {
     362              :                 if (!isfinite(field->u[kk]) || !isfinite(field->v[kk]) ||
     363              :                     !isfinite(field->w[kk]) || !isfinite(field->p[kk])) {
     364              :                     has_nan = 1;
     365              :                 }
     366              :             }
     367          232 :             if (has_nan) {
     368            0 :                 status = CFD_ERROR_DIVERGED;
     369            0 :                 goto cleanup;
     370              :             }
     371              :         }
     372              :     }
     373              : 
     374          213 : cleanup:
     375          213 :     cfd_free(k1_u); cfd_free(k1_v); cfd_free(k1_w); cfd_free(k1_p);
     376          213 :     cfd_free(k2_u); cfd_free(k2_v); cfd_free(k2_w); cfd_free(k2_p);
     377          213 :     cfd_free(u0); cfd_free(v0); cfd_free(w0); cfd_free(p0);
     378          213 :     cfd_free(T_energy_ws);
     379              : 
     380          213 :     return status;
     381              : }
     382              : 
     383              : #endif /* CFD_ENABLE_OPENMP */
        

Generated by: LCOV version 2.0-1