Skip to content
cfd-lab:~/ko/posts/2026-03-28-poiseuille-fl…● online
NOTE #013DAY SAT 논문리뷰DATE 2026.03.28UPDATED 2026.10.19READ 4 min read#Analytical-Solution#Classical-Flows#CFD-Verification#Poiseuille#Finite-Difference

포아즈이유 유동: 해석해 유도와 유한차분 검증

표의 ~2.0은 L2 다섯 칸의 비가 아니다. 2차 다항식이면 중앙차분 절단오차는 0이다.

평판 두 장, 간격 2h2h. 압력은 xx로만 떨어지고, 완전 발달이면 v=0v=0, uu는 yy만 남는다. 원관도 같은 이름(Poiseuille flow)으로 불리지만, 아래 식은 2차원 채널이다. 해가 이 포물선에서 벗어나면 점성항이나 no-slip이 틀린 것이다.

−dp/dx와 μ 슬라이더를 움직이면 화면 아래 umax⁡u_{\max}만 바뀌고, 곡선 모양은 그대로다.

압력 구배가 커지거나 점성이 작아지면 포물선이 가팔라진다. Re > 2300 부근부터는 이 해석해가 무의미 — 천이 영역.

그려진 곡선은 u/umax⁡=1−(y/h)2u/u_{\max} = 1 - (y/h)^2다. Re 슬라이더는 구석의 숫자만 옮긴다. 봉우리 값은 h=1h=1일 때 그 슬라이더를 2μ2\mu로 나눈 것이다.

문제 설정#

형상 및 경계 조건#

두 무한 평행 평판 사이의 완전 발달 층류 유동을 고려한다.

  • 채널 반폭: hh (평판은 y=±hy = \pm h에 위치)
  • 유동 방향: xx (무한히 길다고 가정)
  • 경계 조건: 양 벽면에서 no-slip ⇒\Rightarrow u(±h)=0u(\pm h) = 0
  • 구동력: 일정한 압력 구배 dpdx=−G<0\dfrac{dp}{dx} = -G < 0 (G>0G > 0은 상수)

지배 방정식#

완전 발달 유동이므로 ∂/∂x=0\partial/\partial x = 0, v=0v = 0이다. xx-방향 나비에–스토크스 방정식:

ρ(u∂u∂x+v∂u∂y)=−dpdx+μ∂2u∂y2\rho \left( u \frac{\partial u}{\partial x} + v \frac{\partial u}{\partial y} \right) = -\frac{dp}{dx} + \mu \frac{\partial^2 u}{\partial y^2}

완전 발달 조건(∂u/∂x=0\partial u/\partial x = 0, v=0v = 0)을 적용하면:

μd2udy2=dpdx=−G\mu \frac{d^2 u}{dy^2} = \frac{dp}{dx} = -G

해석해 유도#

1단계: ODE로 단순화#

d2udy2=−Gμ≡−C(C>0)\frac{d^2 u}{dy^2} = -\frac{G}{\mu} \equiv -C \quad (C > 0)

2단계: 1차 적분#

dudy=−Cy+A1\frac{du}{dy} = -C y + A_1

3단계: 2차 적분#

u(y)=−C2y2+A1y+A2u(y) = -\frac{C}{2} y^2 + A_1 y + A_2

4단계: 경계 조건 적용#

u(+h)=0u(+h) = 0:

0=−C2h2+A1h+A2(1)0 = -\frac{C}{2} h^2 + A_1 h + A_2 \tag{1}

u(−h)=0u(-h) = 0:

0=−C2h2−A1h+A2(2)0 = -\frac{C}{2} h^2 - A_1 h + A_2 \tag{2}

(1) −- (2): 0=2A1h⇒A1=00 = 2 A_1 h \Rightarrow A_1 = 0 (대칭)

(1) ++ (2): 0=−Ch2+2A2⇒A2=Ch220 = -C h^2 + 2A_2 \Rightarrow A_2 = \dfrac{C h^2}{2}

최종 해석해#

u(y)=G2μ(h2−y2)\boxed{u(y) = \frac{G}{2\mu}\bigl(h^2 - y^2\bigr)}

채널 중심(y=0y=0)에서 최대 속도:

umax⁡=Gh22μu_{\max} = \frac{G h^2}{2\mu}

