Skip to content
cfd-lab:~/ko/posts/2026-06-30-van-der-waals…online
NOTE #090DAY TUE 유체역학DATE 2026.06.30READ 5 min readWORDS 2,436#Fluid-Mechanics#Equation-of-State#Van-der-Waals#Compressibility#Supercritical

이상기체가 거짓말을 시작하는 곳 — 압축인자 Z와 van der Waals 상태방정식

압축인자, 대응 상태 원리, van der Waals 루프로 보는 실제 기체

100기압, 200 K의 질소를 이상기체 법칙으로 계산하면 밀도를 실제보다 4분의 1이 넘게 틀린다. 같은 압력·온도인데도 분자들은 서로를 끌어당기고, 자기 부피만큼 공간을 차지한다. 이 글은 그 어긋남을 하나의 숫자(압축인자 ZZ)로 잡는 법, van der Waals가 두 개의 상수로 실제 기체를 흉내 낸 방식, 그리고 임계점 아래에서 나타나는 S자 곡선이 어떻게 액체와 기체를 가르는지를 다룬다. 끝에서는 이 이야기가 로켓 인젝터의 초임계 유동과 CFD 격자에 왜 직접 영향을 주는지 본다.

100기압의 질소는 이상기체보다 무겁다#

이상기체 법칙 Pv=RTPv = RT는 분자를 점으로 보고, 분자 사이 힘을 무시한다. 희박한 기체에서는 잘 맞는다. 분자가 띄엄띄엄 떨어져 있기 때문이다.

압력을 올리면 사정이 달라진다. 분자들이 가까워진다. 약한 인력(van der Waals 힘)이 분자를 서로 끌어당겨 부피를 줄인다. 동시에 분자 자신의 부피가 무시할 수 없게 된다.

두 효과는 반대 방향이다. 인력은 부피를 줄이고, 유한 부피는 부피를 키운다. 어느 쪽이 이기느냐는 온도와 압력이 정한다. 그래서 "실제 기체는 이상기체보다 무겁다 또는 가볍다"가 조건마다 뒤집힌다.

압축인자 Z — 어긋남을 하나의 숫자로#

이 어긋남을 한 숫자로 묶은 것이 압축인자다.

Z=PvRT=vactualvidealZ = \frac{Pv}{RT} = \frac{v_\text{actual}}{v_\text{ideal}}

PP는 압력, vv는 몰부피, RR은 기체상수, TT는 온도다. Z=1Z=1이면 이상기체. ZZ가 1에서 멀수록 더 많이 어긋난다.

Z<1Z<1은 인력이 우세하다는 뜻이다. 분자가 서로 끌려 실제 부피가 이상값보다 작다. Z>1Z>1은 유한 부피가 우세하다는 뜻이다. 분자가 밀어내 부피가 커진다. 앞의 질소 예에서 Z0.79Z \approx 0.79였고, 그래서 실제 밀도가 이상값의 1/0.791.271/0.79 \approx 1.27배였다.

대응 상태 원리: 모든 기체가 닮는 좌표#

기체마다 ZZ 곡선은 다르다. 그런데 압력과 온도를 각자의 임계값으로 나누면 곡선이 거의 포개진다.

PR=PPc,TR=TTcP_R = \frac{P}{P_c}, \qquad T_R = \frac{T}{T_c}

PcP_c, TcT_c는 임계 압력·온도다. 대응 상태 원리(principle of corresponding states)는 이렇게 말한다. 환산 압력 PRP_R과 환산 온도 TRT_R이 같으면, 거의 모든 기체의 ZZ가 거의 같다.

질소든 메탄이든 이산화탄소든, TR=1.5T_R=1.5, PR=2P_R=2에서는 비슷한 ZZ를 갖는다. 분자의 종류가 아니라 임계점에 대한 상대 위치가 행동을 정한다. 이 보편성 덕분에 단 하나의 일반화 차트로 수많은 기체를 다룬다.

van der Waals: 두 상수로 분자를 흉내내다#

1873년, van der Waals는 이상기체 법칙에 두 개의 보정을 넣었다.

(P+av2)(vb)=RT\left(P + \frac{a}{v^2}\right)(v - b) = RT

