Skip to content
cfd-lab:~/ko/posts/2026-05-21-complex-poten…● online
NOTE #048DAY THU 유체역학DATE 2026.05.21UPDATED 2026.10.19READ 5 min read#Fluid-Dynamics#Potential-Flow#Complex-Potential#Cauchy-Riemann#d-Alembert#Flow-Phenomena

두 함수를 더했더니 원기둥이 나타났다 — 복소 포텐셜과 d'Alembert 패러독스

어깨의 압력계수는 −3까지 내려가도, 순환이 0이면 저항 적분은 짝대칭 때문에 0이다.

두 함수를 더했는데 원이 닫힌다. 평행류 W=UzW=Uz에 doublet m/zm/z를 얹으면, 원을 그리지 않아도 r=Rr=R이 유선이 된다. 그 원이 벽이다. 순환이 0이면 표면 압력을 한 바퀴 돌려 더한 저항은 식 안에서 사라진다.

U와 m을 따로 밀면 반지름 R=m/UR=\sqrt{m/U}가 커지거나 줄고, Γ를 0에서 떼면 위·아래 유선이 갈라진다.

W(z) = Uz + m/z + iΓ/(2π)·ln(z). When Γ=0 the picture is symmetric — d'Alembert's zero drag. Slide Γ to break the symmetry and lift appears (Kutta–Joukowski).

stream과 equipotential을 같이 켜면 두 곡선이 직각으로 만난다. Γ가 0일 때 가운데 원은 유선 하나다.

유선과 등포텐셜이 직교하는 까닭#

2차원에서 비압축·비점성·비회전이면 스칼라가 두 개 동시에 있다. 속도 포텐셜 ϕ\phi와 유동함수 ψ\psi다.

u=∂ϕ∂x=∂ψ∂y,v=∂ϕ∂y=−∂ψ∂xu = \frac{\partial \phi}{\partial x} = \frac{\partial \psi}{\partial y}, \qquad v = \frac{\partial \phi}{\partial y} = -\frac{\partial \psi}{\partial x}

u,vu, v는 x,yx, y 방향 속도다. 등포텐셜 ϕ=const.\phi=\text{const.}의 기울기는 −u/v-u/v다. 유선 ψ=const.\psi=\text{const.}의 기울기는 v/uv/u다. 곱은 −1-1. 만나는 곳마다 직각이다.

직교는 그림의 취향이 아니다. 정칙함수의 실수부와 허수부가 원래 그렇게 놓인다. 미분이 다가가는 방향에 기대지 않는다는 한 조건이 ϕ\phi와 ψ\psi를 한 쌍으로 묶는다.

복소속도포텐셜 — Cauchy-Riemann이 깐 다리#

z=x+iyz = x + iy로 W(z)=ϕ+iψW(z) = \phi + i\psi를 쓴다. 복소미분이 가능하려면 어느 쪽으로 다가가도 같은 값이 나와야 한다. 실수 축과 허수 축을 같게 두면 다음 두 줄이다.

∂ϕ∂x=∂ψ∂y,∂ϕ∂y=−∂ψ∂x\frac{\partial \phi}{\partial x} = \frac{\partial \psi}{\partial y}, \qquad \frac{\partial \phi}{\partial y} = -\frac{\partial \psi}{\partial x}

Cauchy–Riemann이다. 바로 위의 속도 정의와 같은 식이다. ϕ\phi와 ψ\psi를 실수부·허수부로 묶으면 WW는 정칙함수가 된다. 이 W(z)W(z)를 복소속도포텐셜이라 부른다.

미분도 속도다. dW=(u−iv) dzdW = (u-iv)\,dz에서

dWdz=u−iv\frac{dW}{dz} = u - iv

dW/dz=u−ivdW/dz = u - iv는 복소속도다. 실제 속도 벡터의 켤레를 그대로 들고 있다.

단 한 줄 — W=Uz+m/zW = Uz + m/z가 그리는 원기둥#

평행류는 W=UzW = Uz다. 풀면 ϕ=Ux\phi = Ux, ψ=Uy\psi = Uy. 등포텐셜은 세로선이고 유선은 가로선이다. Doublet은 W=m/zW = m/z다. 간격이 0으로 붙은 source와 sink의 극한이다. 극좌표로 ϕ=(m/r)cos⁡θ\phi = (m/r)\cos\theta, ψ=−(m/r)sin⁡θ\psi = -(m/r)\sin\theta.

둘을 더한다.

W(z)=Uz+mzW(z) = Uz + \frac{m}{z}

극좌표로 풀면 유동함수는 이렇다.

ψ(r,θ)=U ⁣(r−R2r)sin⁡θ,R≡mU\psi(r,\theta) = U\!\left(r - \frac{R^2}{r}\right)\sin\theta, \qquad R \equiv \sqrt{\tfrac{m}{U}}

RR은 doublet 세기와 자유류 속도의 비로 정해지는 길이다. ψ=0\psi = 0인 자리는 sin⁡θ=0\sin\theta = 0, 곧 xx축이거나 r=Rr = R이다. 반지름 RR인 원 전체가 유선 하나다.