물리적 해석#

속도 프로파일#

해석해는 포물선(parabola) 분포다. 벽면 마찰이 유동 단면 전체에 균일하게 전달되어, 중심에서 최대, 벽에서 0이 된다.

무차원 속도 u/umax⁡=1−(y/h)2u/u_{\max} = 1 - (y/h)^2는 채널 반폭 hh나 점도 μ\mu에 무관한 보편적 프로파일이다.

벽면 전단 응력#

τw=μdudy∣y=h=μ⋅Gμ(−h)=−Gh\tau_w = \mu \left.\frac{du}{dy}\right|_{y=h} = \mu \cdot \frac{G}{\mu}(-h) = -G h

(크기: τw=Gh\tau_w = G h) 압력 구배가 클수록, 채널이 넓을수록 벽 마찰이 커진다.

체적 유량#

Q=∫−hhu dy=G2μ∫−hh(h2−y2) dy=G2μ⋅4h33=2Gh33μQ = \int_{-h}^{h} u \, dy = \frac{G}{2\mu} \int_{-h}^{h} (h^2 - y^2) \, dy = \frac{G}{2\mu} \cdot \frac{4h^3}{3} = \frac{2 G h^3}{3\mu}

이를 Hagen-Poiseuille 법칙의 2D 버전이라 부른다. 유량이 h3h^3에 비례하므로, 채널이 두 배 넓어지면 유량은 여덟 배 증가한다.

코드 기본값 h=G=μ=1h=G=\mu=1을 같은 식에 넣으면 umax⁡=1/2u_{\max}=1/2, Q=2/3Q=2/3이다. 높이 2h2h로 나누면 평균 속도는 1/31/3이다. 봉우리와의 비는 3/23/2라서, 평균은 중심의 2/32/3다. 1−(y/h)21-(y/h)^2를 −h-h에서 hh까지 적분하고 2h umax⁡2h\,u_{\max}로 나누면 같은 2/32/3가 나온다. hh와 μ\mu는 비에서 빠진다.

hh만 두 배로 키우면 거듭제곱이 갈라진다. umax⁡u_{\max}는 h2h^2이라 4배, QQ는 h3h^3이라 8배, 벽 응력의 크기 GhGh는 2배다. 유량이 여덟 배가 됐다고 응력도 그 비율일 것이라 두면, 벽 값을 네 배 크게 읽는다. 관 단면으로 넘어가 이 포물선의 운동에너지를 평균 속도로 나누면 계수가 달라진다. 층류 원관에서 그 계수가 2.0000으로 고정되는 계산은 속도수두 앞의 계수에 있다.

Python으로 해석해 시각화#

import numpy as np
import matplotlib.pyplot as plt
 
# 파라미터
h   = 1.0    # 채널 반폭 [m]
G   = 1.0    # 압력 구배 [Pa/m]
mu  = 1.0    # 동점도 [Pa·s]
 
# 격자
y = np.linspace(-h, h, 400)
u_exact = G / (2 * mu) * (h**2 - y**2)
u_max   = G * h**2 / (2 * mu)
 
fig, axes = plt.subplots(1, 2, figsize=(10, 5))
 
# 속도 프로파일 (가로형)
axes[0].plot(u_exact / u_max, y / h, "k-", lw=2)
axes[0].fill_betweenx(y / h, u_exact / u_max, alpha=0.15)
axes[0].axvline(0, color="gray", lw=0.8, ls="--")
axes[0].set_xlabel(r"$u / u_{\max}$")
axes[0].set_ylabel(r"$y / h$")
axes[0].set_title("Poiseuille 속도 프로파일")
axes[0].set_xlim(-0.05, 1.1)
 
# 전단 응력 분포
tau = mu * np.gradient(u_exact, y)
axes[1].plot(tau / G / h, y / h, "r-", lw=2, label=r"$\tau / \tau_w$")
axes[1].axvline(0, color="gray", lw=0.8, ls="--")
axes[1].set_xlabel(r"$\tau / \tau_w$")
axes[1].set_ylabel(r"$y / h$")
axes[1].set_title("전단 응력 분포 (선형)")
axes[1].legend()
 
plt.tight_layout()
plt.savefig("poiseuille_profile.png", dpi=150)
plt.show()