a/v2a/v^2는 분자 간 인력이 만드는 압력 감소다. 밀도가 높을수록(부피가 작을수록) 인력이 강해진다. bb는 분자 자신이 차지하는 부피라서, 자유 공간이 vbv-b로 줄어든다.

두 상수는 임계점에서 결정한다. 임계점에서 PPvv 등온선은 변곡점을 지나며 기울기와 곡률이 동시에 0이다.

(Pv)Tc=0,(2Pv2)Tc=0\left(\frac{\partial P}{\partial v}\right)_{T_c} = 0, \qquad \left(\frac{\partial^2 P}{\partial v^2}\right)_{T_c} = 0

이 두 조건을 풀면 상수와 임계 압축인자가 나온다.

a=27R2Tc264Pc,b=RTc8Pc,Zc=PcvcRTc=38a = \frac{27 R^2 T_c^2}{64 P_c}, \qquad b = \frac{R T_c}{8 P_c}, \qquad Z_c = \frac{P_c v_c}{R T_c} = \frac{3}{8}

여기서 Zc=3/8=0.375Z_c = 3/8 = 0.375는 기체 종류와 무관한 상수다. 실제 기체의 ZcZ_c는 0.27~0.29라 van der Waals는 정량적으로 과대평가하지만, 정성적 그림은 정확히 맞는다.

환산 변수로 다시 쓰면 상수 aa, bb가 사라진다.

PR=8TR3vR13vR2P_R = \frac{8 T_R}{3 v_R - 1} - \frac{3}{v_R^2}

이 한 줄이 대응 상태 원리의 수식 버전이다. 임계점은 (vR,PR,TR)=(1,1,1)(v_R, P_R, T_R) = (1, 1, 1)에 고정된다.

임계점 아래의 S자 곡선과 Maxwell 작도#

TR>1T_R > 1이면 등온선은 단조 감소한다. 압력을 올리면 부피가 줄어드는, 직관에 맞는 곡선이다.

TR<1T_R < 1이면 곡선이 출렁인다. S자 루프가 생긴다. 가운데 구간은 (P/v)T>0(\partial P/\partial v)_T > 0이다. 압력을 올렸는데 부피가 커진다는 뜻이라 역학적으로 불안정하다. 실제 물질은 이 구간을 그냥 건너뛴다.

대신 수평선 하나가 액체와 기체를 잇는다. 이 선의 높이가 포화 압력이다. 위치는 Maxwell 등면적 작도(equal-area rule)로 정한다. 수평선이 루프에서 잘라내는 위·아래 두 면적이 같아지는 높이를 고른다. 이 조건은 액체와 기체의 Gibbs 자유에너지가 같다는 것과 동치다. 두 상이 공존할 수 있는 유일한 압력이다.

아래 시뮬레이션에서 직접 조작해보자. 환산 온도 슬라이더를 1 아래로 내리면 루프가 자라고, Maxwell 작도가 같은 넓이의 두 영역(분홍·청록)을 칠하며 포화 압력을 그린다.

saturation pressure P_r,sat = 0.647
Z(sat. vapor) = 0.633  ·   Z(sat. liquid) = 0.163

TRT_R을 0.85까지 내리면 포화 기체와 포화 액체의 ZZ 간격이 크게 벌어진다. 액체 쪽 ZZ는 0.1 아래로 떨어진다. 분자가 거의 맞닿아 인력이 압도하기 때문이다. TRT_R을 1에 가깝게 올리면 두 ZZ가 한 점으로 모인다. 임계점에서 액체와 기체의 구분이 사라지는 순간이다.

Python으로 Z를 푼다#

van der Waals 식을 ZZ에 대해 정리하면 3차 방정식이 된다. 실제 기체의 임계 상수만 있으면 임의의 PP, TT에서 ZZ를 푼다.

import numpy as np
 
R = 8.314  # J/(mol·K)
 
def vdw_constants(Tc, Pc):
    """임계 상수로부터 van der Waals a, b."""
    a = 27.0 * R**2 * Tc**2 / (64.0 * Pc)
    b = R * Tc / (8.0 * Pc)
    return a, b
 
