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 : }
|