13. CFD ๊ธฐ์ด (Computational Fluid Dynamics Basics)
13. CFD ๊ธฐ์ด (Computational Fluid Dynamics Basics)¶
ํ์ต ๋ชฉํ¶
- ์ ์ฒด์ญํ์ ๊ธฐ๋ณธ ์๋ฆฌ์ ์ง๋ฐฐ๋ฐฉ์ ์ ์ดํด
- ๋ ์ด๋์ฆ ์์ ์ ๋ ํน์ฑ ๊ด๊ณ ํ์
- Navier-Stokes ๋ฐฉ์ ์์ ์ ๋์ ์๋ฏธ ์ดํด
- ์์ถ์ฑ/๋น์์ถ์ฑ ์ ๋ ๊ตฌ๋ถ
- ๊ฒฝ๊ณ์ธต ๊ฐ๋ ํ์ต
- ๊ฐ๋จํ ์ฑ๋ ์ ๋ CFD ๊ตฌํ
1. ์ ์ฒด์ญํ ๊ธฐ์ด¶
1.1 ์ฐ์์ฒด ๊ฐ์ ¶
์ฐ์์ฒด ๊ฐ์ (Continuum Hypothesis):
- ์ ์ฒด๋ฅผ ์ฐ์์ ์ธ ๋งค์ง๋ก ์ทจ๊ธ
- ๊ฐ๋ณ ๋ถ์ ๋์ ์ ์ฒด ์
์(fluid particle) ๊ฐ๋
์ฌ์ฉ
- Knudsen ์ Kn = ฮป/L << 1 ์ผ ๋ ์ ํจ
(ฮป: ํ๊ท ์์ ๊ฒฝ๋ก, L: ํน์ฑ ๊ธธ์ด)
1.2 ๊ธฐ๋ณธ ๋ฌผ์ฑ์น¶
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# ์ ์ฒด ๋ฌผ์ฑ์น ์์
class FluidProperties:
"""์ ์ฒด ๋ฌผ์ฑ์น ํด๋์ค"""
def __init__(self, name, rho, mu, k=None, cp=None):
"""
Parameters:
- name: ์ ์ฒด ์ด๋ฆ
- rho: ๋ฐ๋ [kg/mยณ]
- mu: ๋์ ์ฑ๊ณ์ [Paยทs]
- k: ์ด์ ๋๋ [W/(mยทK)]
- cp: ๋น์ด [J/(kgยทK)]
"""
self.name = name
self.rho = rho # ๋ฐ๋
self.mu = mu # ๋์ ์ฑ๊ณ์ (dynamic viscosity)
self.k = k # ์ด์ ๋๋
self.cp = cp # ์ ์๋น์ด
@property
def nu(self):
"""์ด๋์ ์ฑ๊ณ์ (kinematic viscosity)"""
return self.mu / self.rho
@property
def alpha(self):
"""์ดํ์ฐ๊ณ์ (thermal diffusivity)"""
if self.k and self.cp:
return self.k / (self.rho * self.cp)
return None
@property
def Pr(self):
"""ํ๋ํ ์ (Prandtl number)"""
if self.cp:
return self.mu * self.cp / self.k
return None
def __repr__(self):
return f"FluidProperties({self.name}): rho={self.rho}, mu={self.mu}, nu={self.nu:.2e}"
# ์ผ๋ฐ์ ์ธ ์ ์ฒด๋ค
water = FluidProperties("Water (20ยฐC)", rho=998, mu=1.002e-3, k=0.598, cp=4182)
air = FluidProperties("Air (20ยฐC)", rho=1.204, mu=1.825e-5, k=0.0257, cp=1007)
oil = FluidProperties("Engine Oil (20ยฐC)", rho=880, mu=0.29, k=0.145, cp=1880)
print(water)
print(f" Prandtl number: {water.Pr:.2f}")
print(air)
print(f" Prandtl number: {air.Pr:.2f}")
1.3 ๋ ์ด๋์ฆ ์ (Reynolds Number)¶
๋ ์ด๋์ฆ ์ ์ ์:
Re = ฯUL/ฮผ = UL/ฮฝ = (๊ด์ฑ๋ ฅ)/(์ ์ฑ๋ ฅ)
์ฌ๊ธฐ์:
- ฯ: ์ ์ฒด ๋ฐ๋
- U: ํน์ฑ ์๋
- L: ํน์ฑ ๊ธธ์ด
- ฮผ: ๋์ ์ฑ๊ณ์
- ฮฝ = ฮผ/ฯ: ์ด๋์ ์ฑ๊ณ์
์ ๋ ํน์ฑ:
- Re < 2300: ์ธต๋ฅ (Laminar flow)
- 2300 < Re < 4000: ์ฒ์ด ์์ญ (Transition)
- Re > 4000: ๋๋ฅ (Turbulent flow)
def reynolds_number_analysis():
"""๋ ์ด๋์ฆ ์์ ์ ๋ ํน์ฑ ๋ถ์"""
# ๊ด ์ ๋ ์์
D = 0.05 # ๊ด ์ง๊ฒฝ [m]
U_range = np.linspace(0.01, 2.0, 100) # ์ ์ ๋ฒ์ [m/s]
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# ๋ฌผ์์์ ๋ ์ด๋์ฆ ์
ax1 = axes[0]
Re_water = water.rho * U_range * D / water.mu
ax1.plot(U_range, Re_water, 'b-', linewidth=2, label='Water')
ax1.axhline(y=2300, color='orange', linestyle='--', label='Transition start')
ax1.axhline(y=4000, color='red', linestyle='--', label='Turbulent')
ax1.fill_between(U_range, 0, 2300, alpha=0.2, color='green', label='Laminar')
ax1.fill_between(U_range, 2300, 4000, alpha=0.2, color='orange')
ax1.fill_between(U_range, 4000, max(Re_water), alpha=0.2, color='red')
ax1.set_xlabel('Flow Velocity U [m/s]')
ax1.set_ylabel('Reynolds Number Re')
ax1.set_title(f'Reynolds Number in Pipe Flow (D = {D*100} cm, Water)')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.set_yscale('log')
# ๋ค์ํ ์ ์ฒด ๋น๊ต
ax2 = axes[1]
fluids = [water, air, oil]
colors = ['blue', 'cyan', 'brown']
for fluid, color in zip(fluids, colors):
Re = fluid.rho * U_range * D / fluid.mu
ax2.plot(U_range, Re, color=color, linewidth=2, label=fluid.name)
ax2.axhline(y=2300, color='k', linestyle='--', alpha=0.5)
ax2.set_xlabel('Flow Velocity U [m/s]')
ax2.set_ylabel('Reynolds Number Re')
ax2.set_title('Reynolds Number Comparison for Different Fluids')
ax2.legend()
ax2.grid(True, alpha=0.3)
ax2.set_yscale('log')
plt.tight_layout()
plt.savefig('reynolds_number.png', dpi=150, bbox_inches='tight')
plt.show()
# ์ค์ฉ์ ์์ ๊ณ์ฐ
print("\n=== ์ค์ฉ์ ๋ ์ด๋์ฆ ์ ๊ณ์ฐ ์์ ===")
print(f"๊ด ์ง๊ฒฝ D = {D*100} cm")
test_velocities = [0.1, 0.5, 1.0]
for U in test_velocities:
Re = water.rho * U * D / water.mu
regime = "์ธต๋ฅ" if Re < 2300 else ("์ฒ์ด" if Re < 4000 else "๋๋ฅ")
print(f" U = {U} m/s -> Re = {Re:.0f} ({regime})")
# reynolds_number_analysis()
2. ์ง๋ฐฐ๋ฐฉ์ ์¶
2.1 ์ฐ์ ๋ฐฉ์ ์ (Continuity Equation)¶
์ง๋ ๋ณด์กด ๋ฒ์น:
โฯ/โt + โยท(ฯu) = 0
ํ
์ ํ๊ธฐ:
โฯ/โt + โ(ฯuแตข)/โxแตข = 0
๋น์์ถ์ฑ ์ ๋ (ฯ = const):
โยทu = 0
๋๋: โu/โx + โv/โy + โw/โz = 0
def visualize_continuity():
"""์ฐ์ ๋ฐฉ์ ์ ์๊ฐํ - ๊ฒ์ฌ์ฒด์ ์ ๊ทผ"""
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# ๊ฒ์ฌ์ฒด์ ๊ฐ๋
๋
ax1 = axes[0]
# ๊ฒ์ฌ์ฒด์ (์ฌ๊ฐํ)
cv = plt.Rectangle((0.3, 0.3), 0.4, 0.4, fill=False,
edgecolor='black', linewidth=2)
ax1.add_patch(cv)
# ์ง๋ ์ ์
/์ ์ถ ํ์ดํ
# x ๋ฐฉํฅ
ax1.annotate('', xy=(0.3, 0.5), xytext=(0.1, 0.5),
arrowprops=dict(arrowstyle='->', color='blue', lw=2))
ax1.text(0.15, 0.55, r'$\rho u A$', fontsize=12, color='blue')
ax1.annotate('', xy=(0.9, 0.5), xytext=(0.7, 0.5),
arrowprops=dict(arrowstyle='->', color='blue', lw=2))
ax1.text(0.75, 0.55, r'$\rho u A + \frac{\partial(\rho u)}{\partial x}\Delta x A$', fontsize=10, color='blue')
# y ๋ฐฉํฅ
ax1.annotate('', xy=(0.5, 0.3), xytext=(0.5, 0.1),
arrowprops=dict(arrowstyle='->', color='red', lw=2))
ax1.text(0.55, 0.15, r'$\rho v A$', fontsize=12, color='red')
ax1.annotate('', xy=(0.5, 0.9), xytext=(0.5, 0.7),
arrowprops=dict(arrowstyle='->', color='red', lw=2))
ax1.text(0.55, 0.8, r'$\rho v A + ...$', fontsize=10, color='red')
ax1.text(0.5, 0.5, r'$\Delta V$', fontsize=14, ha='center', va='center')
ax1.set_xlim(0, 1)
ax1.set_ylim(0, 1)
ax1.set_aspect('equal')
ax1.set_title('๊ฒ์ฌ์ฒด์ ๊ณผ ์ง๋ ํ๋ญ์ค')
ax1.axis('off')
# ๋น์์ถ์ฑ ์ ๋์ฅ ์์ (div u = 0)
ax2 = axes[1]
x = np.linspace(-2, 2, 20)
y = np.linspace(-2, 2, 20)
X, Y = np.meshgrid(x, y)
# ์ ๋์ฅ: u = y, v = -x (์ ์์ค ํ์ ์ ๋, div=0)
U = Y
V = -X
# ๋ฐ์ฐ ๊ณ์ฐ (์์น์ )
div = np.zeros_like(X)
ax2.streamplot(X, Y, U, V, color='blue', density=1.5, linewidth=1)
ax2.quiver(X[::2, ::2], Y[::2, ::2], U[::2, ::2], V[::2, ::2],
color='red', alpha=0.7)
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_title(r'๋น์์ถ์ฑ ์ ๋์ฅ ์์: $u=y, v=-x$ ($\nabla \cdot \mathbf{u} = 0$)')
ax2.set_aspect('equal')
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('continuity_equation.png', dpi=150, bbox_inches='tight')
plt.show()
# visualize_continuity()
2.2 ์ด๋๋ ๋ฐฉ์ ์ (Momentum Equation)¶
Newton์ ์ 2๋ฒ์น ์ ์ฉ:
ฯ(Du/Dt) = -โp + ฮผโยฒu + ฯg + f
์ฌ๊ธฐ์:
- Du/Dt = โu/โt + (uยทโ)u : ๋ฌผ์ง ๋ฏธ๋ถ
- โp: ์๋ ฅ ๊ตฌ๋ฐฐ
- ฮผโยฒu: ์ ์ฑ๋ ฅ
- ฯg: ์ค๋ ฅ
- f: ์ธ๋ถ ์ฒด์ ๋ ฅ
์ฑ๋ถ๋ณ (๋น์์ถ์ฑ, 2D):
x: ฯ(โu/โt + uโu/โx + vโu/โy) = -โp/โx + ฮผ(โยฒu/โxยฒ + โยฒu/โyยฒ)
y: ฯ(โv/โt + uโv/โx + vโv/โy) = -โp/โy + ฮผ(โยฒv/โxยฒ + โยฒv/โyยฒ)
2.3 Navier-Stokes ๋ฐฉ์ ์ ์ ๋¶
def navier_stokes_derivation():
"""Navier-Stokes ๋ฐฉ์ ์์ ๊ฐ ํญ ์๋ฏธ ์๊ฐํ"""
print("=" * 60)
print("Navier-Stokes ๋ฐฉ์ ์ ์ ๋")
print("=" * 60)
derivation = """
1. ์ง๋ ๋ณด์กด (์ฐ์๋ฐฉ์ ์):
โฯ/โt + โยท(ฯu) = 0
๋น์์ถ์ฑ: โยทu = 0
2. ์ด๋๋ ๋ณด์กด (Newton ์ 2๋ฒ์น):
๋ฌผ์ง ๋ฏธ๋ถ (Lagrangian derivative):
Du/Dt = โu/โt + (uยทโ)u
โ โ
๊ตญ์๊ฐ์๋ ๋๋ฅ๊ฐ์๋
ํ์ ๊ท ํ:
ฯ(Du/Dt) = โF = -โp + โยทฯ + ฯg
โ โ โ โ
๊ด์ฑ๋ ฅ ์๋ ฅ๋ ฅ ์ ์ฑ๋ ฅ ์ฒด์ ๋ ฅ
Newton ์ ์ฒด ๊ฐ์ (ฯ = ฮผ(โu + โuแต)):
ฯ(Du/Dt) = -โp + ฮผโยฒu + ฯg
3. ๋น์์ถ์ฑ Navier-Stokes ๋ฐฉ์ ์:
โu/โt + (uยทโ)u = -1/ฯ โp + ฮฝโยฒu + g
์ฌ๊ธฐ์ ฮฝ = ฮผ/ฯ (์ด๋์ ์ฑ๊ณ์)
4. ๋ฌด์ฐจ์ํ (Dimensionless form):
ํน์ฑ ์ค์ผ์ผ: L(๊ธธ์ด), U(์๋), T=L/U(์๊ฐ), P=ฯUยฒ(์๋ ฅ)
โu*/โt* + (u*ยทโ*)u* = -โ*p* + (1/Re)โ*ยฒu*
์ฌ๊ธฐ์ Re = UL/ฮฝ (๋ ์ด๋์ฆ ์)
"""
print(derivation)
# ๊ฐ ํญ์ ์๋์ ํฌ๊ธฐ ์๊ฐํ
fig, ax = plt.subplots(figsize=(12, 6))
Re_range = np.logspace(0, 6, 100)
# ๋ฌด์ฐจ์ ํฌ๊ธฐ ๋น๊ต
inertia = np.ones_like(Re_range) # O(1)
viscous = 1 / Re_range # O(1/Re)
pressure = np.ones_like(Re_range) # O(1)
ax.loglog(Re_range, inertia, 'b-', linewidth=2, label='๊ด์ฑํญ O(1)')
ax.loglog(Re_range, viscous, 'r-', linewidth=2, label='์ ์ฑํญ O(1/Re)')
ax.loglog(Re_range, pressure, 'g--', linewidth=2, label='์๋ ฅํญ O(1)')
ax.axvline(x=2300, color='gray', linestyle=':', label='์ธต๋ฅ-๋๋ฅ ์ฒ์ด')
ax.fill_between(Re_range, 1e-6, 1, where=Re_range < 2300,
alpha=0.2, color='green', label='์ ์ฑ ์ง๋ฐฐ')
ax.fill_between(Re_range, 1e-6, 1, where=Re_range >= 2300,
alpha=0.2, color='blue', label='๊ด์ฑ ์ง๋ฐฐ')
ax.set_xlabel('Reynolds Number Re')
ax.set_ylabel('Relative Magnitude')
ax.set_title('Navier-Stokes ๋ฐฉ์ ์ ๊ฐ ํญ์ ์๋์ ํฌ๊ธฐ')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_ylim(1e-6, 10)
plt.tight_layout()
plt.savefig('navier_stokes_terms.png', dpi=150, bbox_inches='tight')
plt.show()
# navier_stokes_derivation()
3. ์์ถ์ฑ vs ๋น์์ถ์ฑ ์ ๋¶
3.1 ๋งํ ์์ ์์ถ์ฑ¶
๋งํ ์ ์ ์:
Ma = U/a
์ฌ๊ธฐ์:
- U: ์ ์ฒด ์๋
- a: ์์ (์ด์๊ธฐ์ฒด: a = โ(ฮณRT))
๋ถ๋ฅ:
- Ma < 0.3: ๋น์์ถ์ฑ์ผ๋ก ์ทจ๊ธ ๊ฐ๋ฅ (๋ฐ๋ ๋ณํ < 5%)
- 0.3 < Ma < 0.8: ์์์ (Subsonic)
- 0.8 < Ma < 1.2: ์ฒ์์ (Transonic)
- 1.2 < Ma < 5: ์ด์์ (Supersonic)
- Ma > 5: ๊ทน์ด์์ (Hypersonic)
def compressibility_analysis():
"""์์ถ์ฑ ํจ๊ณผ ๋ถ์"""
# ๋ฑ์ํธ๋กํผ ๋ฐ๋ ๋น
def density_ratio(Ma, gamma=1.4):
"""ฯ/ฯโ = (1 + (ฮณ-1)/2 Maยฒ)^(-1/(ฮณ-1))"""
return (1 + (gamma - 1) / 2 * Ma**2) ** (-1 / (gamma - 1))
# ์๋ ฅ ๋น
def pressure_ratio(Ma, gamma=1.4):
"""p/pโ = (1 + (ฮณ-1)/2 Maยฒ)^(-ฮณ/(ฮณ-1))"""
return (1 + (gamma - 1) / 2 * Ma**2) ** (-gamma / (gamma - 1))
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
Ma = np.linspace(0, 3, 200)
# ๋ฐ๋ ๋น
ax1 = axes[0]
rho_ratio = density_ratio(Ma)
ax1.plot(Ma, rho_ratio, 'b-', linewidth=2)
ax1.axvline(x=0.3, color='green', linestyle='--', label='Ma=0.3 (๋น์์ถ์ฑ ํ๊ณ)')
ax1.axhline(y=0.95, color='gray', linestyle=':', alpha=0.5)
ax1.fill_between(Ma, 0, rho_ratio, where=Ma < 0.3, alpha=0.3, color='green',
label='๋น์์ถ์ฑ ์์ญ')
ax1.set_xlabel('Mach Number Ma')
ax1.set_ylabel(r'Density Ratio $\rho/\rho_0$')
ax1.set_title('๋ฑ์ํธ๋กํผ ์ ๋์์์ ๋ฐ๋ ๋ณํ')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.set_xlim(0, 3)
ax1.set_ylim(0, 1.1)
# ์์ถ์ฑ/๋น์์ถ์ฑ ๋ฐฉ์ ์ ๋น๊ต
ax2 = axes[1]
equations = {
'๋น์์ถ์ฑ\n(Incompressible)': [
r'$\nabla \cdot \mathbf{u} = 0$',
r'$\rho \frac{D\mathbf{u}}{Dt} = -\nabla p + \mu \nabla^2 \mathbf{u}$',
'3๊ฐ ๋ฐฉ์ ์, 4๊ฐ ๋ฏธ์ง์ (u,v,w,p)',
'์๋ ฅ: Poisson ๋ฐฉ์ ์์ผ๋ก ๊ฒฐ์ '
],
'์์ถ์ฑ\n(Compressible)': [
r'$\frac{\partial \rho}{\partial t} + \nabla \cdot (\rho \mathbf{u}) = 0$',
r'$\frac{\partial (\rho \mathbf{u})}{\partial t} + \nabla \cdot (\rho \mathbf{u} \otimes \mathbf{u}) = -\nabla p + \nabla \cdot \tau$',
r'$\frac{\partial E}{\partial t} + \nabla \cdot ((E+p)\mathbf{u}) = \nabla \cdot (k\nabla T + \tau \cdot \mathbf{u})$',
'5๊ฐ ๋ฐฉ์ ์, 5๊ฐ ๋ฏธ์ง์ (ฯ,u,v,w,E) + ์ํ๋ฐฉ์ ์'
]
}
ax2.text(0.25, 0.95, '๋น์์ถ์ฑ (Ma < 0.3)', fontsize=14, fontweight='bold',
ha='center', transform=ax2.transAxes)
ax2.text(0.75, 0.95, '์์ถ์ฑ (Ma > 0.3)', fontsize=14, fontweight='bold',
ha='center', transform=ax2.transAxes)
y_pos = 0.85
for eq in equations['๋น์์ถ์ฑ\n(Incompressible)']:
ax2.text(0.25, y_pos, eq, fontsize=10, ha='center', transform=ax2.transAxes)
y_pos -= 0.12
y_pos = 0.85
for eq in equations['์์ถ์ฑ\n(Compressible)']:
ax2.text(0.75, y_pos, eq, fontsize=10, ha='center', transform=ax2.transAxes)
y_pos -= 0.12
ax2.axvline(x=0.5, color='black', linestyle='-', linewidth=2,
transform=ax2.transAxes)
ax2.axis('off')
ax2.set_title('์ง๋ฐฐ๋ฐฉ์ ์ ๋น๊ต')
plt.tight_layout()
plt.savefig('compressibility.png', dpi=150, bbox_inches='tight')
plt.show()
# compressibility_analysis()
4. ๊ฒฝ๊ณ์ธต ์ด๋ก ¶
4.1 ๊ฒฝ๊ณ์ธต ๊ฐ๋ ¶
๊ฒฝ๊ณ์ธต (Boundary Layer):
- ๋ฒฝ๋ฉด ๊ทผ์ฒ์์ ์ ์ฑ ํจ๊ณผ๊ฐ ์ง๋ฐฐ์ ์ธ ์์ญ
- ๋ฒฝ๋ฉด: no-slip ์กฐ๊ฑด (u = 0)
- ๊ฒฝ๊ณ์ธต ์ธ๋ถ: ์์ ๋ฅ ์๋ Uโ
๊ฒฝ๊ณ์ธต ๋๊ป ฮด:
- ์๋๊ฐ ์์ ๋ฅ์ 99%๊ฐ ๋๋ ์์น
- ์ธต๋ฅ ํํ: ฮด ~ x/โRe_x (Blasius)
- ๋๋ฅ ํํ: ฮด ~ x/Re_x^(1/5)
def boundary_layer_theory():
"""๊ฒฝ๊ณ์ธต ์ด๋ก ์๊ฐํ"""
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# (1) Blasius ์ธต๋ฅ ๊ฒฝ๊ณ์ธต ํ๋กํ์ผ
ax1 = axes[0, 0]
# Blasius ์ ์ฌํด (๊ทผ์ฌ)
eta = np.linspace(0, 8, 100) # ๋ฌด์ฐจ์ ์ขํ ฮท = yโ(Uโ/ฮฝx)
# f'(ฮท) ๊ทผ์ฌ (์ค์ ๋ Blasius ๋ฐฉ์ ์ ์์นํด)
# ์ฌ๊ธฐ์๋ ๊ฐ๋จํ ๊ทผ์ฌ ์ฌ์ฉ
u_U = np.tanh(eta / 2.5) ** 1.5 # ๊ทผ์ฌ
ax1.plot(u_U, eta, 'b-', linewidth=2)
ax1.axhline(y=5.0, color='red', linestyle='--', label=r'$\delta_{99}$ (ฮท โ 5)')
ax1.fill_betweenx(eta, 0, u_U, alpha=0.3)
ax1.set_xlabel(r'$u/U_\infty$')
ax1.set_ylabel(r'$\eta = y\sqrt{U_\infty/\nu x}$')
ax1.set_title('Blasius ์ธต๋ฅ ๊ฒฝ๊ณ์ธต ์๋ ํ๋กํ์ผ')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.set_xlim(0, 1.1)
ax1.set_ylim(0, 8)
# (2) ๊ฒฝ๊ณ์ธต ๋๊ป ์ฑ์ฅ
ax2 = axes[0, 1]
nu = 1.5e-5 # ๊ณต๊ธฐ ์ด๋์ ์ฑ๊ณ์
U_inf = 10 # ์์ ๋ฅ ์๋ [m/s]
x = np.linspace(0.01, 1, 100) # ํํ ์์น [m]
Re_x = U_inf * x / nu
# ์ธต๋ฅ ๊ฒฝ๊ณ์ธต ๋๊ป (Blasius)
delta_lam = 5.0 * x / np.sqrt(Re_x)
# ๋๋ฅ ๊ฒฝ๊ณ์ธต ๋๊ป (1/7์น ๋ฒ์น)
delta_turb = 0.37 * x / Re_x ** 0.2
# ์ฒ์ด ์์น (Re_x ~ 5ร10^5)
x_trans = 5e5 * nu / U_inf
ax2.plot(x * 1000, delta_lam * 1000, 'b-', linewidth=2, label='์ธต๋ฅ')
ax2.plot(x * 1000, delta_turb * 1000, 'r-', linewidth=2, label='๋๋ฅ')
ax2.axvline(x=x_trans * 1000, color='green', linestyle='--', label=f'์ฒ์ด์ (x โ {x_trans*1000:.0f} mm)')
ax2.set_xlabel('x [mm]')
ax2.set_ylabel(r'$\delta$ [mm]')
ax2.set_title(f'๊ฒฝ๊ณ์ธต ๋๊ป ์ฑ์ฅ (Uโ = {U_inf} m/s, ๊ณต๊ธฐ)')
ax2.legend()
ax2.grid(True, alpha=0.3)
# (3) ๋ฒฝ๋ฉด ์ ๋จ์๋ ฅ
ax3 = axes[1, 0]
# ์ธต๋ฅ ๋ฒฝ๋ฉด ์ ๋จ์๋ ฅ ๊ณ์
Cf_lam = 0.664 / np.sqrt(Re_x)
# ๋๋ฅ ๋ฒฝ๋ฉด ์ ๋จ์๋ ฅ ๊ณ์
Cf_turb = 0.027 / Re_x ** (1/7)
ax3.loglog(Re_x, Cf_lam, 'b-', linewidth=2, label='์ธต๋ฅ (Blasius)')
ax3.loglog(Re_x, Cf_turb, 'r-', linewidth=2, label='๋๋ฅ (1/7์น ๋ฒ์น)')
ax3.axvline(x=5e5, color='green', linestyle='--', alpha=0.5)
ax3.set_xlabel(r'$Re_x$')
ax3.set_ylabel(r'$C_f = \tau_w / (0.5\rho U_\infty^2)$')
ax3.set_title('๋ฒฝ๋ฉด ๋ง์ฐฐ ๊ณ์')
ax3.legend()
ax3.grid(True, alpha=0.3)
# (4) ๊ฒฝ๊ณ์ธต ๊ฐ๋
๋
ax4 = axes[1, 1]
# ํํ
ax4.fill_between([0, 5], [-0.1, -0.1], [0, 0], color='gray', alpha=0.5)
# ๊ฒฝ๊ณ์ธต
x_plate = np.linspace(0, 5, 50)
delta_vis = 0.5 * np.sqrt(x_plate) # ๋จ์ํ๋ ๊ฒฝ๊ณ์ธต
ax4.fill_between(x_plate, 0, delta_vis, alpha=0.3, color='blue', label='๊ฒฝ๊ณ์ธต')
ax4.plot(x_plate, delta_vis, 'b-', linewidth=2)
# ์๋ ํ๋กํ์ผ ํ์ดํ
for x0 in [0.5, 1.5, 3.0, 4.5]:
y_arrows = np.linspace(0, 0.5 * np.sqrt(x0) * 1.2, 6)
for y in y_arrows:
u = min(1, y / (0.5 * np.sqrt(x0))) if x0 > 0 else 0
ax4.annotate('', xy=(x0 + u * 0.3, y), xytext=(x0, y),
arrowprops=dict(arrowstyle='->', color='red', lw=0.8))
# ์์ ๋ฅ
ax4.annotate('', xy=(5, 1.5), xytext=(0, 1.5),
arrowprops=dict(arrowstyle='->', color='black', lw=2))
ax4.text(2.5, 1.7, r'$U_\infty$', fontsize=14)
ax4.text(2.5, 0.3, '๊ฒฝ๊ณ์ธต', fontsize=12, color='blue')
ax4.set_xlabel('x')
ax4.set_ylabel('y')
ax4.set_title('ํํ ์ ๊ฒฝ๊ณ์ธต ๋ฐ๋ฌ')
ax4.set_xlim(-0.5, 5.5)
ax4.set_ylim(-0.2, 2)
ax4.set_aspect('equal')
ax4.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('boundary_layer.png', dpi=150, bbox_inches='tight')
plt.show()
# boundary_layer_theory()
5. ๊ฐ๋จํ CFD ์์ : Poiseuille ์ ๋¶
5.1 2D ์ฑ๋ ์ ๋ (Poiseuille Flow)¶
๋ฌธ์ ์ค์ :
- ๋ ํํ ํํ ์ฌ์ด์ ์ ์ ์ธต๋ฅ ์ ๋
- ์๋ ฅ ๊ตฌ๋ฐฐ์ ์ํด ๊ตฌ๋
- ํด์ํด ์กด์ฌ (๊ฒ์ฆ์ฉ)
์ง๋ฐฐ๋ฐฉ์ ์ (์ ์, ์์ ๋ฐ๋ฌ):
dยฒu/dyยฒ = (1/ฮผ)(dp/dx) = const
๊ฒฝ๊ณ์กฐ๊ฑด:
- y = 0: u = 0 (no-slip)
- y = H: u = 0 (no-slip)
ํด์ํด:
u(y) = -(1/2ฮผ)(dp/dx)y(H-y)
์ต๋ ์๋ (์ค์ฌ):
u_max = (Hยฒ/8ฮผ)|dp/dx|
def poiseuille_flow_exact():
"""Poiseuille ์ ๋ ํด์ํด"""
H = 1.0 # ์ฑ๋ ๋์ด [m]
mu = 0.01 # ๋์ ์ฑ๊ณ์ [Paยทs]
dpdx = -1.0 # ์๋ ฅ ๊ตฌ๋ฐฐ [Pa/m] (์์ = ์์ x ๋ฐฉํฅ ์ ๋)
y = np.linspace(0, H, 100)
u_exact = -(1 / (2 * mu)) * dpdx * y * (H - y)
u_max = H**2 / (8 * mu) * abs(dpdx)
u_avg = 2 / 3 * u_max
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# ์๋ ํ๋กํ์ผ
ax1 = axes[0]
ax1.plot(u_exact, y, 'b-', linewidth=2, label='ํด์ํด')
ax1.axhline(y=H/2, color='red', linestyle='--', alpha=0.5)
ax1.axvline(x=u_max, color='green', linestyle='--', alpha=0.5, label=f'u_max = {u_max:.2f}')
ax1.axvline(x=u_avg, color='orange', linestyle='--', alpha=0.5, label=f'u_avg = {u_avg:.2f}')
ax1.set_xlabel('u [m/s]')
ax1.set_ylabel('y [m]')
ax1.set_title('Poiseuille ์ ๋ ์๋ ํ๋กํ์ผ')
ax1.legend()
ax1.grid(True, alpha=0.3)
# ์ ๋จ์๋ ฅ ํ๋กํ์ผ
ax2 = axes[1]
tau = mu * np.gradient(u_exact, y)
ax2.plot(tau, y, 'r-', linewidth=2)
ax2.axvline(x=0, color='black', linestyle='-', alpha=0.3)
ax2.set_xlabel(r'$\tau$ [Pa]')
ax2.set_ylabel('y [m]')
ax2.set_title('์ ๋จ์๋ ฅ ๋ถํฌ')
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('poiseuille_exact.png', dpi=150, bbox_inches='tight')
plt.show()
return u_exact, y
# u_exact, y = poiseuille_flow_exact()
5.2 ์ ํ์ฐจ๋ถ๋ฒ์ ์ด์ฉํ CFD ๊ตฌํ¶
def cfd_channel_flow():
"""
2D ์ฑ๋ ์ ๋ CFD ์๋ฎฌ๋ ์ด์
- ๋น์ ์ Navier-Stokes ๋ฐฉ์ ์
- ์ ์ ์ํ๊น์ง ์๊ฐ ์ ์ง
"""
# ๊ฒฉ์ ์ค์
Nx = 50 # x ๋ฐฉํฅ ๊ฒฉ์ ์
Ny = 30 # y ๋ฐฉํฅ ๊ฒฉ์ ์
Lx = 2.0 # ์ฑ๋ ๊ธธ์ด [m]
Ly = 1.0 # ์ฑ๋ ๋์ด [m]
dx = Lx / (Nx - 1)
dy = Ly / (Ny - 1)
x = np.linspace(0, Lx, Nx)
y = np.linspace(0, Ly, Ny)
X, Y = np.meshgrid(x, y)
# ๋ฌผ์ฑ์น
rho = 1.0 # ๋ฐ๋ [kg/mยณ]
mu = 0.01 # ๋์ ์ฑ๊ณ์ [Paยทs]
nu = mu / rho
# ์๋ ฅ ๊ตฌ๋ฐฐ (์ฒด์ ๋ ฅ์ผ๋ก ๋ชจ๋ธ๋ง)
dpdx = -1.0 # [Pa/m]
# ์๊ฐ ์ค์
dt = 0.001
n_steps = 2000
# ์ด๊ธฐํ
u = np.zeros((Ny, Nx)) # x-์๋
v = np.zeros((Ny, Nx)) # y-์๋
p = np.zeros((Ny, Nx)) # ์๋ ฅ
# CFL ์กฐ๊ฑด ํ์ธ
u_max_expected = Ly**2 / (8 * mu) * abs(dpdx)
CFL = u_max_expected * dt / dx
print(f"์์ ์ต๋ ์๋: {u_max_expected:.4f} m/s")
print(f"CFL ์: {CFL:.4f}")
# ํด์ํด (๊ฒ์ฆ์ฉ)
u_exact = -(1 / (2 * mu)) * dpdx * y * (Ly - y)
def apply_boundary_conditions(u, v):
"""๊ฒฝ๊ณ์กฐ๊ฑด ์ ์ฉ"""
# ๋ฒฝ๋ฉด (no-slip)
u[0, :] = 0 # ํ๋จ ๋ฒฝ
u[-1, :] = 0 # ์๋จ ๋ฒฝ
v[0, :] = 0
v[-1, :] = 0
# ์
๊ตฌ/์ถ๊ตฌ (์ฃผ๊ธฐ์ ๋๋ Neumann)
u[:, 0] = u[:, 1] # ์
๊ตฌ
u[:, -1] = u[:, -2] # ์ถ๊ตฌ
v[:, 0] = 0
v[:, -1] = v[:, -2]
return u, v
def compute_rhs(u, v, p, nu, dx, dy, dpdx, rho):
"""์ฐ๋ณ ๊ณ์ฐ (์ด๋๋ ๋ฐฉ์ ์)"""
Ny, Nx = u.shape
rhs_u = np.zeros_like(u)
rhs_v = np.zeros_like(v)
for i in range(1, Ny-1):
for j in range(1, Nx-1):
# ๋๋ฅํญ (์ค์ฌ์ฐจ๋ถ)
duudx = (u[i, j+1]**2 - u[i, j-1]**2) / (2 * dx)
duvdy = (u[i+1, j] * v[i+1, j] - u[i-1, j] * v[i-1, j]) / (2 * dy)
dvudx = (v[i, j+1] * u[i, j+1] - v[i, j-1] * u[i, j-1]) / (2 * dx)
dvvdy = (v[i+1, j]**2 - v[i-1, j]**2) / (2 * dy)
# ํ์ฐํญ (์ค์ฌ์ฐจ๋ถ)
d2udx2 = (u[i, j+1] - 2*u[i, j] + u[i, j-1]) / dx**2
d2udy2 = (u[i+1, j] - 2*u[i, j] + u[i-1, j]) / dy**2
d2vdx2 = (v[i, j+1] - 2*v[i, j] + v[i, j-1]) / dx**2
d2vdy2 = (v[i+1, j] - 2*v[i, j] + v[i-1, j]) / dy**2
# ์๋ ฅํญ
dpdx_local = (p[i, j+1] - p[i, j-1]) / (2 * dx) if j > 0 and j < Nx-1 else 0
dpdy_local = (p[i+1, j] - p[i-1, j]) / (2 * dy) if i > 0 and i < Ny-1 else 0
# ์ด๋๋ ๋ฐฉ์ ์ ์ฐ๋ณ
rhs_u[i, j] = -duudx - duvdy - dpdx_local/rho + nu * (d2udx2 + d2udy2) - dpdx/rho
rhs_v[i, j] = -dvudx - dvvdy - dpdy_local/rho + nu * (d2vdx2 + d2vdy2)
return rhs_u, rhs_v
# ์๊ฐ ์ ์ง
history = []
for n in range(n_steps):
# ๊ฒฝ๊ณ์กฐ๊ฑด
u, v = apply_boundary_conditions(u, v)
# RHS ๊ณ์ฐ
rhs_u, rhs_v = compute_rhs(u, v, p, nu, dx, dy, dpdx, rho)
# ์๊ฐ ์ ์ง (Euler)
u = u + dt * rhs_u
v = v + dt * rhs_v
# ์๋ ด ์ฒดํฌ
if n % 200 == 0:
u_center = u[:, Nx//2]
error = np.max(np.abs(u_center - u_exact))
history.append((n, error, np.max(u)))
print(f"Step {n}: max error = {error:.6f}, max u = {np.max(u):.6f}")
# ๊ฒฐ๊ณผ ์๊ฐํ
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# (1) ์๋์ฅ (๋ฒกํฐ)
ax1 = axes[0, 0]
skip = 2
ax1.quiver(X[::skip, ::skip], Y[::skip, ::skip],
u[::skip, ::skip], v[::skip, ::skip],
color='blue', scale=30)
ax1.set_xlabel('x [m]')
ax1.set_ylabel('y [m]')
ax1.set_title('์๋ ๋ฒกํฐ์ฅ')
ax1.set_aspect('equal')
# (2) u-์๋ ๋ฑ๊ณ ์
ax2 = axes[0, 1]
cf = ax2.contourf(X, Y, u, levels=20, cmap='jet')
plt.colorbar(cf, ax=ax2, label='u [m/s]')
ax2.set_xlabel('x [m]')
ax2.set_ylabel('y [m]')
ax2.set_title('u-์๋ ๋ถํฌ')
ax2.set_aspect('equal')
# (3) ํด์ํด์ ๋น๊ต
ax3 = axes[1, 0]
u_center = u[:, Nx//2]
ax3.plot(u_center, y, 'bo-', markersize=4, label='CFD')
ax3.plot(u_exact, y, 'r-', linewidth=2, label='ํด์ํด')
ax3.set_xlabel('u [m/s]')
ax3.set_ylabel('y [m]')
ax3.set_title('์๋ ํ๋กํ์ผ ๋น๊ต (x = L/2)')
ax3.legend()
ax3.grid(True, alpha=0.3)
# (4) ์๋ ด ์ด๋ ฅ
ax4 = axes[1, 1]
steps, errors, u_maxs = zip(*history)
ax4.semilogy(steps, errors, 'b-o', label='Error')
ax4.set_xlabel('Time Step')
ax4.set_ylabel('Max Error')
ax4.set_title('์๋ ด ์ด๋ ฅ')
ax4.legend()
ax4.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('cfd_channel_flow.png', dpi=150, bbox_inches='tight')
plt.show()
return u, v, p, X, Y
# u, v, p, X, Y = cfd_channel_flow()
6. CFD์ ์ฃผ์ ๊ณผ์ ¶
6.1 ๊ฒฉ์ ์์ฑ (Mesh Generation)¶
def mesh_types_visualization():
"""CFD ๊ฒฉ์ ์ ํ ์๊ฐํ"""
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# (1) ๊ตฌ์กฐ ๊ฒฉ์ (Structured)
ax1 = axes[0, 0]
nx, ny = 10, 8
x = np.linspace(0, 2, nx)
y = np.linspace(0, 1, ny)
for i in range(ny):
ax1.plot(x, np.full_like(x, y[i]), 'b-', linewidth=0.5)
for j in range(nx):
ax1.plot(np.full_like(y, x[j]), y, 'b-', linewidth=0.5)
X, Y = np.meshgrid(x, y)
ax1.plot(X, Y, 'ko', markersize=3)
ax1.set_title('๊ตฌ์กฐ ๊ฒฉ์ (Structured)')
ax1.set_aspect('equal')
ax1.set_xlim(-0.1, 2.1)
ax1.set_ylim(-0.1, 1.1)
# (2) ๋น๊ตฌ์กฐ ๊ฒฉ์ (Unstructured)
ax2 = axes[0, 1]
from scipy.spatial import Delaunay
# ๋๋ค ํฌ์ธํธ
np.random.seed(42)
points = np.random.rand(30, 2)
points[:, 0] *= 2
# ๊ฒฝ๊ณ ํฌ์ธํธ ์ถ๊ฐ
boundary = np.array([[0, 0], [2, 0], [2, 1], [0, 1]])
for i in range(4):
edge = np.linspace(boundary[i], boundary[(i+1)%4], 8)[1:-1]
points = np.vstack([points, edge])
tri = Delaunay(points)
ax2.triplot(points[:, 0], points[:, 1], tri.simplices, 'b-', linewidth=0.5)
ax2.plot(points[:, 0], points[:, 1], 'ko', markersize=3)
ax2.set_title('๋น๊ตฌ์กฐ ๊ฒฉ์ (Unstructured)')
ax2.set_aspect('equal')
ax2.set_xlim(-0.1, 2.1)
ax2.set_ylim(-0.1, 1.1)
# (3) O-๊ฒฉ์ (์ํต ์ฃผ์)
ax3 = axes[1, 0]
r_inner = 0.3
r_outer = 1.0
n_r = 8
n_theta = 24
r = np.linspace(r_inner, r_outer, n_r)
theta = np.linspace(0, 2*np.pi, n_theta)
for ri in r:
x_circle = ri * np.cos(theta)
y_circle = ri * np.sin(theta)
ax3.plot(x_circle, y_circle, 'b-', linewidth=0.5)
for ti in theta[:-1]:
x_radial = r * np.cos(ti)
y_radial = r * np.sin(ti)
ax3.plot(x_radial, y_radial, 'b-', linewidth=0.5)
R, Theta = np.meshgrid(r, theta)
X_o = R * np.cos(Theta)
Y_o = R * np.sin(Theta)
ax3.plot(X_o, Y_o, 'ko', markersize=2)
ax3.set_title('O-๊ฒฉ์ (์ํต ์ฃผ์)')
ax3.set_aspect('equal')
ax3.set_xlim(-1.2, 1.2)
ax3.set_ylim(-1.2, 1.2)
# (4) ๊ฒฝ๊ณ์ธต ํ๋ฆฌ์ฆ ๊ฒฉ์
ax4 = axes[1, 1]
# ๊ฒฝ๊ณ์ธต ์์ญ (ํ๋ฆฌ์ฆ)
x_wall = np.linspace(0, 2, 20)
y_layers = [0, 0.02, 0.05, 0.1, 0.2, 0.4]
for yl in y_layers:
ax4.plot(x_wall, np.full_like(x_wall, yl), 'b-', linewidth=0.5)
# ์ธ๋ถ ๋น๊ตฌ์กฐ ๊ฒฉ์ (์ผ๊ฐํ)
np.random.seed(123)
outer_points = np.random.rand(20, 2)
outer_points[:, 0] *= 2
outer_points[:, 1] = outer_points[:, 1] * 0.5 + 0.4
tri_outer = Delaunay(outer_points)
ax4.triplot(outer_points[:, 0], outer_points[:, 1], tri_outer.simplices,
'g-', linewidth=0.5)
ax4.fill_between(x_wall, 0, y_layers[-1], alpha=0.2, color='blue',
label='๊ฒฝ๊ณ์ธต (ํ๋ฆฌ์ฆ)')
ax4.set_title('ํ์ด๋ธ๋ฆฌ๋ ๊ฒฉ์ (ํ๋ฆฌ์ฆ + ์ผ๊ฐํ)')
ax4.set_aspect('equal')
ax4.set_xlim(-0.1, 2.1)
ax4.set_ylim(-0.05, 1)
ax4.legend()
plt.tight_layout()
plt.savefig('mesh_types.png', dpi=150, bbox_inches='tight')
plt.show()
# mesh_types_visualization()
6.2 ๋๋ฅ ๋ชจ๋ธ๋ง¶
์ฃผ์ ๋๋ฅ ๋ชจ๋ธ:
1. RANS (Reynolds-Averaged Navier-Stokes):
- k-ฮต ๋ชจ๋ธ: ๋ฒ์ฉ, ์ฐ์
ํ์ค
- k-ฯ ๋ชจ๋ธ: ๋ฒฝ๋ฉด ๊ทผ์ฒ ์ ํ
- SST ๋ชจ๋ธ: k-ฮต + k-ฯ ์ฅ์ ๊ฒฐํฉ
2. LES (Large Eddy Simulation):
- ํฐ ์๋ฅ๋ ์ง์ ๊ณ์ฐ
- ์์ ์๋ฅ๋ ๋ชจ๋ธ๋ง (SGS ๋ชจ๋ธ)
3. DNS (Direct Numerical Simulation):
- ๋ชจ๋ ๋๋ฅ ์ค์ผ์ผ ์ง์ ๊ณ์ฐ
- Reยณ์ ๋น๋กํ๋ ๊ณ์ฐ ๋น์ฉ
์ ํ ๊ธฐ์ค:
- ์ ํ๋: DNS > LES > RANS
- ๊ณ์ฐ ๋น์ฉ: DNS > LES > RANS
- ์ค์ฉ์ฑ: RANS > LES > DNS
7. ์ฐ์ต ๋ฌธ์ ¶
์ฐ์ต 1: ๋ ์ด๋์ฆ ์ ๊ณ์ฐ¶
์ง๊ฒฝ 5cm ๊ด์์ ๋ฌผ(20ยฐC)์ด ํ๊ท ์๋ 2m/s๋ก ํ๋ฅผ ๋ ๋ ์ด๋์ฆ ์๋ฅผ ๊ณ์ฐํ๊ณ ์ ๋ ์ํ๋ฅผ ํ๋ณํ์์ค.
์ฐ์ต 2: Poiseuille ์ ๋¶
Poiseuille ์ ๋์์ ํ๊ท ์๋์ ์ต๋ ์๋์ ๊ด๊ณ๋ฅผ ์ ๋ํ์์ค.
์ฐ์ต 3: ๊ฒฝ๊ณ์ธต ๋๊ป¶
๊ณต๊ธฐ(20ยฐC)๊ฐ 5m/s๋ก ํํ ์๋ฅผ ํ๋ฅผ ๋, ์ ๋จ์์ 10cm ๋จ์ด์ง ์์น์ ์ธต๋ฅ ๊ฒฝ๊ณ์ธต ๋๊ป๋ฅผ ๊ณ์ฐํ์์ค.
์ฐ์ต 4: CFD ๊ฒฉ์ ์์กด์ฑ¶
์ฑ๋ ์ ๋ CFD ์ฝ๋๋ฅผ ์์ ํ์ฌ ๊ฒฉ์ ์๋ฅผ ๋ณํ์ํค๋ฉด์ ๊ฒฉ์ ์๋ ด ํ ์คํธ๋ฅผ ์ํํ์์ค.
8. ์ฐธ๊ณ ์๋ฃ¶
ํต์ฌ ๊ต์ฌ¶
- Versteeg & Malalasekera, "An Introduction to Computational Fluid Dynamics"
- Anderson, "Computational Fluid Dynamics: The Basics with Applications"
- Ferziger & Peric, "Computational Methods for Fluid Dynamics"
CFD ์ํํธ์จ์ด¶
- OpenFOAM (์คํ์์ค)
- ANSYS Fluent (์์ฉ)
- COMSOL Multiphysics (์์ฉ)
- SU2 (์คํ์์ค)
์จ๋ผ์ธ ์๋ฃ¶
- NASA CFD Resources
- CFD Online (ํฌ๋ผ, ํํ ๋ฆฌ์ผ)
- LearnCAx (๋ฌด๋ฃ ๊ฐ์ข)
์์ฝ¶
CFD ๊ธฐ์ด ํต์ฌ:
1. ์ง๋ฐฐ๋ฐฉ์ ์:
- ์ฐ์: โยทu = 0 (๋น์์ถ์ฑ)
- ์ด๋๋: ฯ(Du/Dt) = -โp + ฮผโยฒu
- ์๋์ง: (์์ถ์ฑ ์ ๋)
2. ๋ฌด์ฐจ์ ์:
- Re = ฯUL/ฮผ (๊ด์ฑ/์ ์ฑ)
- Ma = U/a (์์ถ์ฑ)
- Pr = ฮฝ/ฮฑ (์ด๋๋/์ด ํ์ฐ)
3. CFD ์ ์ฐจ:
โ ๊ฒฉ์ ์์ฑ (์ ์ฒ๋ฆฌ)
โก ์ด์ฐํ ๋ฐ ๊ณ์ฐ (์๋ฒ)
โข ํ์ฒ๋ฆฌ (์๊ฐํ, ๋ถ์)
4. ์ฃผ์ ๊ณ ๋ ค์ฌํญ:
- ๊ฒฉ์ ํ์ง ๋ฐ ์๋ ด
- ๊ฒฝ๊ณ์กฐ๊ฑด ์ค์
- ๋๋ฅ ๋ชจ๋ธ ์ ํ
- ์์น ์์ ์ฑ (CFL ์กฐ๊ฑด)
๋ค์ ๋ ์จ์์๋ ๋น์์ถ์ฑ ์ ๋์ ๋ํ์ ๋ฌธ์ ์ธ Lid-Driven Cavity์ SIMPLE ์๊ณ ๋ฆฌ์ฆ์ ๋ค๋ฃน๋๋ค.