LCOV - code coverage report
Current view: top level - boundary - boundary_conditions.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 44.5 % 654 291
Test Date: 2026-03-04 10:22:18 Functions: 48.2 % 110 53

            Line data    Source code
       1              : /**
       2              :  * Boundary Conditions - Public API and Dispatcher
       3              :  *
       4              :  * Technology-agnostic dispatcher for boundary conditions.
       5              :  * This file contains ONLY:
       6              :  * - Public API functions
       7              :  * - Backend selection and dispatch logic
       8              :  *
       9              :  * The dispatcher uses function pointer tables provided by each backend.
      10              :  * It has NO knowledge of SIMD, OpenMP, or other technologies.
      11              :  *
      12              :  * Backend implementations are in separate folders:
      13              :  * - cpu/boundary_conditions_scalar.c (baseline, single-threaded)
      14              :  * - omp/boundary_conditions_omp.c (OpenMP, multi-threaded scalar loops)
      15              :  * - simd/boundary_conditions_simd.c (SIMD: AVX2 on x86, NEON on ARM)
      16              :  */
      17              : 
      18              : #include "boundary_conditions_internal.h"
      19              : #include "cfd/core/cfd_status.h"
      20              : #include "cfd/core/cpu_features.h"
      21              : #include "cfd/core/logging.h"
      22              : #include <stdbool.h>
      23              : #include <stddef.h>
      24              : #include <stdio.h>
      25              : 
      26              : /* ============================================================================
      27              :  * Error Handler State and Implementation
      28              :  * ============================================================================ */
      29              : 
      30              : static bc_error_handler_t g_error_handler = NULL;
      31              : static void* g_error_handler_user_data = NULL;
      32              : 
      33              : /**
      34              :  * Default error handler - prints to stderr.
      35              :  */
      36            0 : static void default_error_handler(bc_error_code_t error_code,
      37              :                                    const char* function,
      38              :                                    const char* message,
      39              :                                    void* user_data) {
      40            0 :     (void)user_data;  /* Unused in default handler */
      41            0 :     fprintf(stderr, "BC ERROR [%d] in %s: %s\n", (int)error_code, function, message);
      42            0 : }
      43              : 
      44            0 : void bc_set_error_handler(bc_error_handler_t handler, void* user_data) {
      45            0 :     g_error_handler = handler;
      46            0 :     g_error_handler_user_data = user_data;
      47            0 : }
      48              : 
      49            0 : bc_error_handler_t bc_get_error_handler(void) {
      50            0 :     return g_error_handler;
      51              : }
      52              : 
      53            0 : void bc_report_error(bc_error_code_t error_code, const char* function, const char* message) {
      54            0 :     if (g_error_handler != NULL) {
      55            0 :         g_error_handler(error_code, function, message, g_error_handler_user_data);
      56              :     } else {
      57            0 :         default_error_handler(error_code, function, message, NULL);
      58              :     }
      59            0 : }
      60              : 
      61              : /* ============================================================================
      62              :  * Backend State and Selection
      63              :  * ============================================================================ */
      64              : 
      65              : static bc_backend_t g_current_backend = BC_BACKEND_AUTO;
      66              : 
      67              : /**
      68              :  * Get the implementation table for a given backend.
      69              :  * Returns NULL if the backend is not available.
      70              :  */
      71       234484 : static const bc_backend_impl_t* get_backend_impl(bc_backend_t backend) {
      72       234458 :     switch (backend) {
      73              :         case BC_BACKEND_SCALAR:
      74              :             return &bc_impl_scalar;
      75           21 :         case BC_BACKEND_OMP:
      76            7 :             return (bc_impl_omp.apply_neumann != NULL) ? &bc_impl_omp : NULL;
      77           15 :         case BC_BACKEND_SIMD:
      78              :             /* Use runtime check - bc_impl_simd has non-NULL dispatchers,
      79              :              * but the underlying SIMD backend may not be available */
      80           16 :             return bc_simd_backend_available() ? &bc_impl_simd : NULL;
      81              :         case BC_BACKEND_CUDA:
      82              :             /* CUDA not yet implemented for boundary conditions */
      83              :             return NULL;
      84       234444 :         case BC_BACKEND_AUTO:
      85              :         default:
      86              :             /* Auto: priority SIMD > OMP > Scalar (with runtime detection) */
      87       234444 :             if (bc_simd_backend_available()) {
      88              :                 return &bc_impl_simd;
      89              :             }
      90       234444 :             if (bc_impl_omp.apply_neumann != NULL) {
      91              :                 return &bc_impl_omp;
      92              :             }
      93              :             return &bc_impl_scalar;
      94              :     }
      95              : }
      96              : 
      97          187 : bool bc_backend_available(bc_backend_t backend) {
      98          187 :     return get_backend_impl(backend) != NULL;
      99              : }
     100              : 
     101            0 : bc_backend_t bc_get_backend(void) {
     102            0 :     return g_current_backend;
     103              : }
     104              : 
     105            0 : const char* bc_get_backend_name(void) {
     106            0 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
     107              : 
     108            0 :     if (impl == &bc_impl_simd) {
     109              :         /* Report which SIMD variant is active based on runtime detection.
     110              :          * Use enum comparison for robustness instead of string comparison. */
     111            0 :         cfd_simd_arch_t arch = cfd_detect_simd_arch();
     112            0 :         if (g_current_backend == BC_BACKEND_AUTO) {
     113              :             /* Auto mode selected SIMD */
     114            0 :             switch (arch) {
     115              :                 case CFD_SIMD_AVX2:
     116              :                     return "auto (simd/avx2)";
     117            0 :                 case CFD_SIMD_NEON:
     118            0 :                     return "auto (simd/neon)";
     119            0 :                 default:
     120              :                     /* Should not happen: impl == bc_impl_simd implies SIMD available */
     121            0 :                     return "auto (simd)";
     122              :             }
     123              :         } else {
     124              :             /* Explicit SIMD selection */
     125            0 :             switch (arch) {
     126              :                 case CFD_SIMD_AVX2:
     127              :                     return "simd (avx2)";
     128            0 :                 case CFD_SIMD_NEON:
     129            0 :                     return "simd (neon)";
     130            0 :                 default:
     131              :                     /* Should not happen: impl == bc_impl_simd implies SIMD available */
     132            0 :                     return "simd";
     133              :             }
     134              :         }
     135              :     }
     136            0 :     if (impl == &bc_impl_omp) {
     137            0 :         return (g_current_backend == BC_BACKEND_AUTO) ? "auto (omp)" : "omp";
     138              :     }
     139            0 :     return (g_current_backend == BC_BACKEND_AUTO) ? "auto (scalar)" : "scalar";
     140              : }
     141              : 
     142          173 : bool bc_set_backend(bc_backend_t backend) {
     143          173 :     if (!bc_backend_available(backend) && backend != BC_BACKEND_AUTO) {
     144            0 :         g_current_backend = backend;
     145            0 :         return false;
     146              :     }
     147          173 :     g_current_backend = backend;
     148          173 :     return true;
     149              : }
     150              : 
     151              : /* ============================================================================
     152              :  * Internal Dispatch Helpers
     153              :  *
     154              :  * All helpers accept 3D parameters (nz, stride_z).
     155              :  * 2D public API functions call these with nz=1, stride_z=0.
     156              :  * ============================================================================ */
     157              : 
     158              : /** Validate 3D layout parameters.
     159              :  *  nz must be 1 (2D) or >= 3 (true 3D); for nz > 1, stride_z must be >= nx*ny
     160              :  *  to prevent aliasing. nz==2 is rejected as degenerate (consistent with NS solvers). */
     161       230363 : static inline bool validate_3d_layout(size_t nx, size_t ny,
     162              :                                        size_t nz, size_t stride_z) {
     163       230336 :     if (nz == 0 || nz == 2) return false;
     164       230363 :     if (nz > 1 && stride_z < nx * ny) return false;
     165              :     return true;
     166              : }
     167              : 
     168       202167 : static cfd_status_t apply_neumann_with_backend(double* field, size_t nx, size_t ny,
     169              :                                                 size_t nz, size_t stride_z,
     170              :                                                 const bc_backend_impl_t* impl) {
     171       202167 :     if (impl == NULL || impl->apply_neumann == NULL) {
     172              :         return CFD_ERROR_UNSUPPORTED;
     173              :     }
     174       202167 :     impl->apply_neumann(field, nx, ny, nz, stride_z);
     175       202167 :     return CFD_SUCCESS;
     176              : }
     177              : 
     178        28169 : static cfd_status_t apply_periodic_with_backend(double* field, size_t nx, size_t ny,
     179              :                                                  size_t nz, size_t stride_z,
     180              :                                                  const bc_backend_impl_t* impl) {
     181        28169 :     if (impl == NULL || impl->apply_periodic == NULL) {
     182              :         return CFD_ERROR_UNSUPPORTED;
     183              :     }
     184        28169 :     impl->apply_periodic(field, nx, ny, nz, stride_z);
     185        28169 :     return CFD_SUCCESS;
     186              : }
     187              : 
     188         7856 : static cfd_status_t apply_dirichlet_with_backend(double* field, size_t nx, size_t ny,
     189              :                                                   size_t nz, size_t stride_z,
     190              :                                                   const bc_dirichlet_values_t* values,
     191              :                                                   const bc_backend_impl_t* impl) {
     192         3938 :     if (impl == NULL || impl->apply_dirichlet == NULL) {
     193              :         return CFD_ERROR_UNSUPPORTED;
     194              :     }
     195           24 :     if (!validate_3d_layout(nx, ny, nz, stride_z)) {
     196              :         return CFD_ERROR_INVALID;
     197              :     }
     198         3911 :     impl->apply_dirichlet(field, nx, ny, nz, stride_z, values);
     199         3945 :     return CFD_SUCCESS;
     200              : }
     201              : 
     202       230336 : static cfd_status_t apply_scalar_field_bc(double* field, size_t nx, size_t ny,
     203              :                                            size_t nz, size_t stride_z,
     204              :                                            bc_type_t type, const bc_backend_impl_t* impl) {
     205       230336 :     if (!field || nx < 3 || ny < 3 || !validate_3d_layout(nx, ny, nz, stride_z)) {
     206              :         return CFD_ERROR_INVALID;
     207              :     }
     208              : 
     209       230336 :     switch (type) {
     210              :         case BC_TYPE_NEUMANN:
     211       202167 :             return apply_neumann_with_backend(field, nx, ny, nz, stride_z, impl);
     212              : 
     213              :         case BC_TYPE_PERIODIC:
     214        28169 :             return apply_periodic_with_backend(field, nx, ny, nz, stride_z, impl);
     215              : 
     216            0 :         case BC_TYPE_DIRICHLET:
     217            0 :             cfd_warning("BC_TYPE_DIRICHLET requires bc_apply_dirichlet_*() functions with values");
     218            0 :             return CFD_ERROR_INVALID;
     219              : 
     220            0 :         case BC_TYPE_NOSLIP:
     221              :             /* No-slip is Dirichlet with all zeros - use dedicated bc_apply_noslip() */
     222            0 :             cfd_warning("BC_TYPE_NOSLIP requires bc_apply_noslip() for velocity fields");
     223            0 :             return CFD_ERROR_INVALID;
     224              : 
     225            0 :         case BC_TYPE_INLET:
     226            0 :             cfd_warning("BC_TYPE_INLET not implemented");
     227            0 :             return CFD_ERROR_UNSUPPORTED;
     228              : 
     229            0 :         case BC_TYPE_OUTLET:
     230            0 :             cfd_warning("BC_TYPE_OUTLET not implemented");
     231            0 :             return CFD_ERROR_UNSUPPORTED;
     232              : 
     233            0 :         default:
     234            0 :             cfd_warning("Unknown BC type requested");
     235            0 :             return CFD_ERROR_INVALID;
     236              :     }
     237              : }
     238              : 
     239              : /* ============================================================================
     240              :  * Public API - Global Backend (2D)
     241              :  * ============================================================================ */
     242              : 
     243       228313 : cfd_status_t bc_apply_scalar(double* field, size_t nx, size_t ny, bc_type_t type) {
     244       228313 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
     245       228313 :     return apply_scalar_field_bc(field, nx, ny, 1, 0, type, impl);
     246              : }
     247              : 
     248            0 : cfd_status_t bc_apply_velocity(double* u, double* v, size_t nx, size_t ny, bc_type_t type) {
     249            0 :     if (!u || !v || nx < 3 || ny < 3) {
     250              :         return CFD_ERROR_INVALID;
     251              :     }
     252            0 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
     253            0 :     cfd_status_t status = apply_scalar_field_bc(u, nx, ny, 1, 0, type, impl);
     254            0 :     if (status != CFD_SUCCESS) {
     255              :         return status;
     256              :     }
     257            0 :     return apply_scalar_field_bc(v, nx, ny, 1, 0, type, impl);
     258              : }
     259              : 
     260              : /* ============================================================================
     261              :  * Public API - Explicit Backend Selection (2D)
     262              :  * ============================================================================ */
     263              : 
     264            0 : cfd_status_t bc_apply_scalar_cpu(double* field, size_t nx, size_t ny, bc_type_t type) {
     265            0 :     return apply_scalar_field_bc(field, nx, ny, 1, 0, type, &bc_impl_scalar);
     266              : }
     267              : 
     268            0 : cfd_status_t bc_apply_scalar_simd(double* field, size_t nx, size_t ny, bc_type_t type) {
     269            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
     270            0 :     if (impl == NULL) {
     271              :         return CFD_ERROR_UNSUPPORTED;
     272              :     }
     273            0 :     return apply_scalar_field_bc(field, nx, ny, 1, 0, type, impl);
     274              : }
     275              : 
     276            0 : cfd_status_t bc_apply_scalar_omp(double* field, size_t nx, size_t ny, bc_type_t type) {
     277            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
     278            0 :     if (impl == NULL) {
     279              :         return CFD_ERROR_UNSUPPORTED;
     280              :     }
     281            0 :     return apply_scalar_field_bc(field, nx, ny, 1, 0, type, impl);
     282              : }
     283              : 
     284            0 : cfd_status_t bc_apply_velocity_cpu(double* u, double* v, size_t nx, size_t ny, bc_type_t type) {
     285            0 :     if (!u || !v || nx < 3 || ny < 3) {
     286              :         return CFD_ERROR_INVALID;
     287              :     }
     288            0 :     cfd_status_t status = apply_scalar_field_bc(u, nx, ny, 1, 0, type, &bc_impl_scalar);
     289            0 :     if (status != CFD_SUCCESS) {
     290              :         return status;
     291              :     }
     292            0 :     return apply_scalar_field_bc(v, nx, ny, 1, 0, type, &bc_impl_scalar);
     293              : }
     294              : 
     295            0 : cfd_status_t bc_apply_velocity_simd(double* u, double* v, size_t nx, size_t ny, bc_type_t type) {
     296            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
     297            0 :     if (impl == NULL) {
     298              :         return CFD_ERROR_UNSUPPORTED;
     299              :     }
     300            0 :     if (!u || !v || nx < 3 || ny < 3) {
     301              :         return CFD_ERROR_INVALID;
     302              :     }
     303            0 :     cfd_status_t status = apply_scalar_field_bc(u, nx, ny, 1, 0, type, impl);
     304            0 :     if (status != CFD_SUCCESS) {
     305              :         return status;
     306              :     }
     307            0 :     return apply_scalar_field_bc(v, nx, ny, 1, 0, type, impl);
     308              : }
     309              : 
     310            0 : cfd_status_t bc_apply_velocity_omp(double* u, double* v, size_t nx, size_t ny, bc_type_t type) {
     311            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
     312            0 :     if (impl == NULL) {
     313              :         return CFD_ERROR_UNSUPPORTED;
     314              :     }
     315            0 :     if (!u || !v || nx < 3 || ny < 3) {
     316              :         return CFD_ERROR_INVALID;
     317              :     }
     318            0 :     cfd_status_t status = apply_scalar_field_bc(u, nx, ny, 1, 0, type, impl);
     319            0 :     if (status != CFD_SUCCESS) {
     320              :         return status;
     321              :     }
     322            0 :     return apply_scalar_field_bc(v, nx, ny, 1, 0, type, impl);
     323              : }
     324              : 
     325              : /* ============================================================================
     326              :  * Public API - Dirichlet Boundary Conditions (2D)
     327              :  * ============================================================================ */
     328              : 
     329            6 : cfd_status_t bc_apply_dirichlet_scalar(double* field, size_t nx, size_t ny,
     330              :                                         const bc_dirichlet_values_t* values) {
     331            6 :     if (!field || !values || nx < 3 || ny < 3) {
     332              :         return CFD_ERROR_INVALID;
     333              :     }
     334            3 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
     335            3 :     return apply_dirichlet_with_backend(field, nx, ny, 1, 0, values, impl);
     336              : }
     337              : 
     338         3911 : cfd_status_t bc_apply_dirichlet_velocity(double* u, double* v, size_t nx, size_t ny,
     339              :                                           const bc_dirichlet_values_t* u_values,
     340              :                                           const bc_dirichlet_values_t* v_values) {
     341         3911 :     if (!u || !v || !u_values || !v_values || nx < 3 || ny < 3) {
     342              :         return CFD_ERROR_INVALID;
     343              :     }
     344         3911 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
     345         3911 :     cfd_status_t status = apply_dirichlet_with_backend(u, nx, ny, 1, 0, u_values, impl);
     346         3911 :     if (status != CFD_SUCCESS) {
     347              :         return status;
     348              :     }
     349         3911 :     return apply_dirichlet_with_backend(v, nx, ny, 1, 0, v_values, impl);
     350              : }
     351              : 
     352              : /* Backend-specific Dirichlet implementations (2D) */
     353              : 
     354            7 : cfd_status_t bc_apply_dirichlet_scalar_cpu(double* field, size_t nx, size_t ny,
     355              :                                             const bc_dirichlet_values_t* values) {
     356            7 :     if (!field || !values || nx < 3 || ny < 3) {
     357              :         return CFD_ERROR_INVALID;
     358              :     }
     359            5 :     return apply_dirichlet_with_backend(field, nx, ny, 1, 0, values, &bc_impl_scalar);
     360              : }
     361              : 
     362            1 : cfd_status_t bc_apply_dirichlet_scalar_simd(double* field, size_t nx, size_t ny,
     363              :                                                  const bc_dirichlet_values_t* values) {
     364            1 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
     365            0 :     if (impl == NULL) {
     366              :         return CFD_ERROR_UNSUPPORTED;
     367              :     }
     368            0 :     if (!field || !values || nx < 3 || ny < 3) {
     369              :         return CFD_ERROR_INVALID;
     370              :     }
     371            0 :     return apply_dirichlet_with_backend(field, nx, ny, 1, 0, values, impl);
     372              : }
     373              : 
     374            3 : cfd_status_t bc_apply_dirichlet_scalar_omp(double* field, size_t nx, size_t ny,
     375              :                                             const bc_dirichlet_values_t* values) {
     376            3 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
     377            3 :     if (impl == NULL) {
     378              :         return CFD_ERROR_UNSUPPORTED;
     379              :     }
     380            3 :     if (!field || !values || nx < 3 || ny < 3) {
     381              :         return CFD_ERROR_INVALID;
     382              :     }
     383            2 :     return apply_dirichlet_with_backend(field, nx, ny, 1, 0, values, impl);
     384              : }
     385              : 
     386            0 : cfd_status_t bc_apply_dirichlet_velocity_cpu(double* u, double* v, size_t nx, size_t ny,
     387              :                                               const bc_dirichlet_values_t* u_values,
     388              :                                               const bc_dirichlet_values_t* v_values) {
     389            0 :     if (!u || !v || !u_values || !v_values || nx < 3 || ny < 3) {
     390              :         return CFD_ERROR_INVALID;
     391              :     }
     392            0 :     cfd_status_t status = apply_dirichlet_with_backend(u, nx, ny, 1, 0, u_values, &bc_impl_scalar);
     393            0 :     if (status != CFD_SUCCESS) {
     394              :         return status;
     395              :     }
     396            0 :     return apply_dirichlet_with_backend(v, nx, ny, 1, 0, v_values, &bc_impl_scalar);
     397              : }
     398              : 
     399            0 : cfd_status_t bc_apply_dirichlet_velocity_simd(double* u, double* v, size_t nx, size_t ny,
     400              :                                                    const bc_dirichlet_values_t* u_values,
     401              :                                                    const bc_dirichlet_values_t* v_values) {
     402            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
     403            0 :     if (impl == NULL) {
     404              :         return CFD_ERROR_UNSUPPORTED;
     405              :     }
     406            0 :     if (!u || !v || !u_values || !v_values || nx < 3 || ny < 3) {
     407              :         return CFD_ERROR_INVALID;
     408              :     }
     409            0 :     cfd_status_t status = apply_dirichlet_with_backend(u, nx, ny, 1, 0, u_values, impl);
     410            0 :     if (status != CFD_SUCCESS) {
     411              :         return status;
     412              :     }
     413            0 :     return apply_dirichlet_with_backend(v, nx, ny, 1, 0, v_values, impl);
     414              : }
     415              : 
     416            0 : cfd_status_t bc_apply_dirichlet_velocity_omp(double* u, double* v, size_t nx, size_t ny,
     417              :                                               const bc_dirichlet_values_t* u_values,
     418              :                                               const bc_dirichlet_values_t* v_values) {
     419            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
     420            0 :     if (impl == NULL) {
     421              :         return CFD_ERROR_UNSUPPORTED;
     422              :     }
     423            0 :     if (!u || !v || !u_values || !v_values || nx < 3 || ny < 3) {
     424              :         return CFD_ERROR_INVALID;
     425              :     }
     426            0 :     cfd_status_t status = apply_dirichlet_with_backend(u, nx, ny, 1, 0, u_values, impl);
     427            0 :     if (status != CFD_SUCCESS) {
     428              :         return status;
     429              :     }
     430            0 :     return apply_dirichlet_with_backend(v, nx, ny, 1, 0, v_values, impl);
     431              : }
     432              : 
     433              : /* ============================================================================
     434              :  * Public API - No-Slip Wall Boundary Conditions (2D)
     435              :  *
     436              :  * No-slip conditions set velocity to zero at all boundaries.
     437              :  * Implemented using Dirichlet BCs with zero values for efficiency.
     438              :  * ============================================================================ */
     439              : 
     440              : /** Static zero-valued Dirichlet BC for no-slip walls */
     441              : static const bc_dirichlet_values_t g_noslip_zero = {
     442              :     .left = 0.0,
     443              :     .right = 0.0,
     444              :     .top = 0.0,
     445              :     .bottom = 0.0,
     446              :     .front = 0.0,
     447              :     .back = 0.0
     448              : };
     449              : 
     450              : /** Helper: apply no-slip to velocity components using specified backend */
     451           11 : static cfd_status_t apply_noslip_with_backend(double* u, double* v, double* w,
     452              :                                                size_t nx, size_t ny,
     453              :                                                size_t nz, size_t stride_z,
     454              :                                                const bc_backend_impl_t* impl) {
     455           11 :     cfd_status_t status = apply_dirichlet_with_backend(u, nx, ny, nz, stride_z, &g_noslip_zero, impl);
     456           11 :     if (status != CFD_SUCCESS) {
     457              :         return status;
     458              :     }
     459           11 :     status = apply_dirichlet_with_backend(v, nx, ny, nz, stride_z, &g_noslip_zero, impl);
     460           11 :     if (status != CFD_SUCCESS) {
     461              :         return status;
     462              :     }
     463           11 :     if (w && nz > 1) {
     464            1 :         status = apply_dirichlet_with_backend(w, nx, ny, nz, stride_z, &g_noslip_zero, impl);
     465              :     }
     466              :     return status;
     467              : }
     468              : 
     469            8 : cfd_status_t bc_apply_noslip(double* u, double* v, size_t nx, size_t ny) {
     470            8 :     if (!u || !v || nx < 3 || ny < 3) {
     471              :         return CFD_ERROR_INVALID;
     472              :     }
     473            4 :     return apply_noslip_with_backend(u, v, NULL, nx, ny, 1, 0, get_backend_impl(g_current_backend));
     474              : }
     475              : 
     476            4 : cfd_status_t bc_apply_noslip_cpu(double* u, double* v, size_t nx, size_t ny) {
     477            4 :     if (!u || !v || nx < 3 || ny < 3) {
     478              :         return CFD_ERROR_INVALID;
     479              :     }
     480            4 :     return apply_noslip_with_backend(u, v, NULL, nx, ny, 1, 0, &bc_impl_scalar);
     481              : }
     482              : 
     483            0 : cfd_status_t bc_apply_noslip_simd(double* u, double* v, size_t nx, size_t ny) {
     484            0 :     if (!u || !v || nx < 3 || ny < 3) {
     485              :         return CFD_ERROR_INVALID;
     486              :     }
     487            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
     488            0 :     if (impl == NULL) {
     489              :         return CFD_ERROR_UNSUPPORTED;
     490              :     }
     491            0 :     return apply_noslip_with_backend(u, v, NULL, nx, ny, 1, 0, impl);
     492              : }
     493              : 
     494            2 : cfd_status_t bc_apply_noslip_omp(double* u, double* v, size_t nx, size_t ny) {
     495            2 :     if (!u || !v || nx < 3 || ny < 3) {
     496              :         return CFD_ERROR_INVALID;
     497              :     }
     498            2 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
     499            2 :     if (impl == NULL) {
     500              :         return CFD_ERROR_UNSUPPORTED;
     501              :     }
     502            2 :     return apply_noslip_with_backend(u, v, NULL, nx, ny, 1, 0, impl);
     503              : }
     504              : 
     505              : /* ============================================================================
     506              :  * Public API - Inlet Velocity Boundary Conditions (2D)
     507              :  *
     508              :  * Inlet conditions specify velocity at inflow boundaries.
     509              :  * ============================================================================ */
     510              : 
     511              : /** Helper: apply inlet BC with specified backend */
     512           59 : static cfd_status_t apply_inlet_with_backend(double* u, double* v, double* w,
     513              :                                               size_t nx, size_t ny,
     514              :                                               size_t nz, size_t stride_z,
     515              :                                               const bc_inlet_config_t* config,
     516              :                                               const bc_backend_impl_t* impl) {
     517            5 :     if (impl == NULL || impl->apply_inlet == NULL) {
     518              :         return CFD_ERROR_UNSUPPORTED;
     519              :     }
     520            1 :     if (!validate_3d_layout(nx, ny, nz, stride_z)) {
     521              :         return CFD_ERROR_INVALID;
     522              :     }
     523           59 :     return impl->apply_inlet(u, v, w, nx, ny, nz, stride_z, config);
     524              : }
     525              : 
     526              : /* Inlet configuration factory functions */
     527              : 
     528           48 : bc_inlet_config_t bc_inlet_config_uniform(double u_velocity, double v_velocity) {
     529           48 :     bc_inlet_config_t config = {0};
     530           48 :     config.edge = BC_EDGE_LEFT;  /* Default to left boundary */
     531           48 :     config.profile = BC_INLET_PROFILE_UNIFORM;
     532           48 :     config.spec_type = BC_INLET_SPEC_VELOCITY;
     533           48 :     config.spec.velocity.u = u_velocity;
     534           48 :     config.spec.velocity.v = v_velocity;
     535           48 :     config.custom_profile = NULL;
     536           48 :     config.custom_profile_user_data = NULL;
     537           48 :     return config;
     538              : }
     539              : 
     540            8 : bc_inlet_config_t bc_inlet_config_parabolic(double max_velocity) {
     541            8 :     bc_inlet_config_t config = {0};
     542            8 :     config.edge = BC_EDGE_LEFT;  /* Default to left boundary */
     543            8 :     config.profile = BC_INLET_PROFILE_PARABOLIC;
     544            8 :     config.spec_type = BC_INLET_SPEC_VELOCITY;
     545              :     /* For left boundary, parabolic flow is in +x direction */
     546            8 :     config.spec.velocity.u = max_velocity;
     547            8 :     config.spec.velocity.v = 0.0;
     548            8 :     config.custom_profile = NULL;
     549            8 :     config.custom_profile_user_data = NULL;
     550            8 :     return config;
     551              : }
     552              : 
     553            3 : bc_inlet_config_t bc_inlet_config_magnitude_dir(double magnitude, double direction) {
     554            3 :     bc_inlet_config_t config = {0};
     555            3 :     config.edge = BC_EDGE_LEFT;  /* Default to left boundary */
     556            3 :     config.profile = BC_INLET_PROFILE_UNIFORM;
     557            3 :     config.spec_type = BC_INLET_SPEC_MAGNITUDE_DIR;
     558            3 :     config.spec.magnitude_dir.magnitude = magnitude;
     559            3 :     config.spec.magnitude_dir.direction = direction;
     560            3 :     config.custom_profile = NULL;
     561            3 :     config.custom_profile_user_data = NULL;
     562            3 :     return config;
     563              : }
     564              : 
     565           11 : bc_inlet_config_t bc_inlet_config_mass_flow(double mass_flow_rate, double density, double inlet_length) {
     566           11 :     bc_inlet_config_t config = {0};
     567           11 :     config.edge = BC_EDGE_LEFT;  /* Default to left boundary */
     568           11 :     config.profile = BC_INLET_PROFILE_UNIFORM;
     569           11 :     config.spec_type = BC_INLET_SPEC_MASS_FLOW;
     570           11 :     config.spec.mass_flow.mass_flow_rate = mass_flow_rate;
     571           11 :     config.spec.mass_flow.density = density;
     572           11 :     config.spec.mass_flow.inlet_length = inlet_length;
     573           11 :     config.custom_profile = NULL;
     574           11 :     config.custom_profile_user_data = NULL;
     575           11 :     return config;
     576              : }
     577              : 
     578            2 : bc_inlet_config_t bc_inlet_config_custom(bc_inlet_profile_fn callback, void* user_data) {
     579            2 :     bc_inlet_config_t config = {0};
     580            2 :     config.edge = BC_EDGE_LEFT;  /* Default to left boundary */
     581            2 :     config.profile = BC_INLET_PROFILE_CUSTOM;
     582            2 :     config.spec_type = BC_INLET_SPEC_VELOCITY;
     583            2 :     config.spec.velocity.u = 0.0;
     584            2 :     config.spec.velocity.v = 0.0;
     585            2 :     config.custom_profile = callback;
     586            2 :     config.custom_profile_user_data = user_data;
     587            2 :     return config;
     588              : }
     589              : 
     590           56 : void bc_inlet_set_edge(bc_inlet_config_t* config, bc_edge_t edge) {
     591           56 :     if (config != NULL) {
     592           56 :         config->edge = edge;
     593              :     }
     594           56 : }
     595              : 
     596              : /* Inlet BC application functions (2D) */
     597              : 
     598            4 : cfd_status_t bc_apply_inlet(double* u, double* v, size_t nx, size_t ny,
     599              :                              const bc_inlet_config_t* config) {
     600            4 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     601              :         return CFD_ERROR_INVALID;
     602              :     }
     603            4 :     return apply_inlet_with_backend(u, v, NULL, nx, ny, 1, 0, config, get_backend_impl(g_current_backend));
     604              : }
     605              : 
     606           52 : cfd_status_t bc_apply_inlet_cpu(double* u, double* v, size_t nx, size_t ny,
     607              :                                  const bc_inlet_config_t* config) {
     608           52 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     609              :         return CFD_ERROR_INVALID;
     610              :     }
     611           49 :     return apply_inlet_with_backend(u, v, NULL, nx, ny, 1, 0, config, &bc_impl_scalar);
     612              : }
     613              : 
     614            2 : cfd_status_t bc_apply_inlet_simd(double* u, double* v, size_t nx, size_t ny,
     615              :                                       const bc_inlet_config_t* config) {
     616            2 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     617              :         return CFD_ERROR_INVALID;
     618              :     }
     619            2 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
     620            0 :     if (impl == NULL) {
     621              :         return CFD_ERROR_UNSUPPORTED;
     622              :     }
     623            0 :     return apply_inlet_with_backend(u, v, NULL, nx, ny, 1, 0, config, impl);
     624              : }
     625              : 
     626            5 : cfd_status_t bc_apply_inlet_omp(double* u, double* v, size_t nx, size_t ny,
     627              :                                  const bc_inlet_config_t* config) {
     628            5 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     629              :         return CFD_ERROR_INVALID;
     630              :     }
     631            5 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
     632            5 :     if (impl == NULL) {
     633              :         return CFD_ERROR_UNSUPPORTED;
     634              :     }
     635            5 :     return apply_inlet_with_backend(u, v, NULL, nx, ny, 1, 0, config, impl);
     636              : }
     637              : 
     638              : /* ============================================================================
     639              :  * Public API - Time-Varying Inlet Boundary Conditions
     640              :  * ============================================================================ */
     641              : 
     642              : /* Time-varying inlet configuration factory functions */
     643              : 
     644            7 : bc_inlet_config_t bc_inlet_config_time_sinusoidal(
     645              :     double u_velocity, double v_velocity,
     646              :     double frequency, double amplitude, double phase, double offset) {
     647            7 :     bc_inlet_config_t config = bc_inlet_config_uniform(u_velocity, v_velocity);
     648            7 :     config.time_config.profile = BC_TIME_PROFILE_SINUSOIDAL;
     649            7 :     config.time_config.params.sinusoidal.frequency = frequency;
     650            7 :     config.time_config.params.sinusoidal.amplitude = amplitude;
     651            7 :     config.time_config.params.sinusoidal.phase = phase;
     652            7 :     config.time_config.params.sinusoidal.offset = offset;
     653            7 :     return config;
     654              : }
     655              : 
     656            4 : bc_inlet_config_t bc_inlet_config_time_ramp(
     657              :     double u_velocity, double v_velocity,
     658              :     double t_start, double t_end,
     659              :     double value_start, double value_end) {
     660            4 :     bc_inlet_config_t config = bc_inlet_config_uniform(u_velocity, v_velocity);
     661              : 
     662              :     // Validate that t_end > t_start to prevent division by zero
     663            4 :     if (t_end <= t_start) {
     664              :         // Swap values if they are in wrong order
     665            0 :         double temp = t_start;
     666            0 :         t_start = t_end;
     667            0 :         t_end = temp;
     668              : 
     669              :         // Also swap corresponding values to maintain the intended behavior
     670            0 :         temp = value_start;
     671            0 :         value_start = value_end;
     672            0 :         value_end = temp;
     673              : 
     674            0 :         bc_report_error(BC_ERROR_INVALID, "bc_inlet_config_time_ramp",
     675              :                        "t_start >= t_end: parameters have been swapped to ensure t_end > t_start");
     676              :     }
     677              : 
     678            4 :     config.time_config.profile = BC_TIME_PROFILE_RAMP;
     679            4 :     config.time_config.params.ramp.t_start = t_start;
     680            4 :     config.time_config.params.ramp.t_end = t_end;
     681            4 :     config.time_config.params.ramp.value_start = value_start;
     682            4 :     config.time_config.params.ramp.value_end = value_end;
     683            4 :     return config;
     684              : }
     685              : 
     686            4 : bc_inlet_config_t bc_inlet_config_time_step(
     687              :     double u_velocity, double v_velocity,
     688              :     double t_step, double value_before, double value_after) {
     689            4 :     bc_inlet_config_t config = bc_inlet_config_uniform(u_velocity, v_velocity);
     690            4 :     config.time_config.profile = BC_TIME_PROFILE_STEP;
     691            4 :     config.time_config.params.step.t_step = t_step;
     692            4 :     config.time_config.params.step.value_before = value_before;
     693            4 :     config.time_config.params.step.value_after = value_after;
     694            4 :     return config;
     695              : }
     696              : 
     697            2 : bc_inlet_config_t bc_inlet_config_time_custom(
     698              :     bc_inlet_profile_time_fn callback, void* user_data) {
     699            2 :     bc_inlet_config_t config = {0};
     700            2 :     config.edge = BC_EDGE_LEFT;
     701            2 :     config.profile = BC_INLET_PROFILE_UNIFORM;
     702            2 :     config.spec_type = BC_INLET_SPEC_VELOCITY;
     703            2 :     config.spec.velocity.u = 1.0;  /* Default base velocity */
     704            2 :     config.spec.velocity.v = 0.0;
     705            2 :     config.custom_profile_time = callback;
     706            2 :     config.custom_profile_time_user_data = user_data;
     707            2 :     return config;
     708              : }
     709              : 
     710            2 : void bc_inlet_set_time_sinusoidal(
     711              :     bc_inlet_config_t* config,
     712              :     double frequency, double amplitude, double phase, double offset) {
     713            2 :     if (config != NULL) {
     714            2 :         config->time_config.profile = BC_TIME_PROFILE_SINUSOIDAL;
     715            2 :         config->time_config.params.sinusoidal.frequency = frequency;
     716            2 :         config->time_config.params.sinusoidal.amplitude = amplitude;
     717            2 :         config->time_config.params.sinusoidal.phase = phase;
     718            2 :         config->time_config.params.sinusoidal.offset = offset;
     719              :     }
     720            2 : }
     721              : 
     722            1 : void bc_inlet_set_time_ramp(
     723              :     bc_inlet_config_t* config,
     724              :     double t_start, double t_end,
     725              :     double value_start, double value_end) {
     726            1 :     if (config != NULL) {
     727            1 :         config->time_config.profile = BC_TIME_PROFILE_RAMP;
     728            1 :         config->time_config.params.ramp.t_start = t_start;
     729            1 :         config->time_config.params.ramp.t_end = t_end;
     730            1 :         config->time_config.params.ramp.value_start = value_start;
     731            1 :         config->time_config.params.ramp.value_end = value_end;
     732              :     }
     733            1 : }
     734              : 
     735            1 : void bc_inlet_set_time_step(
     736              :     bc_inlet_config_t* config,
     737              :     double t_step, double value_before, double value_after) {
     738            1 :     if (config != NULL) {
     739            1 :         config->time_config.profile = BC_TIME_PROFILE_STEP;
     740            1 :         config->time_config.params.step.t_step = t_step;
     741            1 :         config->time_config.params.step.value_before = value_before;
     742            1 :         config->time_config.params.step.value_after = value_after;
     743              :     }
     744            1 : }
     745              : 
     746              : /* Time-varying inlet application functions (2D) */
     747              : 
     748            2 : cfd_status_t bc_apply_inlet_time(
     749              :     double* u, double* v, size_t nx, size_t ny,
     750              :     const bc_inlet_config_t* config,
     751              :     const bc_time_context_t* time_ctx) {
     752            2 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     753              :         return CFD_ERROR_INVALID;
     754              :     }
     755              :     /* If no time variation, delegate to standard inlet function */
     756            2 :     if (config->time_config.profile == BC_TIME_PROFILE_CONSTANT &&
     757            1 :         config->custom_profile_time == NULL) {
     758            1 :         return bc_apply_inlet(u, v, nx, ny, config);
     759              :     }
     760            1 :     return bc_apply_inlet_time_scalar_impl(u, v, NULL, nx, ny, 1, 0, config, time_ctx);
     761              : }
     762              : 
     763           17 : cfd_status_t bc_apply_inlet_time_cpu(
     764              :     double* u, double* v, size_t nx, size_t ny,
     765              :     const bc_inlet_config_t* config,
     766              :     const bc_time_context_t* time_ctx) {
     767           17 :     return bc_apply_inlet_time_scalar_impl(u, v, NULL, nx, ny, 1, 0, config, time_ctx);
     768              : }
     769              : 
     770            0 : cfd_status_t bc_apply_inlet_time_simd(
     771              :     double* u, double* v, size_t nx, size_t ny,
     772              :     const bc_inlet_config_t* config,
     773              :     const bc_time_context_t* time_ctx) {
     774              :     /* SIMD implementation not yet available, fall back to CPU */
     775            0 :     return bc_apply_inlet_time_scalar_impl(u, v, NULL, nx, ny, 1, 0, config, time_ctx);
     776              : }
     777              : 
     778            0 : cfd_status_t bc_apply_inlet_time_omp(
     779              :     double* u, double* v, size_t nx, size_t ny,
     780              :     const bc_inlet_config_t* config,
     781              :     const bc_time_context_t* time_ctx) {
     782              :     /* OpenMP implementation not yet available, fall back to CPU */
     783            0 :     return bc_apply_inlet_time_scalar_impl(u, v, NULL, nx, ny, 1, 0, config, time_ctx);
     784              : }
     785              : 
     786              : /* ============================================================================
     787              :  * Public API - Outlet Boundary Conditions (2D)
     788              :  *
     789              :  * Outlet conditions specify conditions at outflow boundaries.
     790              :  * Supports zero-gradient (Neumann) and convective outlet types.
     791              :  * ============================================================================ */
     792              : 
     793              : /** Helper: apply outlet BC with specified backend */
     794           61 : static cfd_status_t apply_outlet_with_backend(double* field, size_t nx, size_t ny,
     795              :                                                size_t nz, size_t stride_z,
     796              :                                                const bc_outlet_config_t* config,
     797              :                                                const bc_backend_impl_t* impl) {
     798            7 :     if (impl == NULL || impl->apply_outlet == NULL) {
     799              :         return CFD_ERROR_UNSUPPORTED;
     800              :     }
     801            1 :     if (!validate_3d_layout(nx, ny, nz, stride_z)) {
     802              :         return CFD_ERROR_INVALID;
     803              :     }
     804           57 :     return impl->apply_outlet(field, nx, ny, nz, stride_z, config);
     805              : }
     806              : 
     807              : /* Outlet configuration factory functions */
     808              : 
     809           40 : bc_outlet_config_t bc_outlet_config_zero_gradient(void) {
     810           40 :     bc_outlet_config_t config = {0};
     811           40 :     config.edge = BC_EDGE_RIGHT;  /* Default to right boundary */
     812           40 :     config.type = BC_OUTLET_ZERO_GRADIENT;
     813           40 :     config.advection_velocity = 0.0;
     814           40 :     return config;
     815              : }
     816              : 
     817            5 : bc_outlet_config_t bc_outlet_config_convective(double advection_velocity) {
     818            5 :     bc_outlet_config_t config = {0};
     819            5 :     config.edge = BC_EDGE_RIGHT;  /* Default to right boundary */
     820            5 :     config.type = BC_OUTLET_CONVECTIVE;
     821            5 :     config.advection_velocity = advection_velocity;
     822            5 :     return config;
     823              : }
     824              : 
     825           44 : void bc_outlet_set_edge(bc_outlet_config_t* config, bc_edge_t edge) {
     826           44 :     if (config != NULL) {
     827           44 :         config->edge = edge;
     828              :     }
     829           44 : }
     830              : 
     831              : /* Outlet BC application functions (2D) */
     832              : 
     833            6 : cfd_status_t bc_apply_outlet_scalar(double* field, size_t nx, size_t ny,
     834              :                                      const bc_outlet_config_t* config) {
     835            6 :     if (!field || !config || nx < 3 || ny < 3) {
     836              :         return CFD_ERROR_INVALID;
     837              :     }
     838            6 :     return apply_outlet_with_backend(field, nx, ny, 1, 0, config, get_backend_impl(g_current_backend));
     839              : }
     840              : 
     841            0 : cfd_status_t bc_apply_outlet_velocity(double* u, double* v, size_t nx, size_t ny,
     842              :                                        const bc_outlet_config_t* config) {
     843            0 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     844              :         return CFD_ERROR_INVALID;
     845              :     }
     846            0 :     cfd_status_t status = apply_outlet_with_backend(u, nx, ny, 1, 0, config, get_backend_impl(g_current_backend));
     847            0 :     if (status != CFD_SUCCESS) {
     848            0 :         return status;
     849              :     }
     850            0 :     return apply_outlet_with_backend(v, nx, ny, 1, 0, config, get_backend_impl(g_current_backend));
     851              : }
     852              : 
     853           43 : cfd_status_t bc_apply_outlet_scalar_cpu(double* field, size_t nx, size_t ny,
     854              :                                          const bc_outlet_config_t* config) {
     855           43 :     if (!field || !config || nx < 3 || ny < 3) {
     856              :         return CFD_ERROR_INVALID;
     857              :     }
     858           40 :     return apply_outlet_with_backend(field, nx, ny, 1, 0, config, &bc_impl_scalar);
     859              : }
     860              : 
     861            6 : cfd_status_t bc_apply_outlet_scalar_simd(double* field, size_t nx, size_t ny,
     862              :                                               const bc_outlet_config_t* config) {
     863            6 :     if (!field || !config || nx < 3 || ny < 3) {
     864              :         return CFD_ERROR_INVALID;
     865              :     }
     866            6 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
     867            0 :     if (impl == NULL) {
     868              :         return CFD_ERROR_UNSUPPORTED;
     869              :     }
     870            0 :     return apply_outlet_with_backend(field, nx, ny, 1, 0, config, impl);
     871              : }
     872              : 
     873            6 : cfd_status_t bc_apply_outlet_scalar_omp(double* field, size_t nx, size_t ny,
     874              :                                          const bc_outlet_config_t* config) {
     875            6 :     if (!field || !config || nx < 3 || ny < 3) {
     876              :         return CFD_ERROR_INVALID;
     877              :     }
     878            6 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
     879            6 :     if (impl == NULL) {
     880              :         return CFD_ERROR_UNSUPPORTED;
     881              :     }
     882            6 :     return apply_outlet_with_backend(field, nx, ny, 1, 0, config, impl);
     883              : }
     884              : 
     885            4 : cfd_status_t bc_apply_outlet_velocity_cpu(double* u, double* v, size_t nx, size_t ny,
     886              :                                            const bc_outlet_config_t* config) {
     887            4 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     888              :         return CFD_ERROR_INVALID;
     889              :     }
     890            4 :     cfd_status_t status = apply_outlet_with_backend(u, nx, ny, 1, 0, config, &bc_impl_scalar);
     891            4 :     if (status != CFD_SUCCESS) {
     892            0 :         return status;
     893              :     }
     894            4 :     return apply_outlet_with_backend(v, nx, ny, 1, 0, config, &bc_impl_scalar);
     895              : }
     896              : 
     897            0 : cfd_status_t bc_apply_outlet_velocity_simd(double* u, double* v, size_t nx, size_t ny,
     898              :                                                 const bc_outlet_config_t* config) {
     899            0 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     900              :         return CFD_ERROR_INVALID;
     901              :     }
     902            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
     903            0 :     if (impl == NULL) {
     904              :         return CFD_ERROR_UNSUPPORTED;
     905              :     }
     906            0 :     cfd_status_t status = apply_outlet_with_backend(u, nx, ny, 1, 0, config, impl);
     907            0 :     if (status != CFD_SUCCESS) {
     908            0 :         return status;
     909              :     }
     910            0 :     return apply_outlet_with_backend(v, nx, ny, 1, 0, config, impl);
     911              : }
     912              : 
     913            0 : cfd_status_t bc_apply_outlet_velocity_omp(double* u, double* v, size_t nx, size_t ny,
     914              :                                            const bc_outlet_config_t* config) {
     915            0 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     916              :         return CFD_ERROR_INVALID;
     917              :     }
     918            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
     919            0 :     if (impl == NULL) {
     920              :         return CFD_ERROR_UNSUPPORTED;
     921              :     }
     922            0 :     cfd_status_t status = apply_outlet_with_backend(u, nx, ny, 1, 0, config, impl);
     923            0 :     if (status != CFD_SUCCESS) {
     924            0 :         return status;
     925              :     }
     926            0 :     return apply_outlet_with_backend(v, nx, ny, 1, 0, config, impl);
     927              : }
     928              : 
     929              : /* ============================================================================
     930              :  * Public API - Symmetry Boundary Conditions (2D)
     931              :  * ============================================================================ */
     932              : 
     933              : /** Helper: apply symmetry BC with specified backend */
     934            1 : static cfd_status_t apply_symmetry_with_backend(double* u, double* v, double* w,
     935              :                                                  size_t nx, size_t ny,
     936              :                                                  size_t nz, size_t stride_z,
     937              :                                                  const bc_symmetry_config_t* config,
     938              :                                                  const bc_backend_impl_t* impl) {
     939            0 :     if (impl == NULL || impl->apply_symmetry == NULL) {
     940              :         return CFD_ERROR_UNSUPPORTED;
     941              :     }
     942            0 :     if (!validate_3d_layout(nx, ny, nz, stride_z)) {
     943              :         return CFD_ERROR_INVALID;
     944              :     }
     945            0 :     return impl->apply_symmetry(u, v, w, nx, ny, nz, stride_z, config);
     946              : }
     947              : 
     948            2 : cfd_status_t bc_apply_symmetry(double* u, double* v, size_t nx, size_t ny,
     949              :                                 const bc_symmetry_config_t* config) {
     950            2 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     951              :         return CFD_ERROR_INVALID;
     952              :     }
     953            2 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
     954            2 :     if (impl == NULL) {
     955              :         return CFD_ERROR_UNSUPPORTED;
     956              :     }
     957              :     /* If selected backend doesn't support symmetry, fall back to scalar */
     958            2 :     if (impl->apply_symmetry == NULL) {
     959            2 :         return bc_apply_symmetry_scalar_impl(u, v, NULL, nx, ny, 1, 0, config);
     960              :     }
     961            0 :     return impl->apply_symmetry(u, v, NULL, nx, ny, 1, 0, config);
     962              : }
     963              : 
     964           18 : cfd_status_t bc_apply_symmetry_cpu(double* u, double* v, size_t nx, size_t ny,
     965              :                                     const bc_symmetry_config_t* config) {
     966           18 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     967              :         return CFD_ERROR_INVALID;
     968              :     }
     969           14 :     return bc_apply_symmetry_scalar_impl(u, v, NULL, nx, ny, 1, 0, config);
     970              : }
     971              : 
     972            0 : cfd_status_t bc_apply_symmetry_simd(double* u, double* v, size_t nx, size_t ny,
     973              :                                      const bc_symmetry_config_t* config) {
     974            0 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     975              :         return CFD_ERROR_INVALID;
     976              :     }
     977            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
     978            0 :     if (impl == NULL) {
     979              :         return CFD_ERROR_UNSUPPORTED;
     980              :     }
     981            0 :     return apply_symmetry_with_backend(u, v, NULL, nx, ny, 1, 0, config, impl);
     982              : }
     983              : 
     984            1 : cfd_status_t bc_apply_symmetry_omp(double* u, double* v, size_t nx, size_t ny,
     985              :                                     const bc_symmetry_config_t* config) {
     986            1 :     if (!u || !v || !config || nx < 3 || ny < 3) {
     987              :         return CFD_ERROR_INVALID;
     988              :     }
     989            1 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
     990            1 :     if (impl == NULL) {
     991              :         return CFD_ERROR_UNSUPPORTED;
     992              :     }
     993            1 :     return apply_symmetry_with_backend(u, v, NULL, nx, ny, 1, 0, config, impl);
     994              : }
     995              : 
     996              : /* ============================================================================
     997              :  * Public API - 3D Boundary Conditions
     998              :  *
     999              :  * These functions support 3D fields with z-dimension.
    1000              :  * Fields are stored as contiguous z-planes: field[k * stride_z + j * nx + i].
    1001              :  * ============================================================================ */
    1002              : 
    1003              : /* --- 3D Scalar / Velocity Field BCs --- */
    1004              : 
    1005         2023 : cfd_status_t bc_apply_scalar_3d(double* field, size_t nx, size_t ny,
    1006              :                                  size_t nz, size_t stride_z, bc_type_t type) {
    1007         2023 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
    1008         2023 :     return apply_scalar_field_bc(field, nx, ny, nz, stride_z, type, impl);
    1009              : }
    1010              : 
    1011            0 : cfd_status_t bc_apply_velocity_3d(double* u, double* v, double* w,
    1012              :                                    size_t nx, size_t ny,
    1013              :                                    size_t nz, size_t stride_z, bc_type_t type) {
    1014            0 :     if (!u || !v || nx < 3 || ny < 3) {
    1015              :         return CFD_ERROR_INVALID;
    1016              :     }
    1017            0 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
    1018            0 :     cfd_status_t status = apply_scalar_field_bc(u, nx, ny, nz, stride_z, type, impl);
    1019            0 :     if (status != CFD_SUCCESS) {
    1020              :         return status;
    1021              :     }
    1022            0 :     status = apply_scalar_field_bc(v, nx, ny, nz, stride_z, type, impl);
    1023            0 :     if (status != CFD_SUCCESS) {
    1024              :         return status;
    1025              :     }
    1026            0 :     if (w && nz > 1) {
    1027            0 :         status = apply_scalar_field_bc(w, nx, ny, nz, stride_z, type, impl);
    1028              :     }
    1029              :     return status;
    1030              : }
    1031              : 
    1032            0 : cfd_status_t bc_apply_scalar_cpu_3d(double* field, size_t nx, size_t ny,
    1033              :                                      size_t nz, size_t stride_z, bc_type_t type) {
    1034            0 :     return apply_scalar_field_bc(field, nx, ny, nz, stride_z, type, &bc_impl_scalar);
    1035              : }
    1036              : 
    1037            0 : cfd_status_t bc_apply_scalar_simd_3d(double* field, size_t nx, size_t ny,
    1038              :                                       size_t nz, size_t stride_z, bc_type_t type) {
    1039            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
    1040            0 :     if (impl == NULL) {
    1041              :         return CFD_ERROR_UNSUPPORTED;
    1042              :     }
    1043            0 :     return apply_scalar_field_bc(field, nx, ny, nz, stride_z, type, impl);
    1044              : }
    1045              : 
    1046            0 : cfd_status_t bc_apply_scalar_omp_3d(double* field, size_t nx, size_t ny,
    1047              :                                      size_t nz, size_t stride_z, bc_type_t type) {
    1048            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
    1049            0 :     if (impl == NULL) {
    1050              :         return CFD_ERROR_UNSUPPORTED;
    1051              :     }
    1052            0 :     return apply_scalar_field_bc(field, nx, ny, nz, stride_z, type, impl);
    1053              : }
    1054              : 
    1055            0 : cfd_status_t bc_apply_velocity_cpu_3d(double* u, double* v, double* w,
    1056              :                                        size_t nx, size_t ny,
    1057              :                                        size_t nz, size_t stride_z, bc_type_t type) {
    1058            0 :     if (!u || !v || nx < 3 || ny < 3) {
    1059              :         return CFD_ERROR_INVALID;
    1060              :     }
    1061            0 :     cfd_status_t status = apply_scalar_field_bc(u, nx, ny, nz, stride_z, type, &bc_impl_scalar);
    1062            0 :     if (status != CFD_SUCCESS) return status;
    1063            0 :     status = apply_scalar_field_bc(v, nx, ny, nz, stride_z, type, &bc_impl_scalar);
    1064            0 :     if (status != CFD_SUCCESS) return status;
    1065            0 :     if (w && nz > 1) {
    1066            0 :         status = apply_scalar_field_bc(w, nx, ny, nz, stride_z, type, &bc_impl_scalar);
    1067              :     }
    1068              :     return status;
    1069              : }
    1070              : 
    1071            0 : cfd_status_t bc_apply_velocity_simd_3d(double* u, double* v, double* w,
    1072              :                                         size_t nx, size_t ny,
    1073              :                                         size_t nz, size_t stride_z, bc_type_t type) {
    1074            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
    1075            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1076            0 :     if (!u || !v || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1077            0 :     cfd_status_t status = apply_scalar_field_bc(u, nx, ny, nz, stride_z, type, impl);
    1078            0 :     if (status != CFD_SUCCESS) return status;
    1079            0 :     status = apply_scalar_field_bc(v, nx, ny, nz, stride_z, type, impl);
    1080            0 :     if (status != CFD_SUCCESS) return status;
    1081            0 :     if (w && nz > 1) {
    1082            0 :         status = apply_scalar_field_bc(w, nx, ny, nz, stride_z, type, impl);
    1083              :     }
    1084              :     return status;
    1085              : }
    1086              : 
    1087            0 : cfd_status_t bc_apply_velocity_omp_3d(double* u, double* v, double* w,
    1088              :                                        size_t nx, size_t ny,
    1089              :                                        size_t nz, size_t stride_z, bc_type_t type) {
    1090            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
    1091            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1092            0 :     if (!u || !v || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1093            0 :     cfd_status_t status = apply_scalar_field_bc(u, nx, ny, nz, stride_z, type, impl);
    1094            0 :     if (status != CFD_SUCCESS) return status;
    1095            0 :     status = apply_scalar_field_bc(v, nx, ny, nz, stride_z, type, impl);
    1096            0 :     if (status != CFD_SUCCESS) return status;
    1097            0 :     if (w && nz > 1) {
    1098            0 :         status = apply_scalar_field_bc(w, nx, ny, nz, stride_z, type, impl);
    1099              :     }
    1100              :     return status;
    1101              : }
    1102              : 
    1103              : /* --- 3D Dirichlet BCs --- */
    1104              : 
    1105            1 : cfd_status_t bc_apply_dirichlet_scalar_3d(double* field, size_t nx, size_t ny,
    1106              :                                            size_t nz, size_t stride_z,
    1107              :                                            const bc_dirichlet_values_t* values) {
    1108            1 :     if (!field || !values || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1109            1 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
    1110            1 :     return apply_dirichlet_with_backend(field, nx, ny, nz, stride_z, values, impl);
    1111              : }
    1112              : 
    1113            0 : cfd_status_t bc_apply_dirichlet_velocity_3d(double* u, double* v, double* w,
    1114              :                                              size_t nx, size_t ny,
    1115              :                                              size_t nz, size_t stride_z,
    1116              :                                              const bc_dirichlet_values_t* u_values,
    1117              :                                              const bc_dirichlet_values_t* v_values,
    1118              :                                              const bc_dirichlet_values_t* w_values) {
    1119            0 :     if (!u || !v || !u_values || !v_values || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1120            0 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
    1121            0 :     cfd_status_t status = apply_dirichlet_with_backend(u, nx, ny, nz, stride_z, u_values, impl);
    1122            0 :     if (status != CFD_SUCCESS) return status;
    1123            0 :     status = apply_dirichlet_with_backend(v, nx, ny, nz, stride_z, v_values, impl);
    1124            0 :     if (status != CFD_SUCCESS) return status;
    1125            0 :     if (w && w_values && nz > 1) {
    1126            0 :         status = apply_dirichlet_with_backend(w, nx, ny, nz, stride_z, w_values, impl);
    1127              :     }
    1128              :     return status;
    1129              : }
    1130              : 
    1131            0 : cfd_status_t bc_apply_dirichlet_scalar_cpu_3d(double* field, size_t nx, size_t ny,
    1132              :                                                size_t nz, size_t stride_z,
    1133              :                                                const bc_dirichlet_values_t* values) {
    1134            0 :     if (!field || !values || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1135            0 :     return apply_dirichlet_with_backend(field, nx, ny, nz, stride_z, values, &bc_impl_scalar);
    1136              : }
    1137              : 
    1138            0 : cfd_status_t bc_apply_dirichlet_scalar_simd_3d(double* field, size_t nx, size_t ny,
    1139              :                                                 size_t nz, size_t stride_z,
    1140              :                                                 const bc_dirichlet_values_t* values) {
    1141            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
    1142            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1143            0 :     if (!field || !values || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1144            0 :     return apply_dirichlet_with_backend(field, nx, ny, nz, stride_z, values, impl);
    1145              : }
    1146              : 
    1147            0 : cfd_status_t bc_apply_dirichlet_scalar_omp_3d(double* field, size_t nx, size_t ny,
    1148              :                                                size_t nz, size_t stride_z,
    1149              :                                                const bc_dirichlet_values_t* values) {
    1150            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
    1151            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1152            0 :     if (!field || !values || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1153            0 :     return apply_dirichlet_with_backend(field, nx, ny, nz, stride_z, values, impl);
    1154              : }
    1155              : 
    1156            0 : cfd_status_t bc_apply_dirichlet_velocity_cpu_3d(double* u, double* v, double* w,
    1157              :                                                  size_t nx, size_t ny,
    1158              :                                                  size_t nz, size_t stride_z,
    1159              :                                                  const bc_dirichlet_values_t* u_values,
    1160              :                                                  const bc_dirichlet_values_t* v_values,
    1161              :                                                  const bc_dirichlet_values_t* w_values) {
    1162            0 :     if (!u || !v || !u_values || !v_values || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1163            0 :     cfd_status_t status = apply_dirichlet_with_backend(u, nx, ny, nz, stride_z, u_values, &bc_impl_scalar);
    1164            0 :     if (status != CFD_SUCCESS) return status;
    1165            0 :     status = apply_dirichlet_with_backend(v, nx, ny, nz, stride_z, v_values, &bc_impl_scalar);
    1166            0 :     if (status != CFD_SUCCESS) return status;
    1167            0 :     if (w && w_values && nz > 1) {
    1168            0 :         status = apply_dirichlet_with_backend(w, nx, ny, nz, stride_z, w_values, &bc_impl_scalar);
    1169              :     }
    1170              :     return status;
    1171              : }
    1172              : 
    1173            0 : cfd_status_t bc_apply_dirichlet_velocity_simd_3d(double* u, double* v, double* w,
    1174              :                                                   size_t nx, size_t ny,
    1175              :                                                   size_t nz, size_t stride_z,
    1176              :                                                   const bc_dirichlet_values_t* u_values,
    1177              :                                                   const bc_dirichlet_values_t* v_values,
    1178              :                                                   const bc_dirichlet_values_t* w_values) {
    1179            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
    1180            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1181            0 :     if (!u || !v || !u_values || !v_values || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1182            0 :     cfd_status_t status = apply_dirichlet_with_backend(u, nx, ny, nz, stride_z, u_values, impl);
    1183            0 :     if (status != CFD_SUCCESS) return status;
    1184            0 :     status = apply_dirichlet_with_backend(v, nx, ny, nz, stride_z, v_values, impl);
    1185            0 :     if (status != CFD_SUCCESS) return status;
    1186            0 :     if (w && w_values && nz > 1) {
    1187            0 :         status = apply_dirichlet_with_backend(w, nx, ny, nz, stride_z, w_values, impl);
    1188              :     }
    1189              :     return status;
    1190              : }
    1191              : 
    1192            0 : cfd_status_t bc_apply_dirichlet_velocity_omp_3d(double* u, double* v, double* w,
    1193              :                                                  size_t nx, size_t ny,
    1194              :                                                  size_t nz, size_t stride_z,
    1195              :                                                  const bc_dirichlet_values_t* u_values,
    1196              :                                                  const bc_dirichlet_values_t* v_values,
    1197              :                                                  const bc_dirichlet_values_t* w_values) {
    1198            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
    1199            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1200            0 :     if (!u || !v || !u_values || !v_values || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1201            0 :     cfd_status_t status = apply_dirichlet_with_backend(u, nx, ny, nz, stride_z, u_values, impl);
    1202            0 :     if (status != CFD_SUCCESS) return status;
    1203            0 :     status = apply_dirichlet_with_backend(v, nx, ny, nz, stride_z, v_values, impl);
    1204            0 :     if (status != CFD_SUCCESS) return status;
    1205            0 :     if (w && w_values && nz > 1) {
    1206            0 :         status = apply_dirichlet_with_backend(w, nx, ny, nz, stride_z, w_values, impl);
    1207              :     }
    1208              :     return status;
    1209              : }
    1210              : 
    1211              : /* --- 3D No-Slip Wall BCs --- */
    1212              : 
    1213            1 : cfd_status_t bc_apply_noslip_3d(double* u, double* v, double* w,
    1214              :                                  size_t nx, size_t ny,
    1215              :                                  size_t nz, size_t stride_z) {
    1216            1 :     if (!u || !v || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1217            1 :     return apply_noslip_with_backend(u, v, w, nx, ny, nz, stride_z, get_backend_impl(g_current_backend));
    1218              : }
    1219              : 
    1220            0 : cfd_status_t bc_apply_noslip_cpu_3d(double* u, double* v, double* w,
    1221              :                                      size_t nx, size_t ny,
    1222              :                                      size_t nz, size_t stride_z) {
    1223            0 :     if (!u || !v || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1224            0 :     return apply_noslip_with_backend(u, v, w, nx, ny, nz, stride_z, &bc_impl_scalar);
    1225              : }
    1226              : 
    1227            0 : cfd_status_t bc_apply_noslip_simd_3d(double* u, double* v, double* w,
    1228              :                                       size_t nx, size_t ny,
    1229              :                                       size_t nz, size_t stride_z) {
    1230            0 :     if (!u || !v || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1231            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
    1232            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1233            0 :     return apply_noslip_with_backend(u, v, w, nx, ny, nz, stride_z, impl);
    1234              : }
    1235              : 
    1236            0 : cfd_status_t bc_apply_noslip_omp_3d(double* u, double* v, double* w,
    1237              :                                      size_t nx, size_t ny,
    1238              :                                      size_t nz, size_t stride_z) {
    1239            0 :     if (!u || !v || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1240            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
    1241            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1242            0 :     return apply_noslip_with_backend(u, v, w, nx, ny, nz, stride_z, impl);
    1243              : }
    1244              : 
    1245              : /* --- 3D Inlet BCs --- */
    1246              : 
    1247            1 : cfd_status_t bc_apply_inlet_3d(double* u, double* v, double* w,
    1248              :                                 size_t nx, size_t ny,
    1249              :                                 size_t nz, size_t stride_z,
    1250              :                                 const bc_inlet_config_t* config) {
    1251            1 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1252            1 :     return apply_inlet_with_backend(u, v, w, nx, ny, nz, stride_z, config, get_backend_impl(g_current_backend));
    1253              : }
    1254              : 
    1255            0 : cfd_status_t bc_apply_inlet_cpu_3d(double* u, double* v, double* w,
    1256              :                                     size_t nx, size_t ny,
    1257              :                                     size_t nz, size_t stride_z,
    1258              :                                     const bc_inlet_config_t* config) {
    1259            0 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1260            0 :     return apply_inlet_with_backend(u, v, w, nx, ny, nz, stride_z, config, &bc_impl_scalar);
    1261              : }
    1262              : 
    1263            0 : cfd_status_t bc_apply_inlet_simd_3d(double* u, double* v, double* w,
    1264              :                                      size_t nx, size_t ny,
    1265              :                                      size_t nz, size_t stride_z,
    1266              :                                      const bc_inlet_config_t* config) {
    1267            0 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1268            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
    1269            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1270            0 :     return apply_inlet_with_backend(u, v, w, nx, ny, nz, stride_z, config, impl);
    1271              : }
    1272              : 
    1273            0 : cfd_status_t bc_apply_inlet_omp_3d(double* u, double* v, double* w,
    1274              :                                     size_t nx, size_t ny,
    1275              :                                     size_t nz, size_t stride_z,
    1276              :                                     const bc_inlet_config_t* config) {
    1277            0 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1278            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
    1279            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1280            0 :     return apply_inlet_with_backend(u, v, w, nx, ny, nz, stride_z, config, impl);
    1281              : }
    1282              : 
    1283              : /* --- 3D Time-Varying Inlet BCs --- */
    1284              : 
    1285            0 : cfd_status_t bc_apply_inlet_time_3d(
    1286              :     double* u, double* v, double* w,
    1287              :     size_t nx, size_t ny, size_t nz, size_t stride_z,
    1288              :     const bc_inlet_config_t* config,
    1289              :     const bc_time_context_t* time_ctx) {
    1290            0 :     if (!u || !v || !config || nx < 3 || ny < 3 || !validate_3d_layout(nx, ny, nz, stride_z))
    1291              :         return CFD_ERROR_INVALID;
    1292            0 :     if (config->time_config.profile == BC_TIME_PROFILE_CONSTANT &&
    1293            0 :         config->custom_profile_time == NULL) {
    1294            0 :         return bc_apply_inlet_3d(u, v, w, nx, ny, nz, stride_z, config);
    1295              :     }
    1296            0 :     return bc_apply_inlet_time_scalar_impl(u, v, w, nx, ny, nz, stride_z, config, time_ctx);
    1297              : }
    1298              : 
    1299            0 : cfd_status_t bc_apply_inlet_time_cpu_3d(
    1300              :     double* u, double* v, double* w,
    1301              :     size_t nx, size_t ny, size_t nz, size_t stride_z,
    1302              :     const bc_inlet_config_t* config,
    1303              :     const bc_time_context_t* time_ctx) {
    1304            0 :     if (!u || !v || !config || nx < 3 || ny < 3 || !validate_3d_layout(nx, ny, nz, stride_z))
    1305              :         return CFD_ERROR_INVALID;
    1306            0 :     return bc_apply_inlet_time_scalar_impl(u, v, w, nx, ny, nz, stride_z, config, time_ctx);
    1307              : }
    1308              : 
    1309            0 : cfd_status_t bc_apply_inlet_time_simd_3d(
    1310              :     double* u, double* v, double* w,
    1311              :     size_t nx, size_t ny, size_t nz, size_t stride_z,
    1312              :     const bc_inlet_config_t* config,
    1313              :     const bc_time_context_t* time_ctx) {
    1314            0 :     if (!u || !v || !config || nx < 3 || ny < 3 || !validate_3d_layout(nx, ny, nz, stride_z))
    1315              :         return CFD_ERROR_INVALID;
    1316            0 :     return bc_apply_inlet_time_scalar_impl(u, v, w, nx, ny, nz, stride_z, config, time_ctx);
    1317              : }
    1318              : 
    1319            0 : cfd_status_t bc_apply_inlet_time_omp_3d(
    1320              :     double* u, double* v, double* w,
    1321              :     size_t nx, size_t ny, size_t nz, size_t stride_z,
    1322              :     const bc_inlet_config_t* config,
    1323              :     const bc_time_context_t* time_ctx) {
    1324            0 :     if (!u || !v || !config || nx < 3 || ny < 3 || !validate_3d_layout(nx, ny, nz, stride_z))
    1325              :         return CFD_ERROR_INVALID;
    1326            0 :     return bc_apply_inlet_time_scalar_impl(u, v, w, nx, ny, nz, stride_z, config, time_ctx);
    1327              : }
    1328              : 
    1329              : /* --- 3D Outlet BCs --- */
    1330              : 
    1331            1 : cfd_status_t bc_apply_outlet_scalar_3d(double* field, size_t nx, size_t ny,
    1332              :                                         size_t nz, size_t stride_z,
    1333              :                                         const bc_outlet_config_t* config) {
    1334            1 :     if (!field || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1335            1 :     return apply_outlet_with_backend(field, nx, ny, nz, stride_z, config, get_backend_impl(g_current_backend));
    1336              : }
    1337              : 
    1338            0 : cfd_status_t bc_apply_outlet_velocity_3d(double* u, double* v, double* w,
    1339              :                                           size_t nx, size_t ny,
    1340              :                                           size_t nz, size_t stride_z,
    1341              :                                           const bc_outlet_config_t* config) {
    1342            0 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1343            0 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
    1344            0 :     cfd_status_t status = apply_outlet_with_backend(u, nx, ny, nz, stride_z, config, impl);
    1345            0 :     if (status != CFD_SUCCESS) return status;
    1346            0 :     status = apply_outlet_with_backend(v, nx, ny, nz, stride_z, config, impl);
    1347            0 :     if (status != CFD_SUCCESS) return status;
    1348            0 :     if (w && nz > 1) {
    1349            0 :         status = apply_outlet_with_backend(w, nx, ny, nz, stride_z, config, impl);
    1350              :     }
    1351              :     return status;
    1352              : }
    1353              : 
    1354            0 : cfd_status_t bc_apply_outlet_scalar_cpu_3d(double* field, size_t nx, size_t ny,
    1355              :                                             size_t nz, size_t stride_z,
    1356              :                                             const bc_outlet_config_t* config) {
    1357            0 :     if (!field || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1358            0 :     return apply_outlet_with_backend(field, nx, ny, nz, stride_z, config, &bc_impl_scalar);
    1359              : }
    1360              : 
    1361            0 : cfd_status_t bc_apply_outlet_scalar_simd_3d(double* field, size_t nx, size_t ny,
    1362              :                                              size_t nz, size_t stride_z,
    1363              :                                              const bc_outlet_config_t* config) {
    1364            0 :     if (!field || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1365            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
    1366            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1367            0 :     return apply_outlet_with_backend(field, nx, ny, nz, stride_z, config, impl);
    1368              : }
    1369              : 
    1370            0 : cfd_status_t bc_apply_outlet_scalar_omp_3d(double* field, size_t nx, size_t ny,
    1371              :                                             size_t nz, size_t stride_z,
    1372              :                                             const bc_outlet_config_t* config) {
    1373            0 :     if (!field || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1374            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
    1375            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1376            0 :     return apply_outlet_with_backend(field, nx, ny, nz, stride_z, config, impl);
    1377              : }
    1378              : 
    1379            0 : cfd_status_t bc_apply_outlet_velocity_cpu_3d(double* u, double* v, double* w,
    1380              :                                               size_t nx, size_t ny,
    1381              :                                               size_t nz, size_t stride_z,
    1382              :                                               const bc_outlet_config_t* config) {
    1383            0 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1384            0 :     cfd_status_t status = apply_outlet_with_backend(u, nx, ny, nz, stride_z, config, &bc_impl_scalar);
    1385            0 :     if (status != CFD_SUCCESS) return status;
    1386            0 :     status = apply_outlet_with_backend(v, nx, ny, nz, stride_z, config, &bc_impl_scalar);
    1387            0 :     if (status != CFD_SUCCESS) return status;
    1388            0 :     if (w && nz > 1) {
    1389            0 :         status = apply_outlet_with_backend(w, nx, ny, nz, stride_z, config, &bc_impl_scalar);
    1390              :     }
    1391              :     return status;
    1392              : }
    1393              : 
    1394            0 : cfd_status_t bc_apply_outlet_velocity_simd_3d(double* u, double* v, double* w,
    1395              :                                                size_t nx, size_t ny,
    1396              :                                                size_t nz, size_t stride_z,
    1397              :                                                const bc_outlet_config_t* config) {
    1398            0 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1399            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
    1400            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1401            0 :     cfd_status_t status = apply_outlet_with_backend(u, nx, ny, nz, stride_z, config, impl);
    1402            0 :     if (status != CFD_SUCCESS) return status;
    1403            0 :     status = apply_outlet_with_backend(v, nx, ny, nz, stride_z, config, impl);
    1404            0 :     if (status != CFD_SUCCESS) return status;
    1405            0 :     if (w && nz > 1) {
    1406            0 :         status = apply_outlet_with_backend(w, nx, ny, nz, stride_z, config, impl);
    1407              :     }
    1408              :     return status;
    1409              : }
    1410              : 
    1411            0 : cfd_status_t bc_apply_outlet_velocity_omp_3d(double* u, double* v, double* w,
    1412              :                                               size_t nx, size_t ny,
    1413              :                                               size_t nz, size_t stride_z,
    1414              :                                               const bc_outlet_config_t* config) {
    1415            0 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1416            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
    1417            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1418            0 :     cfd_status_t status = apply_outlet_with_backend(u, nx, ny, nz, stride_z, config, impl);
    1419            0 :     if (status != CFD_SUCCESS) return status;
    1420            0 :     status = apply_outlet_with_backend(v, nx, ny, nz, stride_z, config, impl);
    1421            0 :     if (status != CFD_SUCCESS) return status;
    1422            0 :     if (w && nz > 1) {
    1423            0 :         status = apply_outlet_with_backend(w, nx, ny, nz, stride_z, config, impl);
    1424              :     }
    1425              :     return status;
    1426              : }
    1427              : 
    1428              : /* --- 3D Symmetry BCs --- */
    1429              : 
    1430            1 : cfd_status_t bc_apply_symmetry_3d(double* u, double* v, double* w,
    1431              :                                    size_t nx, size_t ny,
    1432              :                                    size_t nz, size_t stride_z,
    1433              :                                    const bc_symmetry_config_t* config) {
    1434            1 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1435            1 :     if (!validate_3d_layout(nx, ny, nz, stride_z)) return CFD_ERROR_INVALID;
    1436            1 :     const bc_backend_impl_t* impl = get_backend_impl(g_current_backend);
    1437            1 :     if (impl == NULL || impl->apply_symmetry == NULL) {
    1438              :         /* Fall back to scalar implementation when backend lacks symmetry */
    1439            1 :         return bc_apply_symmetry_scalar_impl(u, v, w, nx, ny, nz, stride_z, config);
    1440              :     }
    1441            0 :     return impl->apply_symmetry(u, v, w, nx, ny, nz, stride_z, config);
    1442              : }
    1443              : 
    1444            0 : cfd_status_t bc_apply_symmetry_cpu_3d(double* u, double* v, double* w,
    1445              :                                        size_t nx, size_t ny,
    1446              :                                        size_t nz, size_t stride_z,
    1447              :                                        const bc_symmetry_config_t* config) {
    1448            0 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1449            0 :     return apply_symmetry_with_backend(u, v, w, nx, ny, nz, stride_z, config, &bc_impl_scalar);
    1450              : }
    1451              : 
    1452            0 : cfd_status_t bc_apply_symmetry_simd_3d(double* u, double* v, double* w,
    1453              :                                         size_t nx, size_t ny,
    1454              :                                         size_t nz, size_t stride_z,
    1455              :                                         const bc_symmetry_config_t* config) {
    1456            0 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1457            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_SIMD);
    1458            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1459            0 :     return apply_symmetry_with_backend(u, v, w, nx, ny, nz, stride_z, config, impl);
    1460              : }
    1461              : 
    1462            0 : cfd_status_t bc_apply_symmetry_omp_3d(double* u, double* v, double* w,
    1463              :                                        size_t nx, size_t ny,
    1464              :                                        size_t nz, size_t stride_z,
    1465              :                                        const bc_symmetry_config_t* config) {
    1466            0 :     if (!u || !v || !config || nx < 3 || ny < 3) return CFD_ERROR_INVALID;
    1467            0 :     const bc_backend_impl_t* impl = get_backend_impl(BC_BACKEND_OMP);
    1468            0 :     if (impl == NULL) return CFD_ERROR_UNSUPPORTED;
    1469            0 :     return apply_symmetry_with_backend(u, v, w, nx, ny, nz, stride_z, config, impl);
    1470              : }
        

Generated by: LCOV version 2.0-1