flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_ibm_polynomials.py
Go to the documentation of this file.
1import numpy as np
2
3def poly_D(xi):
4 return xi * (1.0 + xi)
5
6def poly_N_c(xi):
7 return 2.0 * (xi**2 - 1.0)
8
9def poly_N_nb(xi):
10 return xi * (1.0 - xi)
11
12def poly_N_bc(xi):
13 return 2.0
14
16 """
17 Verify 1D IBM Operator consistency with second derivative.
18
19 Setup:
20 - Domain: x in [0, 1]
21 - Grid: N cells, dx = 1/N
22 - Boundary: Located at x_b = (i_ghost + xi) * dx
23 - Function: u(x) = (x - x_b)^2
24 u(x_b) = 0
25 u'(x) = 2(x - x_b)
26 u''(x) = 2
27
28 Discrete Operator L(u) should equal d^2u/dx^2 = 2.
29 L(u)_c = (A_c * u_c + A_nb * u_nb + B_rhs) / D_scale
30
31 Wait, in our code:
32 A_c_mod = D*A_c + K*A_g
33 A_nb_mod = D*A_nb + M*A_g
34 B_mod = D*B + B_fac
35
36 Standard Stencil (Diffusion):
37 A_c = -2/dx^2, A_nb = 1/dx^2, A_g = 1/dx^2
38 B = 0
39
40 Modified Stencil:
41 A_c' = D*(-2) + N_c*(1)
42 A_nb' = D*(1) + N_nb*(1) <- (Here neighbor is fluid, ghost is on other side)
43 So if ghost is West:
44 A_E' = D*(1) + N_E*(1) = D + 0 (since N_E applies to ghost dir)
45 Wait, N_nb in code applies to the FLUID neighbor in the interpolation stencil?
46 Table 1 says "N_{nb,d} applies to the fluid neighbor used in interpolation".
47 Interpolation uses: Ghost(at -1), Center(0), Neighbor(1).
48 So N_nb applies to Neighbor at +1.
49
50 So:
51 A_E' = D * A_E + N_nb * A_W (Cross term? No.)
52 Let's look at code:
53 modify_stencil_ibm_kernel:
54 A_c += A_nb * K (A_nb is the coeff OF the ghost direction in standard stencil)
55
56 Example: Ghost is West (index 1 in loop).
57 orig_AW is the coefficient for u_W in standard stencil.
58 K = N_c * R (R=1 for 1D)
59 M = 0
60 X = N_nb * R
61
62 Update:
63 A_C += orig_AW * K = -2*D + 1*N_c
64
65 Neighbor updates:
66 mod_AW += orig_AW * (D*M - 1) = 1 * (0 - 1) = -1.
67 So A_W becomes 1 + (-1) = 0. Correct.
68
69 mod_AE += orig_AE * X = 1 * N_nb = N_nb.
70 So A_E becomes D*1 + N_nb.
71
72 So Discrete Eq:
73 (A_C' * u_C + A_E' * u_E) * (1/dx^2) / D_scale
74 = [ (-2D + N_c)*u_C + (D + N_nb)*u_E ] / D
75
76 RHS correction:
77 B_val for West ghost:
78 Code says B = 0 if is_ghost is true?
79 Wait, in `compute_ibm_geometry_kernel`:
80 if (is_ghost[kp]) {
81 K = N_c * R
82 X = N_nb * R
83 B = 0
84 }
85 Where is u_bc handled?
86 Ah, `compute_ibm_geometry_kernel` assumes B_val is 0 in the code shown in `src/cfd_solver_ibm.cu`?
87 Let's check lines 220-230.
88
89 Line 226: ibm_data.B_val[...] = 0.0f;
90
91 This looks suspicious! The text says N_bc * u_bc.
92 The code seems to set B_val = 0.
93
94 Let's verify if u_bc is handled elsewhere or if this is the Bug.
95 If u_bc = 0 (Homogeneous Dirichlet), then B=0 is correct.
96 Our verification case uses u=0 at boundary, so B=0 is fine for now.
97
98 Test for u_bc = 0.
99 """
100
101 print("Testing 1D Operator Consistency (u_bc = 0)...")
102
103 # Arbitrary theta (0 < theta <= 1)
104 # distance = theta * dx
105 thetas = [0.1, 0.3, 0.5, 0.8, 0.99]
106
107 for theta in thetas:
108 # Polynomials
109 D = poly_D(theta)
110 Nc = poly_N_c(theta)
111 Nnb = poly_N_nb(theta)
112
113 # Standard Stencil Coeffs (scaled by dx^2)
114 # u'' = (u_E - 2u_C + u_W) / dx^2
115 a_W = 1.0
116 a_C = -2.0
117 a_E = 1.0
118
119 # Modified Stencil (Ghost is West)
120 # a_C' = D*a_C + Nc*a_W = -2D + Nc
121 # a_W' = 0
122 # a_E' = D*a_E + Nnb*a_W = D + Nnb
123
124 # Note: In code `mod_AE += orig_AE * (descale * M - 1.0f)`?
125 # No, for East (k=0), M=1, X=0 (if fluid).
126 # Wait, if West is ghost, East is fluid.
127 # For East direction (k=0): is_ghost=False -> K=0, M=1, X=0.
128 # mod_AE += orig_AE * (D*1 - 1) -> A_E becomes D*A_E.
129 #
130 # For West direction (k=1): is_ghost=True -> K=Nc, M=0, X=Nnb.
131 # A_C += orig_AW * Nc
132 # mod_AW += orig_AW * (D*0 - 1) = -orig_AW -> A_W = 0.
133 # mod_AE += orig_AW * X = 1 * Nnb.
134 #
135 # Total A_E = D*A_E + Nnb*A_W = D + Nnb.
136 # Total A_C = D*A_C + Nc*A_W = -2D + Nc.
137
138 a_C_mod = -2.0 * D + Nc
139 a_E_mod = D + Nnb
140
141 # Test Field: u(x) = x^2 (Parabola with vertex at 0)
142 # Shift so boundary is at x = -theta.
143 # Center at x=0. East at x=1. West at x=-1.
144 # Boundary at x_b = -theta.
145 # u(x) = (x - x_b)^2 = (x + theta)^2
146 # u_C = (0 + theta)^2 = theta^2
147 # u_E = (1 + theta)^2 = 1 + 2theta + theta^2
148 # u_bc = 0
149
150 u_C = theta**2
151 u_E = (1.0 + theta)**2
152
153 # Apply Operator
154 # L(u) = (a_C' * u_C + a_E' * u_E) / D
155
156 val = (a_C_mod * u_C + a_E_mod * u_E) / D
157
158 # Expected Second Derivative: d2/dx2 (x+theta)^2 = 2
159 expected = 2.0
160
161 print(f"Theta={theta:.2f}: L(u)={val:.6f}, Expected={expected:.6f}, Diff={val-expected:.6e}")
162
163 if abs(val - expected) > 1e-5:
164 print(" FAIL: Operator inconsistent")
165 return False
166
167 print("PASS: 1D Operator Consistent for Quadratic Profile")
168 return True
169
170if __name__ == "__main__":