Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ok-landscape
GitHub Repository: Ok-landscape/computational-pipeline
Path: blob/main/latex-templates/templates/fluid-dynamics/navier_stokes.tex
51 views
unlisted
1
\documentclass[a4paper, 11pt]{article}
2
\usepackage[utf8]{inputenc}
3
\usepackage[T1]{fontenc}
4
\usepackage{amsmath, amssymb, amsthm}
5
\usepackage{graphicx}
6
\usepackage{siunitx}
7
\usepackage{booktabs}
8
\usepackage{float}
9
\usepackage{geometry}
10
\geometry{margin=1in}
11
\usepackage[makestderr]{pythontex}
12
13
\newtheorem{theorem}{Theorem}[section]
14
\newtheorem{definition}[theorem]{Definition}
15
16
\title{Navier-Stokes Equations: Viscous Flow Analysis\\and Boundary Layer Theory}
17
\author{Fluid Mechanics Research Laboratory}
18
\date{\today}
19
20
\begin{document}
21
\maketitle
22
23
\begin{abstract}
24
This technical report presents analytical and computational solutions to the Navier-Stokes equations for canonical viscous flow problems. We analyze Couette flow, Poiseuille flow, and boundary layer development using Python-based numerical methods. Results include velocity profiles, shear stress distributions, and Reynolds number effects on flow characteristics.
25
\end{abstract}
26
27
\section{Introduction}
28
29
The Navier-Stokes equations govern the motion of viscous fluids and form the foundation of fluid mechanics:
30
\begin{equation}
31
\rho\left(\frac{\partial \mathbf{u}}{\partial t} + \mathbf{u} \cdot \nabla \mathbf{u}\right) = -\nabla p + \mu \nabla^2 \mathbf{u} + \rho \mathbf{g}
32
\end{equation}
33
34
\begin{definition}[Reynolds Number]
35
The Reynolds number characterizes the ratio of inertial to viscous forces:
36
\begin{equation}
37
Re = \frac{\rho U L}{\mu} = \frac{U L}{\nu}
38
\end{equation}
39
\end{definition}
40
41
\section{Computational Setup}
42
43
\begin{pycode}
44
import numpy as np
45
import matplotlib.pyplot as plt
46
from scipy.integrate import odeint, solve_bvp
47
from scipy.special import erfc
48
49
plt.rc('text', usetex=True)
50
plt.rc('font', family='serif', size=9)
51
np.random.seed(42)
52
53
# Fluid properties (water at 20C)
54
rho = 998.0 # kg/m^3
55
mu = 1.002e-3 # Pa.s
56
nu = mu / rho # m^2/s
57
58
# Geometric parameters
59
H = 0.01 # Channel height (m)
60
L = 0.1 # Channel length (m)
61
\end{pycode}
62
63
\noindent\textbf{Fluid Properties (Water at 20$^\circ$C):}
64
\begin{itemize}
65
\item Density: $\rho = \py{rho}$ kg/m$^3$
66
\item Dynamic Viscosity: $\mu = \py{f"{mu*1000:.3f}"}$ mPa$\cdot$s
67
\item Kinematic Viscosity: $\nu = \py{f"{nu*1e6:.3f}"}$ mm$^2$/s
68
\end{itemize}
69
70
\section{Couette Flow Analysis}
71
72
Couette flow occurs between two parallel plates where one plate moves relative to the other.
73
74
\begin{pycode}
75
# Couette flow: upper plate moving at U_wall
76
U_wall = 0.1 # m/s
77
y = np.linspace(0, H, 100)
78
79
# Velocity profile (linear)
80
u_couette = U_wall * y / H
81
82
# With pressure gradient (generalized Couette)
83
dpdx_values = [-1000, 0, 1000] # Pa/m
84
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
85
86
for dpdx in dpdx_values:
87
u_gen = U_wall * y/H - (1/(2*mu)) * dpdx * y * (H - y)
88
label = f'dp/dx = {dpdx} Pa/m'
89
axes[0, 0].plot(u_gen*1000, y*1000, linewidth=1.5, label=label)
90
91
axes[0, 0].set_xlabel('Velocity (mm/s)')
92
axes[0, 0].set_ylabel('y (mm)')
93
axes[0, 0].set_title('Generalized Couette Flow')
94
axes[0, 0].legend(loc='upper left', fontsize=8)
95
axes[0, 0].grid(True, alpha=0.3)
96
97
# Shear stress distribution
98
tau_couette = mu * U_wall / H
99
y_stress = np.linspace(0, H, 100)
100
tau_wall = mu * U_wall / H * np.ones_like(y_stress)
101
102
# With pressure gradient
103
for dpdx in dpdx_values:
104
du_dy = U_wall/H - (1/(2*mu)) * dpdx * (H - 2*y_stress)
105
tau = mu * du_dy
106
axes[0, 1].plot(tau, y_stress*1000, linewidth=1.5)
107
108
axes[0, 1].set_xlabel('Shear Stress (Pa)')
109
axes[0, 1].set_ylabel('y (mm)')
110
axes[0, 1].set_title('Shear Stress Distribution')
111
axes[0, 1].grid(True, alpha=0.3)
112
113
# Time-dependent startup (Stokes first problem)
114
t_values = [0.01, 0.05, 0.1, 0.5, 1.0]
115
y_stokes = np.linspace(0, 5*H, 200)
116
117
for t in t_values:
118
eta = y_stokes / (2 * np.sqrt(nu * t))
119
u_stokes = U_wall * erfc(eta)
120
axes[1, 0].plot(u_stokes*1000, y_stokes*1000, linewidth=1.5,
121
label=f't = {t} s')
122
123
axes[1, 0].set_xlabel('Velocity (mm/s)')
124
axes[1, 0].set_ylabel('y (mm)')
125
axes[1, 0].set_title("Stokes' First Problem (Impulsive Start)")
126
axes[1, 0].legend(loc='upper right', fontsize=8)
127
axes[1, 0].grid(True, alpha=0.3)
128
129
# Boundary layer thickness vs time
130
t_range = np.linspace(0.001, 2, 100)
131
delta_99 = 3.6 * np.sqrt(nu * t_range)
132
133
axes[1, 1].plot(t_range, delta_99*1000, 'b-', linewidth=1.5)
134
axes[1, 1].set_xlabel('Time (s)')
135
axes[1, 1].set_ylabel('$\\delta_{99}$ (mm)')
136
axes[1, 1].set_title('Boundary Layer Growth')
137
axes[1, 1].grid(True, alpha=0.3)
138
139
plt.tight_layout()
140
plt.savefig('navier_stokes_plot1.pdf', bbox_inches='tight', dpi=150)
141
plt.close()
142
143
# Calculate key parameters
144
Re_couette = rho * U_wall * H / mu
145
tau_wall_couette = mu * U_wall / H
146
\end{pycode}
147
148
\begin{figure}[H]
149
\centering
150
\includegraphics[width=0.95\textwidth]{navier_stokes_plot1.pdf}
151
\caption{Couette flow analysis: velocity profiles, shear stress, and transient development.}
152
\end{figure}
153
154
\begin{table}[H]
155
\centering
156
\caption{Couette Flow Parameters}
157
\begin{tabular}{lcc}
158
\toprule
159
\textbf{Parameter} & \textbf{Value} & \textbf{Unit} \\
160
\midrule
161
Wall Velocity & \py{f"{U_wall*1000:.1f}"} & mm/s \\
162
Channel Height & \py{f"{H*1000:.1f}"} & mm \\
163
Reynolds Number & \py{f"{Re_couette:.2f}"} & -- \\
164
Wall Shear Stress & \py{f"{tau_wall_couette:.4f}"} & Pa \\
165
\bottomrule
166
\end{tabular}
167
\end{table}
168
169
\section{Poiseuille Flow (Pressure-Driven)}
170
171
\begin{theorem}[Hagen-Poiseuille Equation]
172
For laminar flow in a circular pipe, the volumetric flow rate is:
173
\begin{equation}
174
Q = \frac{\pi R^4}{8\mu}\left(-\frac{dp}{dx}\right)
175
\end{equation}
176
\end{theorem}
177
178
\begin{pycode}
179
# Channel Poiseuille flow
180
dpdx = -1000 # Pa/m (negative for flow in +x direction)
181
182
# Velocity profile
183
u_max = -(dpdx * H**2) / (8 * mu)
184
u_poiseuille = -(1/(2*mu)) * dpdx * y * (H - y)
185
u_avg = (2/3) * u_max
186
187
# Pipe flow
188
R_pipe = H/2
189
r = np.linspace(-R_pipe, R_pipe, 100)
190
u_pipe = -(1/(4*mu)) * dpdx * (R_pipe**2 - r**2)
191
u_max_pipe = -(dpdx * R_pipe**2) / (4 * mu)
192
193
# Flow rate calculations
194
Q_channel = (H**3 * (-dpdx)) / (12 * mu) # per unit width
195
Q_pipe = (np.pi * R_pipe**4 * (-dpdx)) / (8 * mu)
196
197
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
198
199
# Channel velocity profile
200
axes[0, 0].plot(u_poiseuille*1000, y*1000, 'b-', linewidth=1.5, label='Velocity')
201
axes[0, 0].axhline(y=H*1000/2, color='r', linestyle='--', alpha=0.7)
202
axes[0, 0].axvline(x=u_max*1000, color='g', linestyle=':', alpha=0.7, label='$u_{max}$')
203
axes[0, 0].set_xlabel('Velocity (mm/s)')
204
axes[0, 0].set_ylabel('y (mm)')
205
axes[0, 0].set_title('Channel Poiseuille Flow')
206
axes[0, 0].legend(loc='upper right', fontsize=8)
207
axes[0, 0].grid(True, alpha=0.3)
208
209
# Pipe velocity profile
210
axes[0, 1].plot(u_pipe*1000, r*1000, 'b-', linewidth=1.5)
211
axes[0, 1].axhline(y=0, color='r', linestyle='--', alpha=0.7)
212
axes[0, 1].set_xlabel('Velocity (mm/s)')
213
axes[0, 1].set_ylabel('r (mm)')
214
axes[0, 1].set_title('Pipe Poiseuille Flow')
215
axes[0, 1].grid(True, alpha=0.3)
216
217
# Entrance length development
218
Re_pipe = rho * (u_avg * 2) * (2*R_pipe) / mu
219
Le_laminar = 0.06 * Re_pipe * (2*R_pipe)
220
221
x_entrance = np.linspace(0, Le_laminar*1.5, 100)
222
# Developing velocity profiles at different x locations
223
x_positions = [0.1*Le_laminar, 0.3*Le_laminar, 0.6*Le_laminar, Le_laminar]
224
r_dev = np.linspace(0, R_pipe, 50)
225
226
for i, x_pos in enumerate(x_positions):
227
# Approximate developing profile
228
alpha = min(1, x_pos/Le_laminar)
229
u_dev = u_avg * (1 + alpha * (2*(1-(r_dev/R_pipe)**2) - 1))
230
axes[1, 0].plot(u_dev*1000, r_dev*1000, linewidth=1.5,
231
label=f'x/Le = {x_pos/Le_laminar:.1f}')
232
233
axes[1, 0].set_xlabel('Velocity (mm/s)')
234
axes[1, 0].set_ylabel('r (mm)')
235
axes[1, 0].set_title('Developing Flow in Entrance Region')
236
axes[1, 0].legend(loc='upper right', fontsize=8)
237
axes[1, 0].grid(True, alpha=0.3)
238
239
# Reynolds number effect on velocity profile shape
240
Re_values = [100, 500, 1000, 2000]
241
for Re in Re_values:
242
U_Re = Re * nu / (2*R_pipe)
243
dpdx_Re = -8 * mu * U_Re / R_pipe**2
244
u_Re = -(1/(4*mu)) * dpdx_Re * (R_pipe**2 - r**2)
245
axes[1, 1].plot(u_Re/U_Re, r/R_pipe, linewidth=1.5, label=f'Re = {Re}')
246
247
axes[1, 1].set_xlabel('$u/U_{avg}$')
248
axes[1, 1].set_ylabel('$r/R$')
249
axes[1, 1].set_title('Normalized Velocity Profiles')
250
axes[1, 1].legend(loc='upper right', fontsize=8)
251
axes[1, 1].grid(True, alpha=0.3)
252
253
plt.tight_layout()
254
plt.savefig('navier_stokes_plot2.pdf', bbox_inches='tight', dpi=150)
255
plt.close()
256
\end{pycode}
257
258
\begin{figure}[H]
259
\centering
260
\includegraphics[width=0.95\textwidth]{navier_stokes_plot2.pdf}
261
\caption{Poiseuille flow analysis: velocity profiles and entrance region development.}
262
\end{figure}
263
264
\begin{table}[H]
265
\centering
266
\caption{Poiseuille Flow Results}
267
\begin{tabular}{lcc}
268
\toprule
269
\textbf{Parameter} & \textbf{Value} & \textbf{Unit} \\
270
\midrule
271
Maximum Velocity & \py{f"{u_max*1000:.2f}"} & mm/s \\
272
Average Velocity & \py{f"{u_avg*1000:.2f}"} & mm/s \\
273
Flow Rate (channel) & \py{f"{Q_channel*1e6:.3f}"} & mm$^2$/s \\
274
Reynolds Number & \py{f"{Re_pipe:.1f}"} & -- \\
275
Entrance Length & \py{f"{Le_laminar*1000:.1f}"} & mm \\
276
\bottomrule
277
\end{tabular}
278
\end{table}
279
280
\section{Boundary Layer Analysis}
281
282
\begin{pycode}
283
# Blasius boundary layer solution
284
# f''' + 0.5*f*f'' = 0 with f(0)=f'(0)=0, f'(inf)=1
285
286
def blasius_ode(eta, f):
287
return [f[1], f[2], -0.5*f[0]*f[2]]
288
289
# Shooting method to find correct f''(0)
290
from scipy.optimize import brentq
291
292
def shoot_blasius(f2_0):
293
eta_span = [0, 10]
294
eta_eval = np.linspace(0, 10, 500)
295
f0 = [0, 0, f2_0]
296
sol = odeint(blasius_ode, f0, eta_eval)
297
return sol[-1, 1] - 1 # f'(inf) should be 1
298
299
# Use bisection to find correct f''(0)
300
f2_0_correct = brentq(shoot_blasius, 0.1, 0.5)
301
302
# Solve with correct initial condition
303
eta = np.linspace(0, 8, 500)
304
f0 = [0, 0, f2_0_correct]
305
sol = odeint(blasius_ode, f0, eta)
306
307
f = sol[:, 0]
308
f_prime = sol[:, 1] # u/U_inf
309
f_double_prime = sol[:, 2]
310
311
# Boundary layer parameters
312
U_inf = 1.0 # Free stream velocity (m/s)
313
x_values = np.array([0.01, 0.05, 0.1, 0.2, 0.5])
314
315
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
316
317
# Blasius velocity profile
318
axes[0, 0].plot(f_prime, eta, 'b-', linewidth=1.5)
319
axes[0, 0].axhline(y=5.0, color='r', linestyle='--', alpha=0.7, label='$\\eta = 5$')
320
axes[0, 0].axvline(x=0.99, color='g', linestyle=':', alpha=0.7, label='$u/U_\\infty = 0.99$')
321
axes[0, 0].set_xlabel('$u/U_\\infty$')
322
axes[0, 0].set_ylabel('$\\eta = y\\sqrt{U_\\infty/\\nu x}$')
323
axes[0, 0].set_title('Blasius Velocity Profile')
324
axes[0, 0].legend(loc='lower right', fontsize=8)
325
axes[0, 0].grid(True, alpha=0.3)
326
327
# Boundary layer thickness along plate
328
Re_x = U_inf * x_values / nu
329
delta = 5.0 * x_values / np.sqrt(Re_x)
330
delta_star = 1.72 * x_values / np.sqrt(Re_x)
331
theta = 0.664 * x_values / np.sqrt(Re_x)
332
333
axes[0, 1].plot(x_values*1000, delta*1000, 'b-', linewidth=1.5, label='$\\delta$')
334
axes[0, 1].plot(x_values*1000, delta_star*1000, 'r--', linewidth=1.5, label='$\\delta^*$')
335
axes[0, 1].plot(x_values*1000, theta*1000, 'g:', linewidth=1.5, label='$\\theta$')
336
axes[0, 1].set_xlabel('x (mm)')
337
axes[0, 1].set_ylabel('Thickness (mm)')
338
axes[0, 1].set_title('Boundary Layer Thickness Growth')
339
axes[0, 1].legend(loc='upper left', fontsize=8)
340
axes[0, 1].grid(True, alpha=0.3)
341
342
# Skin friction coefficient
343
Cf = 0.664 / np.sqrt(Re_x)
344
axes[1, 0].loglog(Re_x, Cf, 'b-', linewidth=1.5, label='Laminar (Blasius)')
345
# Turbulent comparison
346
Re_x_turb = np.logspace(5, 7, 100)
347
Cf_turb = 0.027 / Re_x_turb**(1/7)
348
axes[1, 0].loglog(Re_x_turb, Cf_turb, 'r--', linewidth=1.5, label='Turbulent')
349
axes[1, 0].set_xlabel('$Re_x$')
350
axes[1, 0].set_ylabel('$C_f$')
351
axes[1, 0].set_title('Skin Friction Coefficient')
352
axes[1, 0].legend(loc='upper right', fontsize=8)
353
axes[1, 0].grid(True, which='both', alpha=0.3)
354
355
# Wall shear stress
356
tau_w = 0.332 * rho * U_inf**2 / np.sqrt(Re_x)
357
axes[1, 1].plot(x_values*1000, tau_w, 'b-', linewidth=1.5, marker='o')
358
axes[1, 1].set_xlabel('x (mm)')
359
axes[1, 1].set_ylabel('$\\tau_w$ (Pa)')
360
axes[1, 1].set_title('Wall Shear Stress Distribution')
361
axes[1, 1].grid(True, alpha=0.3)
362
363
plt.tight_layout()
364
plt.savefig('navier_stokes_plot3.pdf', bbox_inches='tight', dpi=150)
365
plt.close()
366
367
# Calculate shape factor
368
H_factor = delta_star / theta
369
\end{pycode}
370
371
\begin{figure}[H]
372
\centering
373
\includegraphics[width=0.95\textwidth]{navier_stokes_plot3.pdf}
374
\caption{Blasius boundary layer solution: velocity profile, thickness growth, and skin friction.}
375
\end{figure}
376
377
\section{Reynolds Number Effects}
378
379
\begin{pycode}
380
# Reynolds number effects on flow characteristics
381
Re_range = np.logspace(0, 4, 100)
382
383
# Critical Reynolds numbers
384
Re_crit_pipe = 2300
385
Re_crit_plate = 5e5
386
387
# Friction factor for pipe flow
388
def friction_factor(Re):
389
if Re < 2300:
390
return 64 / Re
391
else:
392
return 0.316 / Re**0.25
393
394
f_factor = np.array([friction_factor(Re) for Re in Re_range])
395
396
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
397
398
# Friction factor vs Re
399
axes[0, 0].loglog(Re_range, f_factor, 'b-', linewidth=1.5)
400
axes[0, 0].axvline(x=Re_crit_pipe, color='r', linestyle='--', alpha=0.7, label='Transition')
401
axes[0, 0].set_xlabel('Re')
402
axes[0, 0].set_ylabel('Friction Factor $f$')
403
axes[0, 0].set_title('Moody Diagram (Smooth Pipe)')
404
axes[0, 0].legend(loc='upper right', fontsize=8)
405
axes[0, 0].grid(True, which='both', alpha=0.3)
406
407
# Velocity profiles at different Re
408
y_norm = np.linspace(0, 1, 100)
409
for Re in [100, 1000, 5000]:
410
if Re < 2300:
411
u_norm = 1.5 * (1 - (2*y_norm - 1)**2)
412
else:
413
n = 7
414
u_norm = (y_norm)**(1/n)
415
axes[0, 1].plot(u_norm, y_norm, linewidth=1.5, label=f'Re = {Re}')
416
417
axes[0, 1].set_xlabel('$u/u_{max}$')
418
axes[0, 1].set_ylabel('$y/H$')
419
axes[0, 1].set_title('Velocity Profile Shape vs Re')
420
axes[0, 1].legend(loc='lower right', fontsize=8)
421
axes[0, 1].grid(True, alpha=0.3)
422
423
# Pressure drop vs flow rate
424
Q_range = np.linspace(1e-6, 1e-4, 100)
425
D_pipe = 0.01
426
L_pipe = 1.0
427
428
def pressure_drop(Q, D, L_p):
429
A = np.pi * D**2 / 4
430
V = Q / A
431
Re = rho * V * D / mu
432
f = friction_factor(Re)
433
return f * (L_p/D) * (rho * V**2 / 2)
434
435
dp_range = np.array([pressure_drop(Q, D_pipe, L_pipe) for Q in Q_range])
436
437
axes[1, 0].plot(Q_range*1e6, dp_range/1000, 'b-', linewidth=1.5)
438
axes[1, 0].set_xlabel('Flow Rate (mL/s)')
439
axes[1, 0].set_ylabel('Pressure Drop (kPa)')
440
axes[1, 0].set_title('Pressure Drop vs Flow Rate')
441
axes[1, 0].grid(True, alpha=0.3)
442
443
# Entrance length
444
Re_entrance = np.logspace(1, 4, 100)
445
Le_lam = 0.06 * Re_entrance * D_pipe
446
Le_turb = 4.4 * Re_entrance**(1/6) * D_pipe
447
448
axes[1, 1].loglog(Re_entrance, Le_lam*1000, 'b-', linewidth=1.5, label='Laminar')
449
axes[1, 1].loglog(Re_entrance[Re_entrance>2300], Le_turb[Re_entrance>2300]*1000,
450
'r--', linewidth=1.5, label='Turbulent')
451
axes[1, 1].axvline(x=2300, color='g', linestyle=':', alpha=0.7)
452
axes[1, 1].set_xlabel('Re')
453
axes[1, 1].set_ylabel('Entrance Length (mm)')
454
axes[1, 1].set_title('Entrance Length vs Reynolds Number')
455
axes[1, 1].legend(loc='upper left', fontsize=8)
456
axes[1, 1].grid(True, which='both', alpha=0.3)
457
458
plt.tight_layout()
459
plt.savefig('navier_stokes_plot4.pdf', bbox_inches='tight', dpi=150)
460
plt.close()
461
\end{pycode}
462
463
\begin{figure}[H]
464
\centering
465
\includegraphics[width=0.95\textwidth]{navier_stokes_plot4.pdf}
466
\caption{Reynolds number effects on flow characteristics.}
467
\end{figure}
468
469
\section{Vorticity and Stream Function}
470
471
\begin{pycode}
472
# 2D flow visualization using stream function
473
N = 50
474
x_grid = np.linspace(0, L, N)
475
y_grid = np.linspace(0, H, N)
476
X, Y = np.meshgrid(x_grid, y_grid)
477
478
# Analytical solution for Stokes flow in cavity (first mode)
479
psi = np.sin(np.pi * X / L) * np.sinh(np.pi * Y / L)
480
481
# Velocity components
482
u_field = np.pi/L * np.sin(np.pi * X / L) * np.cosh(np.pi * Y / L)
483
v_field = -np.pi/L * np.cos(np.pi * X / L) * np.sinh(np.pi * Y / L)
484
485
# Vorticity
486
omega_field = np.gradient(v_field, x_grid, axis=1) - np.gradient(u_field, y_grid, axis=0)
487
488
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
489
490
# Stream function contours
491
cs1 = axes[0, 0].contour(X*1000, Y*1000, psi, levels=20, cmap='coolwarm')
492
axes[0, 0].set_xlabel('x (mm)')
493
axes[0, 0].set_ylabel('y (mm)')
494
axes[0, 0].set_title('Stream Function $\\psi$')
495
axes[0, 0].set_aspect('equal')
496
plt.colorbar(cs1, ax=axes[0, 0])
497
498
# Velocity magnitude
499
vel_mag = np.sqrt(u_field**2 + v_field**2)
500
cs2 = axes[0, 1].contourf(X*1000, Y*1000, vel_mag, levels=20, cmap='viridis')
501
axes[0, 1].set_xlabel('x (mm)')
502
axes[0, 1].set_ylabel('y (mm)')
503
axes[0, 1].set_title('Velocity Magnitude')
504
axes[0, 1].set_aspect('equal')
505
plt.colorbar(cs2, ax=axes[0, 1])
506
507
# Vorticity field
508
cs3 = axes[1, 0].contourf(X*1000, Y*1000, omega_field, levels=20, cmap='RdBu_r')
509
axes[1, 0].set_xlabel('x (mm)')
510
axes[1, 0].set_ylabel('y (mm)')
511
axes[1, 0].set_title('Vorticity $\\omega$')
512
axes[1, 0].set_aspect('equal')
513
plt.colorbar(cs3, ax=axes[1, 0])
514
515
# Velocity vectors
516
skip = 3
517
axes[1, 1].quiver(X[::skip, ::skip]*1000, Y[::skip, ::skip]*1000,
518
u_field[::skip, ::skip], v_field[::skip, ::skip],
519
scale=50, alpha=0.7)
520
axes[1, 1].set_xlabel('x (mm)')
521
axes[1, 1].set_ylabel('y (mm)')
522
axes[1, 1].set_title('Velocity Vectors')
523
axes[1, 1].set_aspect('equal')
524
525
plt.tight_layout()
526
plt.savefig('navier_stokes_plot5.pdf', bbox_inches='tight', dpi=150)
527
plt.close()
528
\end{pycode}
529
530
\begin{figure}[H]
531
\centering
532
\includegraphics[width=0.95\textwidth]{navier_stokes_plot5.pdf}
533
\caption{Flow field visualization: stream function, velocity magnitude, vorticity, and vectors.}
534
\end{figure}
535
536
\section{Oscillatory Flow (Stokes' Second Problem)}
537
538
\begin{pycode}
539
# Oscillating plate problem
540
omega_osc = 2 * np.pi
541
delta_stokes = np.sqrt(2 * nu / omega_osc)
542
543
y_osc = np.linspace(0, 10*delta_stokes, 200)
544
t_osc_values = np.linspace(0, 2*np.pi/omega_osc, 8)
545
546
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
547
548
# Velocity profiles at different phases
549
for i, t in enumerate(t_osc_values[:-1]):
550
phase = omega_osc * t
551
u_osc = U_wall * np.exp(-y_osc/delta_stokes) * np.cos(phase - y_osc/delta_stokes)
552
axes[0, 0].plot(u_osc*1000, y_osc/delta_stokes, linewidth=1,
553
label=f'$\\omega t$ = {np.rad2deg(phase):.0f}')
554
555
axes[0, 0].axvline(x=0, color='gray', linestyle='-', alpha=0.3)
556
axes[0, 0].set_xlabel('Velocity (mm/s)')
557
axes[0, 0].set_ylabel('$y/\\delta_s$')
558
axes[0, 0].set_title("Stokes' Second Problem")
559
axes[0, 0].legend(loc='upper right', fontsize=7)
560
axes[0, 0].grid(True, alpha=0.3)
561
562
# Amplitude decay
563
amplitude = U_wall * np.exp(-y_osc/delta_stokes)
564
axes[0, 1].plot(amplitude*1000, y_osc/delta_stokes, 'b-', linewidth=1.5)
565
axes[0, 1].axhline(y=1, color='r', linestyle='--', alpha=0.7, label='$\\delta_s$')
566
axes[0, 1].set_xlabel('Amplitude (mm/s)')
567
axes[0, 1].set_ylabel('$y/\\delta_s$')
568
axes[0, 1].set_title('Velocity Amplitude Decay')
569
axes[0, 1].legend(loc='upper right', fontsize=8)
570
axes[0, 1].grid(True, alpha=0.3)
571
572
# Phase lag
573
phase_lag = y_osc / delta_stokes
574
axes[1, 0].plot(np.rad2deg(phase_lag), y_osc/delta_stokes, 'b-', linewidth=1.5)
575
axes[1, 0].set_xlabel('Phase Lag (degrees)')
576
axes[1, 0].set_ylabel('$y/\\delta_s$')
577
axes[1, 0].set_title('Phase Lag with Distance')
578
axes[1, 0].grid(True, alpha=0.3)
579
580
# Stokes layer thickness vs frequency
581
freq_range = np.logspace(-1, 2, 100)
582
omega_range = 2 * np.pi * freq_range
583
delta_range = np.sqrt(2 * nu / omega_range)
584
585
axes[1, 1].loglog(freq_range, delta_range*1000, 'b-', linewidth=1.5)
586
axes[1, 1].set_xlabel('Frequency (Hz)')
587
axes[1, 1].set_ylabel('$\\delta_s$ (mm)')
588
axes[1, 1].set_title('Stokes Layer Thickness vs Frequency')
589
axes[1, 1].grid(True, which='both', alpha=0.3)
590
591
plt.tight_layout()
592
plt.savefig('navier_stokes_plot6.pdf', bbox_inches='tight', dpi=150)
593
plt.close()
594
\end{pycode}
595
596
\begin{figure}[H]
597
\centering
598
\includegraphics[width=0.95\textwidth]{navier_stokes_plot6.pdf}
599
\caption{Oscillatory flow: Stokes' second problem and Stokes layer characteristics.}
600
\end{figure}
601
602
\begin{table}[H]
603
\centering
604
\caption{Oscillatory Flow Parameters}
605
\begin{tabular}{lcc}
606
\toprule
607
\textbf{Parameter} & \textbf{Value} & \textbf{Unit} \\
608
\midrule
609
Oscillation Frequency & \py{f"{omega_osc/(2*np.pi):.2f}"} & Hz \\
610
Stokes Layer Thickness & \py{f"{delta_stokes*1000:.3f}"} & mm \\
611
Penetration Depth ($3\delta_s$) & \py{f"{3*delta_stokes*1000:.3f}"} & mm \\
612
\bottomrule
613
\end{tabular}
614
\end{table}
615
616
\section{Conclusions}
617
618
This analysis of the Navier-Stokes equations demonstrated:
619
620
\begin{enumerate}
621
\item \textbf{Couette Flow}: Linear velocity profile with wall shear stress $\tau_w = \py{f"{tau_wall_couette:.4f}"}$ Pa. Generalized solutions with pressure gradients show parabolic modifications.
622
623
\item \textbf{Poiseuille Flow}: Parabolic velocity profile with maximum velocity $u_{max} = \py{f"{u_max*1000:.2f}"}$ mm/s. The entrance length scales linearly with Reynolds number for laminar flow.
624
625
\item \textbf{Boundary Layers}: Blasius solution gives $f''(0) = \py{f"{f2_0_correct:.4f}"}$, with boundary layer thickness $\delta \propto x^{1/2}$.
626
627
\item \textbf{Reynolds Number Effects}: Critical $Re = 2300$ for pipe flow transition, with friction factor following the Blasius correlation in turbulent regime.
628
629
\item \textbf{Oscillatory Flows}: Stokes layer thickness $\delta_s = \py{f"{delta_stokes*1000:.3f}"}$ mm determines penetration depth of oscillations.
630
\end{enumerate}
631
632
\end{document}
633
634