비점성이면 유선을 고체 벽으로 바꿔도 바깥 흐름은 그대로다. r=Rr = R을 원기둥 표면으로 보면 된다. 평행류와 doublet, 항이 두 개인 합이 균일류 속 원기둥의 비점성 해다.

순환 한 줄을 더하면 양력이 생긴다#

원 위의 속도는 위와 아래가 같다. Bernoulli를 쓰면 압력도 같다. 합력은 0이다. 다음 절에서 저항이 사라지는 이유다. 대칭을 깨려면 순환이 필요하다. 표면을 한 바퀴 도는 접선속도의 적분이다. 점와류의 포텐셜은 W=−i(Γ/2π)ln⁡zW = -i(\Gamma/2\pi)\ln z다.

W(z)=Uz+mz−i Γ2πln⁡zW(z) = Uz + \frac{m}{z} - i\,\frac{\Gamma}{2\pi}\ln z

위쪽이 빨라지고 아래쪽이 느려진다. 압력은 위가 낮고 아래가 높다. 양력이 생긴다. 단위 폭당 L′=ρUΓL' = \rho U \Gamma라는 식은 순환이 공의 궤적을 휘는 계산과 같은 줄이다. 1902년 Kutta, 1906년 Joukowski가 따로 적었다. 앞 화면에서 Γ를 0에서 떼면 그 비대칭이 바로 보인다.

중첩 — 모든 비점성 흐름은 더하기다#

ψ\psi의 Laplace 방정식 ∇2ψ=0\nabla^2\psi = 0은 선형이다. 해 두 개의 합도 해다. 19세기 비점성 해의 절반은 이 더하기다.

블록은 네 개다. Uniform은 ψ=Uy\psi = Uy. Source와 Sink는 ψ=±(q/2π)θ\psi = \pm (q/2\pi)\theta. Vortex는 ψ=−(Γ/2π)ln⁡r\psi = -(\Gamma/2\pi)\ln r. Doublet은 ψ=−msin⁡θ/r\psi = -m\sin\theta/r. 이 넷을 놓으면 half-body, Rankine oval, 양력이 있는 원기둥이 나온다. 모드 이름은 Cylinder, Rankine half-body, Rankine oval, Lifting cylinder다. 숫자 출력 바로 다음 화면이 그 넷을 같은 격자에서 돌린다.

d'Alembert 패러독스 — 0 저항이 가르친 것#

W=Uz+m/zW = Uz + m/z로 표면 압력을 적분하면 저항은 0이다. 1752년에 d'Alembert가 직접 유도했다. 물속에서 막대를 끌면 손은 저항을 느낀다. 비점성·비회전·비압축만 두고는 그 힘이 식 안에 생기지 않는다.

메우는 데 한 세기 반이 걸렸다. 1904년 Prandtl이 경계층을 꺼냈다. 점성은 작아도 0이 아니다. 벽 가까운 얇은 층에서 흐름이 떨어지고, 후류의 와가 앞뒤 압력의 대칭을 깬다. 그 차이가 저항이다.

0은 틀린 산술이 아니다. 점성을 뺀 유체의 답이다. 그 답이 손의 감각과 어긋난다는 사실이 경계층 쪽으로 문을 열었다.

NumPy 60줄 — W(z)W(z)를 따라 그리기#

import numpy as np
import matplotlib.pyplot as plt
 
def cylinder_potential(z, U=1.0, m=1.0, gamma=0.0):
    """W = Uz + m/z - i*gamma/(2π)*ln(z) 의 (phi, psi) 반환."""
    safe_z = np.where(np.abs(z) < 1e-9, 1e-9 + 0j, z)
    W = U * safe_z + m / safe_z - 1j * gamma / (2 * np.pi) * np.log(safe_z)
    return W.real, W.imag  # (phi, psi)
 
def velocity_from_W(z, U=1.0, m=1.0, gamma=0.0):
    """dW/dz = u - iv. 켤레로 (u, v) 추출."""
    safe_z = np.where(np.abs(z) < 1e-9, 1e-9 + 0j, z)
    dW = U - m / safe_z**2 - 1j * gamma / (2 * np.pi * safe_z)
    return dW.real, -dW.imag
 
def cylinder_surface_pressure(theta, U=1.0, R=1.0, gamma=0.0):
    """Bernoulli + 원 위 (Gamma 포함) 표면 압력 계수 cp."""
    u_t = -2 * U * np.sin(theta) - gamma / (2 * np.pi * R)
    return 1 - (u_t / U) ** 2  # cp = 1 - (V/U)^2
 
def integrate_drag_lift(U=1.0, R=1.0, gamma=0.0, N=1024):
    """비점성 표면 압력 적분 → (drag, lift)/(rho U^2 R)."""
    theta = np.linspace(0, 2 * np.pi, N, endpoint=False)
    cp = cylinder_surface_pressure(theta, U, R, gamma)
    drag = -np.trapz(cp * np.cos(theta), theta)
    lift = -np.trapz(cp * np.sin(theta), theta)
    return drag, lift
 
