두 함수를 더했더니 원기둥이 나타났다 — 복소 포텐셜과 d'Alembert 패러독스
평행 흐름 + 작은 점 하나로 원기둥 주위 흐름이 자동으로 그려지는 이유.
두 함수를 더하기만 했는데 원기둥이 나타난다. 평행한 흐름에 점 하나의 작은 와류를 얹으면, 누구도 동그라미를 그리지 않았는데 한가운데에 매끈한 원 하나가 떠오른다. 이 글은 그 마법이 사실은 19세기 복소함수론의 한 줄이라는 이야기다. Cauchy–Riemann 관계식이 왜 2차원 비점성 유동의 뼈대이고, 평행 흐름 에 doublet 를 더한 단 하나의 식이 어떻게 원기둥 주위 흐름을 만들어내며, 그 결과가 1752년 d'Alembert를 절망시킨 "저항 0"의 모순까지 이어지는지를 따라간다. 끝에는 NumPy 60줄로 그 그림을 직접 그려 본다.
유선과 등포텐셜이 직교하는 까닭#
2차원 비압축·비점성·비회전 유동에는 두 가지 스칼라 함수가 동시에 정의된다. 속도 포텐셜 와 유동함수 다. 둘은 다음 식으로 속도와 묶인다.
여기서 는 각각 방향 속도다. 등포텐셜선 의 기울기는 , 유선 의 기울기는 . 두 기울기의 곱이 이다. 두 곡선은 어디서나 직교한다.
직교 격자라는 것은 단지 그림의 모양새가 아니다. 정칙함수(regular function)의 실수부·허수부가 가지는 성질 그 자체다. 미분이 방향과 무관하게 동일한 값으로 수렴해야 한다는 단 한 줄이 이 두 함수를 한 쌍으로 묶었다.
복소속도포텐셜 — Cauchy-Riemann이 깐 다리#
복소수 를 변수로 하는 함수 를 생각하자. 가 미분 가능하려면, 로 다가가는 모든 방향에서 미분계수가 같아야 한다. 실수 축으로 다가가는 경우와 허수 축으로 다가가는 경우를 같게 놓으면 두 줄이 나온다.
Cauchy–Riemann 관계식이다. 그런데 같은 식이 바로 위에서 우리가 본 포텐셜 유동의 속도 정의 그 자체다. 2차원 비회전 유동의 와 를 실·허수부로 묶으면, 그 자체로 정칙함수가 된다. 를 우리는 복소속도포텐셜이라 부른다.
복소 미분 도 의미를 가진다. 전미분에서 . 따라서
가 된다. 이 값은 복소속도다. 켤레 복소수 의 형태로 실제 속도 벡터를 그대로 들고 있다.
단 한 줄 — 가 그리는 원기둥#
가장 단순한 두 흐름의 복소 포텐셜은 이렇다. 평행 흐름 . 풀어 쓰면 . 등포텐셜은 수직선, 유선은 수평선이다. Doublet(이중용출, 거리가 0인 source–sink 한 쌍의 극한) . 극좌표로 , 즉 , .
두 식을 더한다.
극좌표로 풀면
여기서 은 doublet 강도와 자유 흐름의 비로 결정되는 길이 스케일이다. 인 자리를 찾자. (곧 -축)이거나 이다. 후자가 결정적이다. 반지름 인 원이 통째로 하나의 유선이다.
비점성 가정 아래에서는 어느 유선이든 고체 벽으로 바꾸어도 그 바깥 유동은 변하지 않는다. 그러니 원을 그대로 원기둥 벽으로 본다. 평행 흐름과 점 하나의 doublet — 두 개의 단순 항을 더한 것이 균일류 속에 놓인 원기둥의 정확한 비점성 해다. 아무도 동그라미를 그리지 않았는데 동그라미가 나타나는 이유다.
아래 시뮬레이션에서 직접 조작해보자.
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).
U와 m을 함께 키우면 원의 반지름 가 바뀐다. Γ를 0이 아닌 값으로 옮기면 위·아래 대칭이 깨진다 — 다음 절의 양력이다. stream과 equipotential을 동시에 켜 보면 두 곡선이 격자처럼 직교하는 모습이 그대로 보인다.
순환 한 줄을 더하면 양력이 생긴다#
원기둥 표면에서의 속도 분포는 위·아래 대칭이다. Bernoulli 정리로 압력도 대칭이다. 합력은 0. 이것이 다음 절의 d'Alembert 패러독스다. 비대칭을 만들려면 시계방향이든 반시계방향이든 순환(circulation, 표면 둘레를 따라 적분한 접선속도)이 있어야 한다. 점 와류의 복소 포텐셜은 . 이걸 더한다.
이제 원기둥 위쪽 속도가 아래쪽보다 빠르다. 압력이 위가 낮고 아래가 높다. 양력이 생긴다. Kutta–Joukowski 정리는 단위 폭당 양력이 임을 알려준다 — 1902년 Kutta, 1906년 Joukowski가 독립적으로 얻은 결과다. 위 시뮬레이션에서 Γ 슬라이더가 만들어내는 비대칭이 바로 이것이다.
중첩 — 모든 비점성 흐름은 더하기다#
가 만족하는 Laplace 방정식 은 선형이다. 두 개의 해를 더한 것도 또 다른 해다. 이 단순한 사실이 19세기 유체역학의 거의 절반을 떠받친다.
기본 빌딩 블록은 네 가지. Uniform(평행 흐름) . Source/Sink(용출/흡입) . Vortex(점 와류) . Doublet . 이 넷을 적당히 배치하면 반무한체(half-body), Rankine oval, 회전하는 원기둥이 차례로 만들어진다.
Each preset is a sum of elementary flows. The same machinery — superposition of ψ — builds half-bodies, ovals, and lifting cylinders.
Cylinder = U + doublet은 위에서 본 그 그림이다. Rankine half-body는 평행 흐름 + 한 점의 source — 안쪽에 솟아오른 코 모양의 정체점(stagnation point)이 생긴다. Rankine oval은 source와 sink를 한 줄에 놓아 닫힌 도형을 만든다. Lifting cylinder는 위 두 그림에 와류를 더해 위·아래 비대칭을 만든다.
d'Alembert 패러독스 — 0 저항이 가르친 것#
위 식 로 원기둥 표면의 압력을 적분하면 정확히 저항 0이 나온다. 1752년 d'Alembert가 직접 유도한 결과다. 그 자신이 모순을 알고 있었다. 누구나 손으로 물속에서 막대를 끌어 보면 저항이 느껴진다. 비점성·비회전·비압축 가정만으로는 이 저항을 만들어낼 수 없다.
해결은 거의 한 세기 반이 걸렸다. 1904년 Prandtl이 경계층(boundary layer, 벽 가까이에서 점성이 지배하는 얇은 층)을 발견했다. 점성은 작아도 0이 아니다. 벽 근처에서 작용해 얇은 경계층을 만들고, 그것이 후류에서 박리(separation, 흐름이 벽에서 떨어져 나가는 현상)와 와류를 유발해 비대칭 압력 분포를 만든다. 그 비대칭이 저항이다.
d'Alembert의 0 저항은 잘못된 답이 아니다. 점성을 무시한 유체에 대한 정확한 답이다. 그 답이 자연의 답과 다르다는 사실이 근대 유체역학의 문을 열었다.
NumPy 60줄 — 를 따라 그리기#
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저항은 항상 0이다. 양력은 에 정확히 맞춰진다 (이므로 ). d'Alembert는 옳았다. 잘못된 것은 가정이었다.
기억할 세 가지#
- 평행 흐름 + doublet이라는 단 두 항의 복소 포텐셜 는 균일류 속 원기둥의 정확한 비점성 해다 — 아무도 동그라미를 그리지 않았는데 동그라미가 나타난다.
- Cauchy–Riemann 관계식은 그저 미분 가능성의 조건이 아니라, 2차원 비회전 유동의 와 가 한 쌍으로 묶이는 이유다.
- 비점성 해의 저항이 0이라는 d'Alembert 패러독스는 잘못된 답이 아니다. 점성을 빼면 자연이 비대칭 압력장을 만들지 못한다는 사실의 정직한 그림이고, 그것이 경계층 이론의 출발선이 되었다.
도움이 됐다면 공유해주세요.