LCOV - code coverage report
Current view: top level - solvers/linear/cpu - linear_solver_redblack.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 67 67
Test Date: 2026-06-23 13:41:07 Functions: 100.0 % 4 4

            Line data    Source code
       1              : /**
       2              :  * @file linear_solver_redblack.c
       3              :  * @brief Red-Black SOR solver - scalar CPU implementation
       4              :  *
       5              :  * Red-Black SOR characteristics:
       6              :  * - Two-color sweep (red then black) allows parallelization
       7              :  * - In-place updates (no temporary buffer needed)
       8              :  * - SOR acceleration (omega > 1) for faster convergence
       9              :  * - Best balance of convergence speed and parallelism
      10              :  */
      11              : 
      12              : #include "../linear_solver_internal.h"
      13              : 
      14              : #include "cfd/boundary/boundary_conditions.h"
      15              : #include "cfd/core/indexing.h"
      16              : #include "cfd/core/memory.h"
      17              : 
      18              : #include <math.h>
      19              : 
      20              : /* ============================================================================
      21              :  * RED-BLACK CONTEXT
      22              :  * ============================================================================ */
      23              : 
      24              : typedef struct {
      25              :     double dx2;        /* dx^2 */
      26              :     double dy2;        /* dy^2 */
      27              :     double inv_dz2;
      28              :     double inv_factor; /* 1 / (2 * (1/dx^2 + 1/dy^2)) */
      29              :     double omega;      /* SOR relaxation parameter */
      30              :     size_t stride_z;
      31              :     size_t k_start;
      32              :     size_t k_end;
      33              :     int initialized;
      34              : } redblack_context_t;
      35              : 
      36              : /* ============================================================================
      37              :  * RED-BLACK SCALAR IMPLEMENTATION
      38              :  * ============================================================================ */
      39              : 
      40           20 : static cfd_status_t redblack_scalar_init(
      41              :     poisson_solver_t* solver,
      42              :     size_t nx, size_t ny, size_t nz,
      43              :     double dx, double dy, double dz,
      44              :     const poisson_solver_params_t* params)
      45              : {
      46           20 :     redblack_context_t* ctx = (redblack_context_t*)cfd_calloc(1, sizeof(redblack_context_t));
      47           20 :     if (!ctx) {
      48              :         return CFD_ERROR_NOMEM;
      49              :     }
      50              : 
      51           20 :     ctx->dx2 = dx * dx;
      52           20 :     ctx->dy2 = dy * dy;
      53           20 :     ctx->inv_dz2 = poisson_solver_compute_inv_dz2(dz);
      54           20 :     poisson_solver_compute_3d_bounds(nz, nx, ny,
      55              :         &ctx->stride_z, &ctx->k_start, &ctx->k_end);
      56              : 
      57           20 :     double factor = 2.0 * (1.0 / ctx->dx2 + 1.0 / ctx->dy2 + ctx->inv_dz2);
      58           20 :     ctx->inv_factor = 1.0 / factor;
      59           20 :     ctx->omega = poisson_solver_resolve_omega(
      60              :         params ? params->omega : 0.0, nx, ny, nz, dx, dy, dz);
      61           20 :     ctx->initialized = 1;
      62              : 
      63           20 :     solver->context = ctx;
      64           20 :     return CFD_SUCCESS;
      65              : }
      66              : 
      67           22 : static void redblack_scalar_destroy(poisson_solver_t* solver) {
      68           22 :     if (solver && solver->context) {
      69           20 :         cfd_free(solver->context);
      70           20 :         solver->context = NULL;
      71              :     }
      72           22 : }
      73              : 
      74              : /**
      75              :  * Red-Black SOR iteration (scalar)
      76              :  *
      77              :  * Red cells: (i+j) % 2 == 0
      78              :  * Black cells: (i+j) % 2 == 1
      79              :  */
      80        56732 : static cfd_status_t redblack_scalar_iterate(
      81              :     poisson_solver_t* solver,
      82              :     double* x,
      83              :     double* x_temp,
      84              :     const double* rhs,
      85              :     double* residual)
      86              : {
      87        56732 :     (void)x_temp;  /* Not needed for in-place SOR */
      88              : 
      89        56732 :     redblack_context_t* ctx = (redblack_context_t*)solver->context;
      90        56732 :     size_t nx = solver->nx;
      91        56732 :     size_t ny = solver->ny;
      92        56732 :     double dx2 = ctx->dx2;
      93        56732 :     double dy2 = ctx->dy2;
      94        56732 :     double inv_factor = ctx->inv_factor;
      95        56732 :     double omega = ctx->omega;
      96              : 
      97        56732 :     size_t stride_z = ctx->stride_z;
      98        56732 :     double inv_dz2 = ctx->inv_dz2;
      99              : 
     100              :     /* Red sweep: (i+j+k) % 2 == 0 */
     101       117860 :     for (size_t k = ctx->k_start; k < ctx->k_end; k++) {
     102      2050512 :         for (size_t j = 1; j < ny - 1; j++) {
     103      1989384 :             size_t i_start = ((j + k) % 2 == 0) ? 1 : 2;
     104     41293624 :             for (size_t i = i_start; i < nx - 1; i += 2) {
     105     39304240 :                 size_t idx = k * stride_z + IDX_2D(i, j, nx);
     106              : 
     107     39304240 :                 double p_new = -(rhs[idx]
     108     39304240 :                     - (x[idx + 1] + x[idx - 1]) / dx2
     109     39304240 :                     - (x[idx + nx] + x[idx - nx]) / dy2
     110     39304240 :                     - (x[idx + stride_z] + x[idx - stride_z]) * inv_dz2
     111              :                     ) * inv_factor;
     112              : 
     113              :                 /* SOR update */
     114     39304240 :                 x[idx] = x[idx] + omega * (p_new - x[idx]);
     115              :             }
     116              :         }
     117              :     }
     118              : 
     119              :     /* Black sweep: (i+j+k) % 2 == 1 */
     120       117860 :     for (size_t k = ctx->k_start; k < ctx->k_end; k++) {
     121      2050512 :         for (size_t j = 1; j < ny - 1; j++) {
     122      1989384 :             size_t i_start = ((j + k) % 2 == 0) ? 2 : 1;
     123     41349728 :             for (size_t i = i_start; i < nx - 1; i += 2) {
     124     39360344 :                 size_t idx = k * stride_z + IDX_2D(i, j, nx);
     125              : 
     126     39360344 :                 double p_new = -(rhs[idx]
     127     39360344 :                     - (x[idx + 1] + x[idx - 1]) / dx2
     128     39360344 :                     - (x[idx + nx] + x[idx - nx]) / dy2
     129     39360344 :                     - (x[idx + stride_z] + x[idx - stride_z]) * inv_dz2
     130              :                     ) * inv_factor;
     131              : 
     132              :                 /* SOR update */
     133     39360344 :                 x[idx] = x[idx] + omega * (p_new - x[idx]);
     134              :             }
     135              :         }
     136              :     }
     137              : 
     138              :     /* Apply boundary conditions */
     139        56732 :     poisson_solver_apply_bc(solver, x);
     140              : 
     141              :     /* Compute residual if requested */
     142        56732 :     if (residual) {
     143        56732 :         *residual = poisson_solver_compute_residual(solver, x, rhs);
     144              :     }
     145              : 
     146        56732 :     return CFD_SUCCESS;
     147              : }
     148              : 
     149              : /* ============================================================================
     150              :  * FACTORY FUNCTION
     151              :  * ============================================================================ */
     152              : 
     153           22 : poisson_solver_t* create_redblack_scalar_solver(void) {
     154           22 :     poisson_solver_t* solver = (poisson_solver_t*)cfd_calloc(1, sizeof(poisson_solver_t));
     155           22 :     if (!solver) {
     156              :         return NULL;
     157              :     }
     158              : 
     159           22 :     solver->name = POISSON_SOLVER_TYPE_REDBLACK_SCALAR;
     160           22 :     solver->description = "Red-Black SOR iteration (scalar CPU)";
     161           22 :     solver->method = POISSON_METHOD_REDBLACK_SOR;
     162           22 :     solver->backend = POISSON_BACKEND_SCALAR;
     163           22 :     solver->params = poisson_solver_params_default();
     164              : 
     165           22 :     solver->init = redblack_scalar_init;
     166           22 :     solver->destroy = redblack_scalar_destroy;
     167           22 :     solver->solve = NULL;  /* Use common solve loop */
     168           22 :     solver->iterate = redblack_scalar_iterate;
     169           22 :     solver->apply_bc = NULL;
     170              : 
     171           22 :     return solver;
     172              : }
        

Generated by: LCOV version 2.0-1