유한차분법으로 수치 검증#

FD 이산화#

d2u/dy2=−G/μd^2u/dy^2 = -G/\mu를 2차 중앙차분으로 이산화한다. 균일 격자 Δy=2h/(N−1)\Delta y = 2h/(N-1):

ui+1−2ui+ui−1(Δy)2=−Gμ\frac{u_{i+1} - 2u_i + u_{i-1}}{(\Delta y)^2} = -\frac{G}{\mu}

경계 조건: u0=uN−1=0u_0 = u_{N-1} = 0.

이를 삼대각 행렬(tridiagonal) 연립방정식으로 쓰면:

(−211−21⋱⋱⋱1−2)u=−G(Δy)2μ1\begin{pmatrix} -2 & 1 \\ 1 & -2 & 1 \\ & \ddots & \ddots & \ddots \\ & & 1 & -2 \end{pmatrix} \mathbf{u} = -\frac{G (\Delta y)^2}{\mu} \mathbf{1}

Python 구현#

import numpy as np
 
def solve_poiseuille_fd(N, h=1.0, G=1.0, mu=1.0):
    """
    N : 내부 격자점 수 (경계 제외)
    returns (y, u_num, u_exact, L2_error)
    """
    dy = 2 * h / (N + 1)
    y_inner = np.linspace(-h + dy, h - dy, N)
 
    # 삼대각 계수 행렬
    diag  = -2 * np.ones(N)
    upper =  np.ones(N - 1)
    lower =  np.ones(N - 1)
    A = (np.diag(diag) + np.diag(upper, k=1) + np.diag(lower, k=-1))
 
    rhs = -G * dy**2 / mu * np.ones(N)
 
    u_num = np.linalg.solve(A, rhs)
 
    # 경계 포함 전체 격자
    y_full  = np.concatenate([[-h], y_inner, [h]])
    u_full  = np.concatenate([[0.0], u_num, [0.0]])
    u_exact = G / (2 * mu) * (h**2 - y_full**2)
 
    L2 = np.sqrt(np.mean((u_full - u_exact)**2))
    return y_full, u_full, u_exact, L2
 
# 격자 수렴 테스트
Ns = [4, 8, 16, 32, 64, 128, 256]
errors = []
for N in Ns:
    _, _, _, err = solve_poiseuille_fd(N)
    errors.append(err)
 
# 수렴 차수
for i in range(1, len(Ns)):
    ratio = errors[i-1] / errors[i]
    order = np.log2(ratio)
    print(f"N={Ns[i]:4d}  L2={errors[i]:.3e}  수렴차수={order:.2f}")

수렴 결과#

NNΔy\Delta yL2L_2 오차수렴 차수
40.40002.78e-16—
80.22221.67e-16~2.0
160.11762.22e-16~2.0
320.06062.78e-16~2.0
640.03082.22e-16~2.0

참고: 포아즈이유 유동은 2차 다항식이므로 2차 중앙차분으로 기계적 정밀도(machine precision) 수준의 정확도를 얻는다. 실제 오차는 Δy2\Delta y^2 비례가 아니라 부동소수점 반올림 오차 수준에서 포화된다.

낮은 NN에서도 포물선은 식에 붙는다. 점성항이 죽었는지는 여기서 바로 보인다. 차수 열은 그 판정에 쓸 수 없다.

이산화 문장은 Δy=2h/(N−1)\Delta y = 2h/(N-1)라고 적었다. 표의 간격은 N=4N=4에서 0.4000, N=8N=8에서 0.2222, N=16N=16에서 0.1176, N=32N=32에서 0.0606, N=64N=64에서 0.0308이다. h=1h=1이면 2/(N+1)2/(N+1)과 같다. 2/5=0.42/5=0.4, 2/9≈0.22222/9 \approx 0.2222, 2/17≈0.11762/17 \approx 0.1176, 2/33≈0.06062/33 \approx 0.0606, 2/65≈0.03082/65 \approx 0.0308. 코드는 양 벽을 미지수에서 빼서 내부 NN칸을 2h/(N+1)2h/(N+1)로 나눈다. N−1N-1은 경계점까지 미지수로 센 개수다.