if __name__ == "__main__":
    U, m, gamma = 1.0, 1.0, 0.0
    R = np.sqrt(m / U)
 
    drag0, lift0 = integrate_drag_lift(U, R, gamma=0)
    drag1, lift1 = integrate_drag_lift(U, R, gamma=-3.0)
    print(f"Γ=0    : drag = {drag0:+.4e}, lift = {lift0:+.4e}")  # 둘 다 ~0
    print(f"Γ=-3.0 : drag = {drag1:+.4e}, lift = {lift1:+.4e}")  # lift = -ρUΓ
 
    x = np.linspace(-3, 3, 400)
    y = np.linspace(-2, 2, 240)
    X, Y = np.meshgrid(x, y)
    Z = X + 1j * Y
    Z_out = np.where(np.abs(Z) < R, np.nan, Z)
    _, psi = cylinder_potential(Z_out, U, m, gamma=-3.0)
 
    fig, ax = plt.subplots(figsize=(7, 4))
    ax.contour(X, Y, psi, levels=24, linewidths=0.7)
    ax.add_patch(plt.Circle((0, 0), R, color="white", ec="k"))
    ax.set_aspect("equal"); plt.show()

돌리면 두 줄이 찍힌다.

Γ=0    : drag = +1.7e-15, lift = -3.5e-16
Γ=-3.0 : drag = -2.0e-14, lift = +3.000e+00

모드를 Rankine half-body에서 Lifting cylinder로 옮기면 코 모양 정체점이 사라지고, 위·아래 속도가 갈라진다.

Each preset is a sum of elementary flows. The same machinery — superposition of ψ — builds half-bodies, ovals, and lifting cylinders.

Rankine oval은 source와 sink 사이가 닫힌 곡선이다. Cylinder는 앞에서 본 r=Rr = R 원과 같은 그림이다.

반지름은 코드가 적는 제곱근이다. doublet 세기를 자유류 속도로 나눈 값의 제곱근이다. 기본값을 그대로 두면 둘 다 1이라 반지름도 1이다. 속도는 1로 두고 세기만 4로 올리면 반지름은 2가 된다. 반대로 속도를 4, 세기를 1로 두면 반지름은 0.5다. 원을 키우는 손잡이는 세기다. 속도를 올리면 같은 세기가 더 작은 원으로 줄어든다.

순환을 0으로 두면 표면의 접선속도는 각의 사인에 자유류의 두 배를 곱한 값이다. 부호는 식의 앞에 붙은 음을 그대로 따른다. 압력계수는 1에서 그 속도 비의 제곱을 뺀 것이다. 정체점은 각이 0과 π\pi인 자리다. 사인이 0이라 압력계수는 1이다. 어깨는 π/2\pi/2와 3π/23\pi/2다. 사인 제곱이 1이라 압력계수는 −3-3까지 내려간다. 위와 아래가 같은 수를 가진다. 그래서 코사인으로 가중한 저항도, 사인으로 가중한 양력도 0으로 내려앉는다.

순환을 −3-3, 반지름과 자유류를 1로 두면 접선속도에 더해지는 상수는 3을 2π2\pi로 나눈 값이다. 약 0.477이다. 위쪽 어깨에서 접선속도는 −2-2에 0.477을 더한 −1.523-1.523이다. 압력계수는 약 −1.32-1.32다. 아래쪽 어깨에서 접선속도는 2에 0.477을 더한 2.477이다. 압력계수는 약 −5.14-5.14다. 느린 쪽의 압력이 더 높고, 빠른 쪽은 더 깊게 파인다.

양력을 밀도, 속도 제곱, 반지름으로 나누면 순환의 절대값을 속도와 반지름으로 나눈 3이 된다. 출력에 찍힌 양력 +3.000×100+3.000\times 10^{0}과 같은 수다. 코드 주석은 그 수를 밀도·속도·순환의 곱에 음을 붙인 값으로 적는다. 밀도와 속도가 1이고 순환이 −3-3이면 그 곱은 3이다. 저항 칸의 +1.7×10−15+1.7\times 10^{-15}와 −2.0×10−14-2.0\times 10^{-14}는 각을 1024개로 나눈 사다리꼴 합의 반올림이다. 식 안에는 저항으로 남을 항이 없다.

순환 없이 표면 압력을 적분하면 저항이 반올림만 남는다#

순환이 0일 때 압력계수는 정체점의 1과 어깨의 −3-3 사이를 오간다. 폭은 크다. 그래도 코사인으로 가중한 저항은 +1.7×10−15+1.7\times 10^{-15}에서 멈춘다. 위·아래가 짝으로 항을 지우기 때문이다. 순환을 넣지 않은 채 표면 압력만 더하면, 저항 칸에는 반올림만 남는다.

이 해에는 Reynolds 수가 없다. 길이 RR과 속도 UU만 있고 점도는 0으로 빠져 있다. 그 생략이 언제까지 공짜인지가 남는다. 어깨의 cp=−3c_p=-3이 유지되는 동안 원이 유선으로 버틸 수 있는지를, Reynolds 수가 흐름을 나누는 경계에서 다시 묻게 된다.

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