def vdw_compressibility(P, T, Tc, Pc):
    """Z^3 - (1+B)Z^2 + A Z - A B = 0 의 물리적 근."""
    a, b = vdw_constants(Tc, Pc)
    A = a * P / (R * T)**2          # 인력 항의 무차원 크기
    B = b * P / (R * T)            # 유한 부피 항의 무차원 크기
    coeffs = [1.0, -(1.0 + B), A, -A * B]
    roots = np.roots(coeffs)
    real = roots[np.abs(roots.imag) < 1e-8].real
    real = real[real > B]          # v > b  ⇒  Z > B 만 물리적
    return real.max(), real.min(), real.size
 
# 질소: Tc = 126.2 K, Pc = 3.39 MPa, M = 28 g/mol
Tc, Pc, M = 126.2, 3.39e6, 0.028
P, T = 1.0e7, 200.0                # 100 bar, 200 K
Z_gas, Z_liq, n = vdw_compressibility(P, T, Tc, Pc)
 
rho_ideal = P * M / (R * T)        # 이상기체 밀도
rho_real = rho_ideal / Z_gas       # 실제 밀도
print(f"근 개수 = {n},  Z = {Z_gas:.4f}")
print(f"이상기체 밀도 = {rho_ideal:6.1f} kg/m^3")
print(f"실제   밀도 = {rho_real:6.1f} kg/m^3  ({100*(rho_real/rho_ideal-1):+.0f}%)")

실행 결과는 다음과 같다.

근 개수 = 1,  Z = 0.7905
이상기체 밀도 =  168.4 kg/m^3
실제   밀도 =  213.0 kg/m^3  (+27%)

TR=200/126.21.59T_R = 200/126.2 \approx 1.59라 초임계 영역이다. 실근이 하나뿐이라 액체·기체 구분이 없다. 그래도 이상기체로 잡은 밀도는 실제보다 27% 작다. 첫 문장의 "4분의 1이 넘는" 오차가 여기서 나온다.

초임계 인젝션과 CFD — 왜 상태방정식이 격자를 흔드는가#

이 이야기는 칠판 위에만 있지 않다. 액체 로켓 엔진의 연소실은 보통 추진제의 임계 압력보다 높은 압력에서 작동한다. LOX(액체 산소)나 극저온 질소 제트가 이런 챔버로 분사되면 익숙한 물방울이 생기지 않는다.

표면장력이 사라지기 때문이다. 임계점을 넘으면 액체와 기체의 경계가 무너진다. 제트는 방울로 쪼개지지 않고, 밀도 구배가 빗살처럼 풀리며 주변과 섞인다. 이것이 초임계(또는 천이임계) 인젝션이고, 차세대 엔진 설계의 핵심 유동 현상이다.

여기서 상태방정식이 격자를 흔든다. 밀도가 압력·온도와 비선형으로 묶이기 때문이다. CFD 솔버가 이상기체를 쓰면 제트 밀도를 수십 % 틀리고, 그 결과 운동량 플럭스 ρu2\rho u^2와 혼합 길이가 통째로 어긋난다. 그래서 실제 코드는 cubic EOS(Peng–Robinson, SRK)나 NASG(Noble–Abel Stiffened Gas) 같은 실제 기체 모델을 쓴다. 압축인자 ZZ는 그 모델이 격자 한 칸마다 푸는 양이다.

기억할 점#

  • **압축인자 Z=Pv/RTZ = Pv/RT**는 실제 기체가 이상기체에서 얼마나 벗어났는지를 하나의 숫자로 잡는다. Z<1Z<1은 인력 우세, Z>1Z>1은 유한 부피 우세.
  • van der Waals 식은 인력(a/v2a/v^2)과 분자 부피(bb)라는 두 상수로 임계점·루프·상전이를 정성적으로 모두 재현한다. 환산 변수에서는 Zc=3/8Z_c = 3/8로 보편화된다.
  • 임계점 너머의 초임계 유동은 표면장력 없이 섞이고, 이를 CFD로 풀려면 이상기체가 아닌 실제 상태방정식이 격자 한 칸마다 필요하다.

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