PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
wallfunction.c
Go to the documentation of this file.
1/**
2 * @file wallfunction.c
3 * @brief Wall function implementations for near-wall turbulence modeling
4 *
5 * This file contains various wall function models that bridge the gap between
6 * the wall and the first computational grid point. Wall functions allow simulations
7 * to avoid resolving the viscous sublayer, reducing computational cost while
8 * maintaining reasonable accuracy for turbulent wall-bounded flows.
9 *
10 * PHYSICAL BACKGROUND:
11 * The near-wall region in turbulent flows is characterized by three layers:
12 * 1. Viscous sublayer (y+ < 5): Dominated by viscous effects, u+ = y+
13 * 2. Buffer layer (5 < y+ < 30): Transition region
14 * 3. Log-law region (y+ > 30): Inertial layer, u+ = (1/κ) ln(y+) + B
15 * where:
16 * u+ = u / u_τ (normalized velocity)
17 * y+ = y * u_τ / ν (normalized wall distance)
18 * u_τ = sqrt(τ_w / ρ) (friction velocity)
19 * κ ≈ 0.41 (von Karman constant)
20 * B ≈ 5.5 (log-law intercept for smooth walls)
21 *
22 * IMPLEMENTED MODELS:
23 * - No-slip: Linear interpolation for stationary walls
24 * - Free-slip: Normal interpolation, tangential extrapolation
25 * - Werner-Wengle: Algebraic wall function
26 * - Log-law: Standard equilibrium wall function with roughness
27 * - Cabot: Non-equilibrium wall function with pressure gradient effects
28 *
29 * @author Original implementation adapted from legacy code
30 * @date Enhanced with comprehensive documentation
31 */
32
33#include "wallfunction.h"
34#include <math.h>
35#include <stdlib.h>
36#include <stdio.h>
37
38// ============================================================================
39// PHYSICAL CONSTANTS
40// ============================================================================
41
42/** @brief von Karman constant (universal turbulence constant) */
43#define KAPPA 0.41
44
45/** @brief Log-law intercept constant B for smooth walls */
46#define LOGLAW_B 5.5
47
48/** @brief Viscous sublayer thickness y+ threshold */
49#define VISCOUS_SUBLAYER_YPLUS 11.81
50
51/** @brief Smooth-to-rough transition y+ threshold */
52#define ROUGHNESS_TRANSITION_YPLUS 2.25
53
54/** @brief Fully rough regime y+ threshold */
55#define FULLY_ROUGH_YPLUS 90.0
56
57/** @brief Eddy viscosity damping coefficient (van Driest damping) */
58#define DAMPING_COEFFICIENT 19.0
59
60// ============================================================================
61// BASIC BOUNDARY CONDITION FUNCTIONS
62// ============================================================================
63
64/**
65 * @brief Internal helper implementation: `noslip()`.
66 * @details Local to this translation unit.
67 */
68void noslip(UserCtx *user,
69 double distance_reference, double distance_boundary,
70 Cmpnts velocity_wall, Cmpnts velocity_reference,
71 Cmpnts *velocity_boundary,
72 double normal_x, double normal_y, double normal_z)
73{
74 (void)user;
75 (void)normal_x;
76 (void)normal_y;
77 (void)normal_z;
78 // Compute velocity difference between reference point and wall
79 double delta_u = velocity_reference.x - velocity_wall.x;
80 double delta_v = velocity_reference.y - velocity_wall.y;
81 double delta_w = velocity_reference.z - velocity_wall.z;
82
83 // Linear interpolation factor
84 double interpolation_factor = distance_boundary / distance_reference;
85
86 // Apply linear interpolation
87 (*velocity_boundary).x = interpolation_factor * delta_u;
88 (*velocity_boundary).y = interpolation_factor * delta_v;
89 (*velocity_boundary).z = interpolation_factor * delta_w;
90
91 // Add wall velocity (shift to absolute frame)
92 (*velocity_boundary).x += velocity_wall.x;
93 (*velocity_boundary).y += velocity_wall.y;
94 (*velocity_boundary).z += velocity_wall.z;
95}
96
97/**
98 * @brief Internal helper implementation: `freeslip()`.
99 * @details Local to this translation unit.
100 */
101void freeslip(UserCtx *user,
102 double distance_reference, double distance_boundary,
103 Cmpnts velocity_wall, Cmpnts velocity_reference,
104 Cmpnts *velocity_boundary,
105 double normal_x, double normal_y, double normal_z)
106{
107 (void)user;
108 // Extract normal components of velocities
109 double wall_normal_velocity = velocity_wall.x * normal_x +
110 velocity_wall.y * normal_y +
111 velocity_wall.z * normal_z;
112
113 double reference_normal_velocity = velocity_reference.x * normal_x +
114 velocity_reference.y * normal_y +
115 velocity_reference.z * normal_z;
116
117 // Interpolate normal velocity component
118 double boundary_normal_velocity = wall_normal_velocity +
119 (reference_normal_velocity - wall_normal_velocity) * (distance_boundary / distance_reference);
120
121 // Extract tangential velocity components (extrapolated from reference point)
122 double tangential_u = velocity_reference.x - reference_normal_velocity * normal_x;
123 double tangential_v = velocity_reference.y - reference_normal_velocity * normal_y;
124 double tangential_w = velocity_reference.z - reference_normal_velocity * normal_z;
125
126 // Reconstruct total velocity: U = U_t + U_n * n
127 (*velocity_boundary).x = tangential_u + boundary_normal_velocity * normal_x;
128 (*velocity_boundary).y = tangential_v + boundary_normal_velocity * normal_y;
129 (*velocity_boundary).z = tangential_w + boundary_normal_velocity * normal_z;
130}
131
132// ============================================================================
133// ROUGHNESS-CORRECTED LOG-LAW FUNCTIONS
134// ============================================================================
135
136/**
137 * @brief Internal helper implementation: `E_coeff()`.
138 * @details Local to this translation unit.
139 */
140double E_coeff(double friction_velocity, double roughness_height, double kinematic_viscosity)
141{
142 // Compute roughness Reynolds number
143 double roughness_reynolds = friction_velocity * roughness_height / kinematic_viscosity;
144
145 double roughness_correction;
146
147 if (roughness_reynolds <= ROUGHNESS_TRANSITION_YPLUS) {
148 // Hydraulically smooth regime: no correction needed
149 roughness_correction = 0.0;
150 }
151 else if (roughness_reynolds < FULLY_ROUGH_YPLUS) {
152 // Transitional regime: smooth interpolation using sine function
153 // This provides a gradual transition between smooth and rough wall behavior
154 double max_correction = LOGLAW_B - 8.5 + (1.0 / KAPPA) * log(roughness_reynolds);
155
156 roughness_correction = max_correction *
157 sin(0.4258 * (log(roughness_reynolds) - 0.811));
158 }
159 else {
160 // Fully rough regime: maximum correction
161 roughness_correction = LOGLAW_B - 8.5 + (1.0 / KAPPA) * log(roughness_reynolds);
162 }
163
164 // Return E = exp[κ(B - ΔB)]
165 return exp(KAPPA * (LOGLAW_B - roughness_correction));
166}
167
168/**
169 * @brief Internal helper implementation: `u_hydset_roughness()`.
170 * @details Local to this translation unit.
171 */
172double u_hydset_roughness(double kinematic_viscosity, double wall_distance,
173 double friction_velocity, double roughness_height)
174{
175 // Normalized wall distance
176 double yplus = friction_velocity * wall_distance / kinematic_viscosity;
177
178 // Threshold values for regime transitions
179 const double viscous_limit = VISCOUS_SUBLAYER_YPLUS;
180 const double loglayer_limit = 300.0;
181
182 double tangential_velocity;
183
184 if (yplus <= viscous_limit) {
185 // Viscous sublayer: linear velocity profile
186 tangential_velocity = friction_velocity * yplus;
187 }
188 else if (yplus <= loglayer_limit) {
189 // Log-layer: logarithmic velocity profile with roughness correction
190 double E = E_coeff(friction_velocity, roughness_height, kinematic_viscosity);
191 tangential_velocity = (friction_velocity / KAPPA) * log(E * yplus);
192 }
193 else {
194 // Outside valid range (wake region or beyond)
195 tangential_velocity = -1.0;
196 }
197
198 return tangential_velocity;
199}
200
201// ============================================================================
202// NEWTON-RAPHSON SOLVER FOR FRICTION VELOCITY (LOG-LAW)
203// ============================================================================
204
205/**
206 * @brief Internal helper implementation: `f_hydset()`.
207 * @details Local to this translation unit.
208 */
209double f_hydset(double kinematic_viscosity, double known_velocity,
210 double wall_distance, double friction_velocity_guess,
211 double roughness_height)
212{
213 double yplus = friction_velocity_guess * wall_distance / kinematic_viscosity;
214 double residual;
215
216 if (yplus <= VISCOUS_SUBLAYER_YPLUS) {
217 // Viscous sublayer: u = u_τ * y+
218 residual = friction_velocity_guess * yplus - known_velocity;
219 }
220 else {
221 // Log-layer: u = (u_τ / κ) * ln(E * y+)
222 double E = E_coeff(friction_velocity_guess, roughness_height, kinematic_viscosity);
223 residual = friction_velocity_guess * (1.0 / KAPPA * log(E * yplus)) - known_velocity;
224 }
225
226 return residual;
227}
228
229/**
230 * @brief Implementation of \ref df_hydset().
231 * @details Full API contract (arguments, ownership, side effects) is documented with
232 * the header declaration in `include/wallfunction.h`.
233 * @see df_hydset()
234 */
235double df_hydset(double kinematic_viscosity, double known_velocity,
236 double wall_distance, double friction_velocity_guess,
237 double roughness_height)
238{
239 const double perturbation = 1.e-7;
240
241 double f_plus = f_hydset(kinematic_viscosity, known_velocity, wall_distance,
242 friction_velocity_guess + perturbation, roughness_height);
243
244 double f_minus = f_hydset(kinematic_viscosity, known_velocity, wall_distance,
245 friction_velocity_guess - perturbation, roughness_height);
246
247 return (f_plus - f_minus) / (2.0 * perturbation);
248}
249
250/**
251 * @brief Implementation of \ref find_utau_hydset().
252 * @details Full API contract (arguments, ownership, side effects) is documented with
253 * the header declaration in `include/wallfunction.h`.
254 * @see find_utau_hydset()
255 */
256double find_utau_hydset(double kinematic_viscosity, double known_velocity,
257 double wall_distance, double initial_guess,
258 double roughness_height)
259{
260 double friction_velocity = initial_guess;
261 double friction_velocity_old = initial_guess;
262
263 const int max_iterations = 30;
264 const double convergence_tolerance = 1.e-7;
265
266 int iteration;
267 for (iteration = 0; iteration < max_iterations; iteration++) {
268 // Newton-Raphson update
269 double residual = f_hydset(kinematic_viscosity, known_velocity, wall_distance,
270 friction_velocity_old, roughness_height);
271 double derivative = df_hydset(kinematic_viscosity, known_velocity, wall_distance,
272 friction_velocity_old, roughness_height);
273
274 friction_velocity = friction_velocity_old - residual / derivative;
275
276 // Check convergence
277 if (fabs(friction_velocity - friction_velocity_old) < convergence_tolerance) {
278 break;
279 }
280
281 friction_velocity_old = friction_velocity;
282 }
283
284 // Warn if convergence not achieved
285 if (iteration == max_iterations) {
287 "WARNING: u_tau iteration did not converge after %d iterations. "
288 "Final difference: %le\n",
289 iteration, fabs(friction_velocity - friction_velocity_old));
290 }
291
292 return friction_velocity;
293}
294
295// ============================================================================
296// TURBULENT EDDY VISCOSITY MODELS
297// ============================================================================
298
299/**
300 * @brief Internal helper implementation: `nu_t()`.
301 * @details Local to this translation unit.
302 */
303double nu_t(double yplus)
304{
305 // van Driest damping function: [1 - exp(-y+ / A+)]²
306 double damping_function = 1.0 - exp(-yplus / DAMPING_COEFFICIENT);
307
308 // Mixing length model: ν_t / ν = κ * y+ * D²
309 return KAPPA * yplus * damping_function * damping_function;
310}
311
312/**
313 * @brief Implementation of \ref integrate_1().
314 * @details Full API contract (arguments, ownership, side effects) is documented with
315 * the header declaration in `include/wallfunction.h`.
316 * @see integrate_1()
317 */
318double integrate_1(double kinematic_viscosity, double wall_distance,
319 double friction_velocity, int integration_mode)
320{
321 const int num_points = 30;
322 double step_size = wall_distance / (num_points - 1);
323 double integral_values[num_points];
324
325 // Compute integrand at each point
326 for (int i = 0; i < num_points; i++) {
327 double current_distance = i * step_size;
328 double yplus = current_distance * friction_velocity / kinematic_viscosity;
329 double eddy_viscosity_ratio = nu_t(yplus);
330
331 if (integration_mode == 0) {
332 // Mode 0: integrand = 1 / [(1 + ν_t/ν) * ν]
333 integral_values[i] = 1.0 / ((1.0 + eddy_viscosity_ratio) * kinematic_viscosity);
334 }
335 else {
336 // Mode 1: integrand = y / [(1 + ν_t/ν) * ν]
337 integral_values[i] = current_distance /
338 ((1.0 + eddy_viscosity_ratio) * kinematic_viscosity);
339 }
340 }
341
342 // Trapezoidal rule integration with corrected endpoints
343 double integral_sum = 0.0;
344
345 // Interior points: weight = (f[i-1] + 2*f[i] + f[i+1]) * dy/4
346 for (int i = 1; i < num_points - 1; i++) {
347 integral_sum += (integral_values[i-1] + 2.0 * integral_values[i] +
348 integral_values[i+1]) * 0.25 * step_size;
349 }
350
351 // Endpoint correction for higher accuracy
352 integral_sum += 0.1667 * step_size *
353 (2.0 * integral_values[0] + integral_values[1] +
354 2.0 * integral_values[num_points-1] + integral_values[num_points-2]);
355
356 return integral_sum;
357}
358
359// ============================================================================
360// CABOT NON-EQUILIBRIUM WALL FUNCTION
361// ============================================================================
362
363/**
364 * @brief Implementation of \ref taw().
365 * @details Full API contract (arguments, ownership, side effects) is documented with
366 * the header declaration in `include/wallfunction.h`.
367 * @see taw()
368 */
369double taw(double kinematic_viscosity, double friction_velocity,
370 double wall_distance, double velocity, double pressure_gradient_tangent)
371{
372 // Compute integrated viscosity profiles
373 double F1 = integrate_1(kinematic_viscosity, wall_distance, friction_velocity, 0);
374 double Fy = integrate_1(kinematic_viscosity, wall_distance, friction_velocity, 1);
375
376 // Apply momentum balance: τ_w * F1 = u - (dp/dx) * Fy
377 return (1.0 / F1) * (velocity - pressure_gradient_tangent * Fy);
378}
379
380/**
381 * @brief Implementation of \ref u_Cabot().
382 * @details Full API contract (arguments, ownership, side effects) is documented with
383 * the header declaration in `include/wallfunction.h`.
384 * @see u_Cabot()
385 */
386double u_Cabot(double kinematic_viscosity, double wall_distance,
387 double friction_velocity, double pressure_gradient_tangent,
388 double wall_shear_stress)
389{
390 double F1 = integrate_1(kinematic_viscosity, wall_distance, friction_velocity, 0);
391 double Fy = integrate_1(kinematic_viscosity, wall_distance, friction_velocity, 1);
392
393 return wall_shear_stress * F1 + pressure_gradient_tangent * Fy;
394}
395
396/**
397 * @brief Internal helper implementation: `f_Cabot()`.
398 * @details Local to this translation unit.
399 */
400double f_Cabot(double kinematic_viscosity, double velocity, double wall_distance,
401 double friction_velocity_guess, double pressure_gradient_tangent,
402 double pressure_gradient_normal)
403{
404 (void)pressure_gradient_normal;
405 double wall_shear = taw(kinematic_viscosity, friction_velocity_guess,
406 wall_distance, velocity, pressure_gradient_tangent);
407
408 return friction_velocity_guess - sqrt(fabs(wall_shear));
409}
410
411/**
412 * @brief Implementation of \ref df_Cabot().
413 * @details Full API contract (arguments, ownership, side effects) is documented with
414 * the header declaration in `include/wallfunction.h`.
415 * @see df_Cabot()
416 */
417double df_Cabot(double kinematic_viscosity, double velocity, double wall_distance,
418 double friction_velocity_guess, double pressure_gradient_tangent,
419 double pressure_gradient_normal)
420{
421 const double perturbation = 1.e-7;
422
423 double f_plus = f_Cabot(kinematic_viscosity, velocity, wall_distance,
424 friction_velocity_guess + perturbation,
425 pressure_gradient_tangent, pressure_gradient_normal);
426
427 double f_minus = f_Cabot(kinematic_viscosity, velocity, wall_distance,
428 friction_velocity_guess - perturbation,
429 pressure_gradient_tangent, pressure_gradient_normal);
430
431 return (f_plus - f_minus) / (2.0 * perturbation);
432}
433
434/**
435 * @brief Implementation of \ref find_utau_Cabot().
436 * @details Full API contract (arguments, ownership, side effects) is documented with
437 * the header declaration in `include/wallfunction.h`.
438 * @see find_utau_Cabot()
439 */
440void find_utau_Cabot(double kinematic_viscosity, double velocity, double wall_distance,
441 double initial_guess, double pressure_gradient_tangent,
442 double pressure_gradient_normal, double *friction_velocity,
443 double *wall_shear_velocity, double *wall_shear_normal)
444{
445 double current_guess = initial_guess;
446 double new_guess;
447
448 const int max_iterations = 30;
449 const double convergence_tolerance = 1.e-10;
450
451 int iteration;
452 for (iteration = 0; iteration < max_iterations; iteration++) {
453 double residual = f_Cabot(kinematic_viscosity, velocity, wall_distance,
454 current_guess, pressure_gradient_tangent,
455 pressure_gradient_normal);
456
457 double derivative = df_Cabot(kinematic_viscosity, velocity, wall_distance,
458 current_guess, pressure_gradient_tangent,
459 pressure_gradient_normal);
460
461 new_guess = fabs(current_guess - residual / derivative);
462
463 if (fabs(current_guess - new_guess) < convergence_tolerance) {
464 break;
465 }
466
467 current_guess = new_guess;
468 }
469
470 if (fabs(current_guess - new_guess) > 1.e-5 && iteration >= 29) {
472 "Cabot wall-function iteration reached its limit without the requested tolerance.\n");
473 }
474
475 *friction_velocity = new_guess;
476 *wall_shear_velocity = taw(kinematic_viscosity, new_guess, wall_distance,
477 velocity, pressure_gradient_tangent);
478 *wall_shear_normal = taw(kinematic_viscosity, new_guess, wall_distance,
479 0.0, pressure_gradient_normal);
480}
481
482// ============================================================================
483// WERNER-WENGLE ALGEBRAIC WALL FUNCTION
484// ============================================================================
485
486/**
487 * @brief Internal helper implementation: `u_Werner()`.
488 * @details Local to this translation unit.
489 */
490double u_Werner(double kinematic_viscosity, double wall_distance,
491 double friction_velocity)
492{
493 double yplus = friction_velocity * wall_distance / kinematic_viscosity;
494
495 // Werner-Wengle constants
496 const double power_law_coefficient = 8.3;
497 const double power_law_exponent = 1.0 / 7.0;
498
499 double velocity;
500
501 if (yplus <= VISCOUS_SUBLAYER_YPLUS) {
502 // Viscous sublayer: u+ = y+
503 velocity = yplus * friction_velocity;
504 }
505 else {
506 // Power-law region: u+ = A * (y+)^B
507 velocity = power_law_coefficient * pow(yplus, power_law_exponent) * friction_velocity;
508 }
509
510 return velocity;
511}
512
513/**
514 * @brief Internal helper implementation: `f_Werner()`.
515 * @details Local to this translation unit.
516 */
517double f_Werner(double kinematic_viscosity, double velocity,
518 double wall_distance, double friction_velocity)
519{
520 const double power_law_coefficient = 8.3;
521 const double power_law_exponent = 1.0 / 7.0;
522
523 // Transition point between viscous and power-law regions
524 double transition_distance = VISCOUS_SUBLAYER_YPLUS * kinematic_viscosity / friction_velocity;
525
526 // Transition velocity
527 double transition_velocity = kinematic_viscosity / (2.0 * transition_distance) *
528 pow(power_law_coefficient, 2.0 / (1.0 - power_law_exponent));
529
530 double residual;
531
532 if (fabs(velocity) <= transition_velocity) {
533 // Viscous sublayer regime
534 residual = friction_velocity * friction_velocity -
535 velocity / wall_distance * kinematic_viscosity;
536 }
537 else {
538 // Power-law regime (more complex inversion formula)
539 double term1 = 0.5 * (1.0 - power_law_exponent) *
540 pow(power_law_coefficient, (1.0 + power_law_exponent) /
541 (1.0 - power_law_exponent)) *
542 pow(kinematic_viscosity / wall_distance, 1.0 + power_law_exponent);
543
544 double term2 = (1.0 + power_law_exponent) / power_law_coefficient *
545 pow(kinematic_viscosity / wall_distance, power_law_exponent) *
546 fabs(velocity);
547
548 residual = friction_velocity * friction_velocity -
549 velocity / fabs(velocity) * pow(term1 + term2, 2.0 / (1.0 + power_law_exponent));
550 }
551
552 return residual;
553}
554
555/**
556 * @brief Implementation of \ref df_Werner().
557 * @details Full API contract (arguments, ownership, side effects) is documented with
558 * the header declaration in `include/wallfunction.h`.
559 * @see df_Werner()
560 */
561double df_Werner(double kinematic_viscosity, double velocity,
562 double wall_distance, double friction_velocity)
563{
564 const double perturbation = 1.e-7;
565
566 double f_plus = f_Werner(kinematic_viscosity, velocity, wall_distance,
567 friction_velocity + perturbation);
568
569 double f_minus = f_Werner(kinematic_viscosity, velocity, wall_distance,
570 friction_velocity - perturbation);
571
572 return (f_plus - f_minus) / (2.0 * perturbation);
573}
574
575/**
576 * @brief Implementation of \ref find_utau_Werner().
577 * @details Full API contract (arguments, ownership, side effects) is documented with
578 * the header declaration in `include/wallfunction.h`.
579 * @see find_utau_Werner()
580 */
581double find_utau_Werner(double kinematic_viscosity, double velocity,
582 double wall_distance, double initial_guess)
583{
584 double current_guess = initial_guess;
585 double new_guess;
586
587 const int max_iterations = 20;
588 const double convergence_tolerance = 1.e-7;
589
590 int iteration;
591 for (iteration = 0; iteration < max_iterations; iteration++) {
592 double residual = f_Werner(kinematic_viscosity, velocity, wall_distance, current_guess);
593 double derivative = df_Werner(kinematic_viscosity, velocity, wall_distance, current_guess);
594
595 new_guess = current_guess - residual / derivative;
596
597 if (fabs(current_guess - new_guess) < convergence_tolerance) {
598 break;
599 }
600
601 current_guess = new_guess;
602 }
603
604 if (fabs(current_guess - new_guess) > 1.e-5 && iteration >= 19) {
606 "Werner-Wengle wall-function iteration reached its limit without the requested tolerance.\n");
607 }
608
609 return new_guess;
610}
611
612// ============================================================================
613// SIMPLE LOG-LAW WALL FUNCTION (NO ROUGHNESS)
614// ============================================================================
615
616/**
617 * @brief Implementation of \ref u_loglaw().
618 * @details Full API contract (arguments, ownership, side effects) is documented with
619 * the header declaration in `include/wallfunction.h`.
620 * @see u_loglaw()
621 */
622double u_loglaw(double wall_distance, double friction_velocity, double roughness_length)
623{
624 return friction_velocity * (1.0 / KAPPA) * log((roughness_length + wall_distance) / roughness_length);
625}
626
627/**
628 * @brief Internal helper implementation: `find_utau_loglaw()`.
629 * @details Local to this translation unit.
630 */
631double find_utau_loglaw(double velocity, double wall_distance, double roughness_length)
632{
633 return KAPPA * velocity / log((wall_distance + roughness_length) / roughness_length);
634}
635
636// ============================================================================
637// UTILITY FUNCTIONS
638// ============================================================================
639
640// ============================================================================
641// HIGH-LEVEL WALL FUNCTION INTERFACE FUNCTIONS
642// ============================================================================
643
644/**
645 * @brief Implementation of \ref wall_function().
646 * @details Full API contract (arguments, ownership, side effects) is documented with
647 * the header declaration in `include/wallfunction.h`.
648 * @see wall_function()
649 */
650void wall_function(UserCtx *user, double distance_reference, double distance_boundary,
651 Cmpnts velocity_wall, Cmpnts velocity_reference,
652 Cmpnts *velocity_boundary, PetscReal *friction_velocity,
653 double normal_x, double normal_y, double normal_z)
654{
655 SimCtx *simCtx = user->simCtx;
656 double kinematic_viscosity = 1.0 / simCtx->ren;
657
658 // Decompose velocity into components
659 double delta_u = velocity_reference.x - velocity_wall.x;
660 double delta_v = velocity_reference.y - velocity_wall.y;
661 double delta_w = velocity_reference.z - velocity_wall.z;
662
663 double normal_velocity = delta_u * normal_x + delta_v * normal_y + delta_w * normal_z;
664
665 double tangential_u = delta_u - normal_velocity * normal_x;
666 double tangential_v = delta_v - normal_velocity * normal_y;
667 double tangential_w = delta_w - normal_velocity * normal_z;
668
669 double tangential_magnitude = sqrt(tangential_u * tangential_u +
670 tangential_v * tangential_v +
671 tangential_w * tangential_w);
672
673 // Apply Werner-Wengle wall function
674 double utau_guess = 0.05;
675 double tangential_modeled = u_Werner(kinematic_viscosity, distance_boundary, utau_guess);
676 if (friction_velocity) {
677 // Werner-Wengle path currently uses a fixed u_tau estimate.
678 *friction_velocity = (PetscReal)utau_guess;
679 }
680
681 // Scale tangential components
682 if (tangential_magnitude > 1.e-10) {
683 tangential_u *= tangential_modeled / tangential_magnitude;
684 tangential_v *= tangential_modeled / tangential_magnitude;
685 tangential_w *= tangential_modeled / tangential_magnitude;
686 }
687 else {
688 tangential_u = tangential_v = tangential_w = 0.0;
689 }
690
691 // Reconstruct velocity: U = U_t + U_n * n
692 (*velocity_boundary).x = tangential_u + (distance_boundary / distance_reference) * normal_velocity * normal_x;
693 (*velocity_boundary).y = tangential_v + (distance_boundary / distance_reference) * normal_velocity * normal_y;
694 (*velocity_boundary).z = tangential_w + (distance_boundary / distance_reference) * normal_velocity * normal_z;
695
696 // Add wall velocity
697 (*velocity_boundary).x += velocity_wall.x;
698 (*velocity_boundary).y += velocity_wall.y;
699 (*velocity_boundary).z += velocity_wall.z;
700}
701
702/**
703 * @brief Internal helper implementation: `wall_function_loglaw()`.
704 * @details Local to this translation unit.
705 */
706void wall_function_loglaw(UserCtx *user, double roughness_height,
707 double distance_reference, double distance_boundary,
708 Cmpnts velocity_wall, Cmpnts velocity_reference,
709 Cmpnts *velocity_boundary, PetscReal *friction_velocity,
710 double normal_x, double normal_y, double normal_z)
711{
712 SimCtx *simCtx = user->simCtx;
713 double kinematic_viscosity = 1.0 / simCtx->ren;
714
715 // Decompose velocity
716 double delta_u = velocity_reference.x - velocity_wall.x;
717 double delta_v = velocity_reference.y - velocity_wall.y;
718 double delta_w = velocity_reference.z - velocity_wall.z;
719
720 double normal_velocity = delta_u * normal_x + delta_v * normal_y + delta_w * normal_z;
721
722 double tangential_u = delta_u - normal_velocity * normal_x;
723 double tangential_v = delta_v - normal_velocity * normal_y;
724 double tangential_w = delta_w - normal_velocity * normal_z;
725
726 double tangential_magnitude = sqrt(tangential_u * tangential_u +
727 tangential_v * tangential_v +
728 tangential_w * tangential_w);
729
730 // Solve for friction velocity
731 *friction_velocity = find_utau_hydset(kinematic_viscosity, tangential_magnitude,
732 distance_reference, 0.001, roughness_height);
733
734 // Compute wall function velocity
735 double tangential_modeled = u_hydset_roughness(kinematic_viscosity, distance_boundary,
736 *friction_velocity, roughness_height);
737
738 // Check if outside valid range (y+ > 300)
739 if (tangential_modeled < 0.0) {
740 tangential_modeled = tangential_magnitude;
741 }
742
743 // Scale tangential components
744 if (tangential_magnitude > 1.e-10) {
745 tangential_u *= tangential_modeled / tangential_magnitude;
746 tangential_v *= tangential_modeled / tangential_magnitude;
747 tangential_w *= tangential_modeled / tangential_magnitude;
748 }
749 else {
750 tangential_u = tangential_v = tangential_w = 0.0;
751 }
752
753 // Reconstruct total velocity
754 (*velocity_boundary).x = tangential_u + (distance_boundary / distance_reference) * normal_velocity * normal_x;
755 (*velocity_boundary).y = tangential_v + (distance_boundary / distance_reference) * normal_velocity * normal_y;
756 (*velocity_boundary).z = tangential_w + (distance_boundary / distance_reference) * normal_velocity * normal_z;
757
758 // Add wall velocity
759 (*velocity_boundary).x += velocity_wall.x;
760 (*velocity_boundary).y += velocity_wall.y;
761 (*velocity_boundary).z += velocity_wall.z;
762}
763
764/**
765 * @brief Internal helper implementation: `wall_function_Cabot()`.
766 * @details Local to this translation unit.
767 */
768void wall_function_Cabot(UserCtx *user, double roughness_height,
769 double distance_reference, double distance_boundary,
770 Cmpnts velocity_wall, Cmpnts velocity_reference,
771 Cmpnts *velocity_boundary, PetscReal *friction_velocity,
772 double normal_x, double normal_y, double normal_z,
773 double pressure_gradient_x, double pressure_gradient_y,
774 double pressure_gradient_z, int iteration_count)
775{
776 (void)roughness_height;
777 SimCtx *simCtx = user->simCtx;
778 double kinematic_viscosity = 1.0 / simCtx->ren;
779
780 // Decompose velocity
781 double delta_u = velocity_reference.x - velocity_wall.x;
782 double delta_v = velocity_reference.y - velocity_wall.y;
783 double delta_w = velocity_reference.z - velocity_wall.z;
784
785 double normal_velocity = delta_u * normal_x + delta_v * normal_y + delta_w * normal_z;
786
787 double tangential_u = delta_u - normal_velocity * normal_x;
788 double tangential_v = delta_v - normal_velocity * normal_y;
789 double tangential_w = delta_w - normal_velocity * normal_z;
790
791 double tangential_magnitude = sqrt(tangential_u * tangential_u +
792 tangential_v * tangential_v +
793 tangential_w * tangential_w);
794
795 // Compute tangential pressure gradient
796 double pressure_gradient_tangent = 0.0;
797 if (tangential_magnitude > 1.e-10) {
798 pressure_gradient_tangent = (pressure_gradient_x * tangential_u +
799 pressure_gradient_y * tangential_v +
800 pressure_gradient_z * tangential_w) / tangential_magnitude;
801 }
802
803 // Normal pressure gradient (currently set to zero)
804 double pressure_gradient_normal = 0.0;
805
806 // Solve for friction velocity (only on first iteration or periodically)
807 if (iteration_count == 0 || iteration_count > 4) {
808 double utau, wall_shear1, wall_shear2;
809 find_utau_Cabot(kinematic_viscosity, tangential_magnitude, distance_reference,
810 0.01, pressure_gradient_tangent, pressure_gradient_normal,
811 &utau, &wall_shear1, &wall_shear2);
812 *friction_velocity = utau;
813 }
814
815 // Compute wall function velocity with pressure gradient correction
816 double tangential_modeled = u_Cabot(kinematic_viscosity, distance_boundary,
817 *friction_velocity, pressure_gradient_tangent,
818 taw(kinematic_viscosity, *friction_velocity,
819 distance_reference, tangential_magnitude,
820 pressure_gradient_tangent));
821
822 // Scale tangential components
823 if (tangential_magnitude > 1.e-10) {
824 tangential_u *= tangential_modeled / tangential_magnitude;
825 tangential_v *= tangential_modeled / tangential_magnitude;
826 tangential_w *= tangential_modeled / tangential_magnitude;
827 }
828 else {
829 tangential_u = tangential_v = tangential_w = 0.0;
830 }
831
832 // Reconstruct total velocity
833 (*velocity_boundary).x = tangential_u + (distance_boundary / distance_reference) * normal_velocity * normal_x;
834 (*velocity_boundary).y = tangential_v + (distance_boundary / distance_reference) * normal_velocity * normal_y;
835 (*velocity_boundary).z = tangential_w + (distance_boundary / distance_reference) * normal_velocity * normal_z;
836
837 // Add wall velocity
838 (*velocity_boundary).x += velocity_wall.x;
839 (*velocity_boundary).y += velocity_wall.y;
840 (*velocity_boundary).z += velocity_wall.z;
841}
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:45
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:200
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:909
PetscReal ren
Definition variables.h:744
PetscScalar x
Definition variables.h:103
PetscScalar z
Definition variables.h:103
PetscScalar y
Definition variables.h:103
A 3D point or vector with PetscScalar components.
Definition variables.h:102
The master context for the entire simulation.
Definition variables.h:695
User-defined context containing data specific to a single computational grid level.
Definition variables.h:906
double df_Cabot(double kinematic_viscosity, double velocity, double wall_distance, double friction_velocity_guess, double pressure_gradient_tangent, double pressure_gradient_normal)
Implementation of df_Cabot().
double f_Cabot(double kinematic_viscosity, double velocity, double wall_distance, double friction_velocity_guess, double pressure_gradient_tangent, double pressure_gradient_normal)
Internal helper implementation: f_Cabot().
void wall_function_loglaw(UserCtx *user, double roughness_height, double distance_reference, double distance_boundary, Cmpnts velocity_wall, Cmpnts velocity_reference, Cmpnts *velocity_boundary, PetscReal *friction_velocity, double normal_x, double normal_y, double normal_z)
Internal helper implementation: wall_function_loglaw().
double find_utau_loglaw(double velocity, double wall_distance, double roughness_length)
Internal helper implementation: find_utau_loglaw().
double u_Werner(double kinematic_viscosity, double wall_distance, double friction_velocity)
Internal helper implementation: u_Werner().
double u_hydset_roughness(double kinematic_viscosity, double wall_distance, double friction_velocity, double roughness_height)
Internal helper implementation: u_hydset_roughness().
void wall_function(UserCtx *user, double distance_reference, double distance_boundary, Cmpnts velocity_wall, Cmpnts velocity_reference, Cmpnts *velocity_boundary, PetscReal *friction_velocity, double normal_x, double normal_y, double normal_z)
Implementation of wall_function().
#define DAMPING_COEFFICIENT
Eddy viscosity damping coefficient (van Driest damping)
double taw(double kinematic_viscosity, double friction_velocity, double wall_distance, double velocity, double pressure_gradient_tangent)
Implementation of taw().
double find_utau_hydset(double kinematic_viscosity, double known_velocity, double wall_distance, double initial_guess, double roughness_height)
Implementation of find_utau_hydset().
double u_Cabot(double kinematic_viscosity, double wall_distance, double friction_velocity, double pressure_gradient_tangent, double wall_shear_stress)
Implementation of u_Cabot().
#define ROUGHNESS_TRANSITION_YPLUS
Smooth-to-rough transition y+ threshold.
double df_Werner(double kinematic_viscosity, double velocity, double wall_distance, double friction_velocity)
Implementation of df_Werner().
double f_Werner(double kinematic_viscosity, double velocity, double wall_distance, double friction_velocity)
Internal helper implementation: f_Werner().
#define VISCOUS_SUBLAYER_YPLUS
Viscous sublayer thickness y+ threshold.
double u_loglaw(double wall_distance, double friction_velocity, double roughness_length)
Implementation of u_loglaw().
void wall_function_Cabot(UserCtx *user, double roughness_height, double distance_reference, double distance_boundary, Cmpnts velocity_wall, Cmpnts velocity_reference, Cmpnts *velocity_boundary, PetscReal *friction_velocity, double normal_x, double normal_y, double normal_z, double pressure_gradient_x, double pressure_gradient_y, double pressure_gradient_z, int iteration_count)
Internal helper implementation: wall_function_Cabot().
#define LOGLAW_B
Log-law intercept constant B for smooth walls.
void noslip(UserCtx *user, double distance_reference, double distance_boundary, Cmpnts velocity_wall, Cmpnts velocity_reference, Cmpnts *velocity_boundary, double normal_x, double normal_y, double normal_z)
Internal helper implementation: noslip().
void find_utau_Cabot(double kinematic_viscosity, double velocity, double wall_distance, double initial_guess, double pressure_gradient_tangent, double pressure_gradient_normal, double *friction_velocity, double *wall_shear_velocity, double *wall_shear_normal)
Implementation of find_utau_Cabot().
double integrate_1(double kinematic_viscosity, double wall_distance, double friction_velocity, int integration_mode)
Implementation of integrate_1().
#define FULLY_ROUGH_YPLUS
Fully rough regime y+ threshold.
double f_hydset(double kinematic_viscosity, double known_velocity, double wall_distance, double friction_velocity_guess, double roughness_height)
Internal helper implementation: f_hydset().
double E_coeff(double friction_velocity, double roughness_height, double kinematic_viscosity)
Internal helper implementation: E_coeff().
#define KAPPA
von Karman constant (universal turbulence constant)
double nu_t(double yplus)
Internal helper implementation: nu_t().
double df_hydset(double kinematic_viscosity, double known_velocity, double wall_distance, double friction_velocity_guess, double roughness_height)
Implementation of df_hydset().
double find_utau_Werner(double kinematic_viscosity, double velocity, double wall_distance, double initial_guess)
Implementation of find_utau_Werner().
void freeslip(UserCtx *user, double distance_reference, double distance_boundary, Cmpnts velocity_wall, Cmpnts velocity_reference, Cmpnts *velocity_boundary, double normal_x, double normal_y, double normal_z)
Internal helper implementation: freeslip().