L2L_2 다섯 칸은 2.78×10−162.78\times 10^{-16}, 1.67×10−161.67\times 10^{-16}, 2.22×10−162.22\times 10^{-16}, 2.78×10−162.78\times 10^{-16}, 2.22×10−162.22\times 10^{-16}이다. 인접 비의 이진로그는 약 0.7, 음수, 음수, 약 0.3이다. 표의 ~2.0과 다르다. yy의 2차식은 3계 도함수가 0이라 2차 중앙차분의 절단오차가 0이다. 이 열로 차수를 재지 못한다.

해석 결과 비교 플롯#

import matplotlib.pyplot as plt
 
fig, ax = plt.subplots(figsize=(6, 5))
 
colors = plt.cm.viridis(np.linspace(0.2, 0.9, 4))
Ns_plot = [4, 8, 16, 64]
 
for N, c in zip(Ns_plot, colors):
    y, u_num, _, _ = solve_poiseuille_fd(N)[:3]
    _, _, u_exact, _ = solve_poiseuille_fd(N)
    ax.plot(u_num, y, "o--", color=c, ms=4, label=f"FD N={N}")
 
# 해석해
y_fine = np.linspace(-1, 1, 400)
ax.plot(0.5 * (1 - y_fine**2), y_fine, "k-", lw=2, label="해석해")
 
ax.set_xlabel(r"$u$ (무차원)")
ax.set_ylabel(r"$y/h$")
ax.set_title("FD vs 해석해 비교")
ax.legend(fontsize=9)
plt.tight_layout()
plt.savefig("poiseuille_fd_vs_exact.png", dpi=150)
plt.show()

OpenFOAM 설정 힌트#

포아즈이유 유동을 OpenFOAM icoFoam으로 설정할 때 핵심 파라미터:

0/U — 속도 경계 조건#

boundaryField
{
    walls
    {
        type    noSlip;         // 벽면 no-slip
    }
    inlet
    {
        type    fixedValue;
        value   uniform (1 0 0); // 또는 parabolicVelocity
    }
    outlet
    {
        type    zeroGradient;
    }
}

0/p — 압력 경계 조건#

boundaryField
{
    inlet   { type fixedValue; value uniform 1; }
    outlet  { type fixedValue; value uniform 0; }
    walls   { type zeroGradient; }
}

constant/transportProperties#

nu              nu [ 0 2 -1 0 0 0 0 ] 0.01;  // 동점성계수 조정

발달 길이와 중심 속도#

  1. 완전 발달 조건: 채널 길이가 입구 발달 길이(Ldev≈0.05 Re⋅hL_{dev} \approx 0.05 \, Re \cdot h)보다 충분히 길어야 함
  2. 격자 독립성: yy-방향 격자 수 16 이상이면 2차 정확도 확보
  3. 검증 지표: 중심선 속도 uc=Gh2/(2μ)u_c = G h^2 / (2\mu)와 벽면 전단 응력 τw=Gh\tau_w = G h 비교

L2가 1e-16이면 차수 칸을 볼 것인가#

N=4N=4에서 L2L_2는 이미 2.78×10−162.78\times 10^{-16}이다. 격자를 64까지 넣어도 그 칸은 10−1610^{-16}대에서 오르내린다. ~2.0은 그 비에서 나오지 않았다. 2차 다항식 위에서 중앙차분은 절단오차가 0이다. 차수 실험으로 합격 도장을 찍는 판단이 여기서 틀린다. 점성항 μ∇2u\mu \nabla^2 \mathbf{u}가 살아 있는지는 중심 속도와 벽 응력으로 본다.

볼 숫자는 Gh2/(2μ)Gh^2/(2\mu)와 GhGh다. 오더가 갈라지는 데이터는 불연속이 있는 해 쪽에 있다. 같은 코드가 0.79차와 2.00차를 동시에 낸 경우는 차수 칸이 스킴 차수가 아닐 수 있음을 따로 잰다. 잔차가 반올림에 붙어 있으면 격자를 더 넣어도 차수는 나오지 않는다.


참고 문헌

  • Batchelor, G. K. An Introduction to Fluid Dynamics. Cambridge, 1967.
  • Ferziger, J. H., Perić, M. Computational Methods for Fluid Dynamics. Springer, 2002.
  • OpenFOAM Documentation — icoFoam tutorial: cavity

도움이 됐다면 공유해주세요.