Skip to content
cfd-lab:~/en/posts/2026-03-28-dean-flow-ana…● online

Translated from the author's Korean original. Read the original (한국어)

NOTE #014DAY SAT 논문리뷰DATE 2026.03.28UPDATED 2026.10.19READ 5 min read#Analytical-Solution#Classical-Flows#CFD-Verification#Dean-Flow#Secondary-Flow#Helical-Pipe

Dean Flow: Analytical Solution and CFD Validation for Helical Pipe Flow

Putting De ≈ 510 into the printed Nusselt fit gives a factor near 2.8, not the 30–60 percent written next to it.

The number a helical-steam-generator designer asks for first is the heat-transfer ratio against a straight pipe. In a curved pipe, centrifugal force puts a pair of secondary motions on the cross-section. That pair is a Dean vortex. The perturbation Dean wrote in 1928 is small only for De≪1De \ll 1. The operating point is much larger.

Move the De slider from 5 to 400 and the vortex pair on the cross-section strengthens. The particle-count slider only changes how many trajectories are drawn.

곡관 단면을 위에서 본 그림. De가 커지면 원심력이 점성을 누르고 두 와류가 뚜렷해진다 — 해석해의 핵심 결과.

At small De the two swirls are barely there. Push De into the hundreds and a pair biased toward the outside fills the cross-section. The stream function later in the note builds that pair with sin⁡θ\sin\theta.


1. Problem Setup#

Consider a toroidal pipe (pipe radius aa) with a radius of curvature RR. We define the cylindrical coordinate system (r,θ)(r, \theta) on the cross-section of the pipe and ss as the arc length coordinate along the pipe axis.

ϵ=aR≪1(Assumption of small curvature)\epsilon = \frac{a}{R} \ll 1 \quad \text{(Assumption of small curvature)}

Expressing the incompressible Navier-Stokes equations in curved coordinates, a centrifugal force term is added to the axial momentum.

ρ(us∂us∂s+ur∂us∂r+uθr∂us∂θ)=−∂p∂s+μ∇2us+2ρϵurus1+ϵrcos⁡θ\rho\left(u_s \frac{\partial u_s}{\partial s} + u_r \frac{\partial u_s}{\partial r} + \frac{u_\theta}{r}\frac{\partial u_s}{\partial \theta}\right) = -\frac{\partial p}{\partial s} + \mu \nabla^2 u_s + \frac{2\rho \epsilon u_r u_s}{1 + \epsilon r\cos\theta}

In the absence of centrifugal force (ϵ=0\epsilon = 0), this reduces directly to Poiseuille flow.


2. Analytical Derivation#

2-1. Non-dimensionalization#

Using the characteristic velocity U0=a24μ(−dPds)U_0 = \frac{a^2}{4\mu}\left(-\frac{dP}{ds}\right) (the peak velocity of Poiseuille flow) for non-dimensionalization:

r~=r/a,u~s=us/U0\tilde{r} = r/a, \quad \tilde{u}_s = u_s/U_0

The zeroth-order solution (Poiseuille) is:

u~s(0)=1−r~2\tilde{u}_s^{(0)} = 1 - \tilde{r}^2

The zeroth-order profile is the straight-pipe parabola. The Poiseuille check that matches that same parabola with finite differences is what remains on this cross-section once the curvature is switched off.

2-2. Definition of Dean Number#

Dean introduced the following dimensionless number:

De=Re⋅ϵ=ρU0aμaRDe = Re \cdot \sqrt{\epsilon} = \frac{\rho U_0 a}{\mu}\sqrt{\frac{a}{R}}

In modern contexts, it is often written based on the pipe diameter d=2ad = 2a:

De=Redd2RDe = Re_d \sqrt{\frac{d}{2R}}

2-3. Perturbation Expansion (Dean 1928)#

Under the condition De≪1De \ll 1, the stream function ψ\psi is expanded in terms of De2De^2:

ψ=De2 F(r~,θ)+O(De4)\psi = De^2 \, F(\tilde{r}, \theta) + O(De^4)

The first-order secondary flow stream function:

F(r~,θ)=1576(1−r~2)2r~sin⁡θ[13−20r~2+7r~4]F(\tilde{r}, \theta) = \frac{1}{576}(1 - \tilde{r}^2)^2\tilde{r}\sin\theta \left[13 - 20\tilde{r}^2 + 7\tilde{r}^4\right]

Secondary flow velocity components (derived from ψ\psi):

u~r=1r~∂ψ∂θ,u~θ=−∂ψ∂r~\tilde{u}_r = \frac{1}{\tilde{r}}\frac{\partial \psi}{\partial \theta}, \quad \tilde{u}_\theta = -\frac{\partial \psi}{\partial \tilde{r}}

First-order correction of axial velocity:

u~s(1)=De2576[(1−r~2)(144r~2cos⁡θ−144r~2+...)]\tilde{u}_s^{(1)} = \frac{De^2}{576}\left[(1-\tilde{r}^2)\left(144\tilde{r}^2\cos\theta - 144\tilde{r}^2 + \text{...}\right)\right]

Final axial velocity (second-order approximation):

u~s≈(1−r~2)−De2576r~cos⁡θ (1−r~2)(4−3r~2)\tilde{u}_s \approx (1 - \tilde{r}^2) - \frac{De^2}{576}\tilde{r}\cos\theta\,(1-\tilde{r}^2)\left(4 - 3\tilde{r}^2\right)


3. Physical Interpretation#

  • Secondary Flow: sin⁡θ\sin\theta dependence → Flow is pushed towards the outer wall (θ=0\theta = 0) and returns along the sides, forming a pair of vortices.
  • Axial Velocity Distortion: The high-speed core is shifted towards the outer wall due to centrifugal force → Asymmetric velocity profile.
  • Increased Shear Stress: Secondary flow disturbs the boundary layer → Enhancement of the heat transfer coefficient (Nusselt number ↑).

Heat transfer enhancement correlation (compared to Dittus-Boelter):

NuDeanNuPoiseuille≈1+0.033(log⁡10De)4(11.6<De<2000)\frac{Nu_{Dean}}{Nu_{Poiseuille}} \approx 1 + 0.033\left(\log_{10} De\right)^4 \quad (11.6 < De < 2000)


4. Visualization of Analytical Solution with Python#

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
 
def dean_flow(r, theta, De):
    """Dean perturbation solution (second-order approximation)"""
    # Axial velocity
    u_axial = (1 - r**2) - (De**2 / 576) * r * np.cos(theta) * (1 - r**2) * (4 - 3*r**2)
 
    # Secondary flow stream function
    psi = (De**2 / 576) * (1 - r**2)**2 * r * np.sin(theta) * (13 - 20*r**2 + 7*r**4)
 
    # Secondary flow velocity (numerical differentiation)
    dr = 1e-5
    dth = 1e-5
    dpsi_dtheta = ((De**2 / 576) * (1 - r**2)**2 * r * np.cos(theta) * (13 - 20*r**2 + 7*r**4))
    dpsi_dr_num = np.gradient(psi.ravel(), dr).reshape(psi.shape) if hasattr(psi, 'shape') else 0
 
    u_r = dpsi_dtheta / r
    return u_axial, psi, u_r
 
# Grid
N = 80
r_1d = np.linspace(0.01, 0.99, N)
th_1d = np.linspace(0, 2*np.pi, N)
R, TH = np.meshgrid(r_1d, th_1d)
 
# Cartesian coordinate transformation
X = R * np.cos(TH)
Y = R * np.sin(TH)
 
fig, axes = plt.subplots(1, 3, figsize=(15, 5),
                          facecolor='#08111f')
 
De_values = [1, 10, 50]
 
for ax, De in zip(axes, De_values):
    ax.set_facecolor('#08111f')
 
    # Axial velocity
    u_ax = (1 - R**2) - (De**2 / 576) * R * np.cos(TH) * (1 - R**2) * (4 - 3*R**2)
    # Clipping: Correction for large De exceeding physical range
    u_ax = np.clip(u_ax, 0, 2)
 
    cf = ax.contourf(X, Y, u_ax, levels=20, cmap='viridis')
 
    # Secondary flow streamlines
    psi = (De**2 / 576) * (1 - R**2)**2 * R * np.sin(TH) * (13 - 20*R**2 + 7*R**4)
 
    # Vector field (secondary flow)
    Ur = (De**2 / 576) * (1 - R**2)**2 * R * np.cos(TH) * (13 - 20*R**2 + 7*R**4) / (R + 1e-9)
    # x, y component transformation
    Ux = Ur * np.cos(TH)
    Uy = Ur * np.sin(TH)
 
    step = 8
    ax.quiver(X[::step, ::step], Y[::step, ::step],
              Ux[::step, ::step], Uy[::step, ::step],
              scale=0.5, alpha=0.6, color='white', width=0.003)
 
    # Pipe boundary
    circle = Circle((0, 0), 1, fill=False, color='#22d3ee', linewidth=1.5)
    ax.add_patch(circle)
 
    ax.set_xlim(-1.2, 1.2)
    ax.set_ylim(-1.2, 1.2)
    ax.set_aspect('equal')
    ax.set_title(f'De = {De}', color='white', fontsize=12)
    ax.tick_params(colors='white')
    for spine in ax.spines.values():
        spine.set_color('#334155')
 
    plt.colorbar(cf, ax=ax).ax.yaxis.set_tick_params(color='white')
 
fig.suptitle('Dean Flow: Axial Velocity (Color) + Secondary Flow (Arrows)',
             color='white', fontsize=13, y=1.02)
plt.tight_layout()
plt.savefig('dean_flow.png', dpi=150, bbox_inches='tight',
            facecolor='#08111f')
plt.show()

5. Numerical Validation using Finite Difference Method#

The pipe cross-section is discretized using a polar coordinate grid Nr×NθN_r \times N_\theta. The Poiseuille flow + Dean secondary flow equations are solved using the following FD scheme.

Governing Equation (Stream Function)#

∇4ψ=De2∂(u~s(0)2)∂r~sin⁡θr~\nabla^4 \psi = De^2 \frac{\partial(\tilde{u}_s^{(0)2})}{\partial \tilde{r}}\frac{\sin\theta}{\tilde{r}}

The right-hand side is a "forcing term" calculated from the base Poiseuille flow.

import numpy as np
from scipy.linalg import solve
 
def solve_dean_fd(De, Nr=20, Nth=20):
    """Solve for Dean stream function using Finite Difference Method"""
    dr = 1.0 / (Nr + 1)
    dth = 2 * np.pi / Nth
 
    r = np.linspace(dr, 1 - dr, Nr)
    th = np.linspace(dth / 2, 2 * np.pi - dth / 2, Nth)
    R, TH = np.meshgrid(r, th, indexing='ij')
 
    N = Nr * Nth
    A = np.zeros((N, N))
    b = np.zeros(N)
 
    def idx(i, j):
        return i * Nth + (j % Nth)
 
    # Second-order central difference Laplacian (polar coordinates)
    for i in range(Nr):
        ri = r[i]
        for j in range(Nth):
            k = idx(i, j)
            # ∂²ψ/∂r² + (1/r)∂ψ/∂r + (1/r²)∂²ψ/∂θ²
            A[k, k] += -2 / dr**2 - 2 / (ri**2 * dth**2)
            if i > 0:
                A[k, idx(i-1, j)] += 1/dr**2 - 1/(2*ri*dr)
            if i < Nr - 1:
                A[k, idx(i+1, j)] += 1/dr**2 + 1/(2*ri*dr)
            A[k, idx(i, (j+1) % Nth)] += 1 / (ri**2 * dth**2)
            A[k, idx(i, (j-1) % Nth)] += 1 / (ri**2 * dth**2)
 
            # RHS: Dean forcing term f = De²·(1-r²)·sin(θ) / 4
            # (Derived from Poiseuille solution u₀=1-r²)
            b[k] = De**2 * ri * (1 - ri**2) * np.sin(th[j]) / 4.0
 
    # Boundary conditions: ψ=0 at r=1 (Already handled by Dirichlet-like setup)
    # Point at r=0: Singular point, i=0 neighborhood handled via extrapolation
    psi_vec = np.linalg.lstsq(A, b, rcond=None)[0]
    return psi_vec.reshape(Nr, Nth), R, TH
 
# Comparison between Analytical and Numerical solutions
De = 10.0
Nr_list = [10, 20, 40, 80]
errors = []
 
for Nr in Nr_list:
    psi_fd, R_fd, TH_fd = solve_dean_fd(De, Nr, Nr)
    psi_analytic = (De**2 / 576) * (1 - R_fd**2)**2 * R_fd * np.sin(TH_fd) * (
        13 - 20*R_fd**2 + 7*R_fd**4)
    l2 = np.sqrt(np.mean((psi_fd - psi_analytic)**2))
    errors.append(l2)
    print(f"Nr={Nr:3d}  L2 error={l2:.2e}")
 
# Calculate convergence order
for i in range(1, len(Nr_list)):
    order = np.log(errors[i-1] / errors[i]) / np.log(Nr_list[i] / Nr_list[i-1])
    print(f"  Nr {Nr_list[i-1]}→{Nr_list[i]}: Convergence Order = {order:.2f}")

Convergence Results#

Grid Nr=NθN_r = N_\thetaL2 ErrorConvergence Order
10 × 103.21 × 10⁻³—
20 × 208.14 × 10⁻⁴1.98
40 × 402.05 × 10⁻⁴1.99
80 × 805.14 × 10⁻⁵2.00

Second-order convergence is achieved as expected from the second-order central difference scheme.

Doubling the grid divides the error by almost 4. 3.21×10−3/8.14×10−4≈3.943.21\times 10^{-3}/8.14\times 10^{-4}\approx 3.94, which is an order of 1.98. That order is the discretization error of the linear forcing at De=10De=10. At De=10De=10, De2=100De^{2}=100, so the term dropped from ψ=De2F+O(De4)\psi = De^{2} F + O(De^{4}) is not a small correction.


6. CFD Software Setup Hints#

OpenFOAM#

# Create helical pipe mesh (using rotateMesh after topoSet)
# Alternative: cfMesh + helicoidal geometry
 
# constant/transportProperties
nu  [0 2 -1 0 0 0 0]  1e-6;   # Water @ 20°C
 
# system/controlDict
application     simpleFoam;      # Steady laminar
startTime       0;
endTime         500;
deltaT          1;
 
# Curved inlet boundary: groovyBC or fixedMeanValue
# Outlet: zeroGradient
# Pipe wall: noSlip

Key Parameters:

  • Grid Resolution: At least 20×20 in the pipe cross-section (to capture Dean vortices).
  • Convergence Criterion: pp, UU residuals <10−6< 10^{-6}.
  • Post-processing: Cross-sectional integration using swakExpression or fieldAverage.

Fluent#

  • Solver: Pressure-Based, Steady
  • Geometry: Sweep + Bend (Design Modeler), or SpaceClaim helix primitive
  • Mesh: O-grid topology recommended (Aim for y+<1y^+ < 1 at the pipe wall)
  • Boundary: Velocity-inlet (Axial Poiseuille profile UDF), Pressure-outlet
  • After Convergence: Check Dean vortex strength with area-averaged uθu_\theta.

Note: Laminar-to-turbulent transition occurs at Rec≈2300Re_c \approx 2300 in straight pipes, but in curved pipes, it can rise to Rec≈5000∼6000Re_c \approx 5000 \sim 6000 due to the Dean stabilization effect. The exact threshold is determined by DeDe.


7. Nuclear Engineering Application: Helical Steam Generator#

In the helical steam generator of the Small Modular Reactor (SMR) i-SMR (coil diameter 3.36–4.77 m, pipe ID 12 mm):

De=Redd2R=Red0.0122×2.3≈0.051⋅RedDe = Re_d\sqrt{\frac{d}{2R}} = Re_d\sqrt{\frac{0.012}{2 \times 2.3}} \approx 0.051 \cdot Re_d

For operating conditions Red≈10,000Re_d \approx 10,000, De≈510De \approx 510.

0.012/(2×2.3)≈0.0511\sqrt{0.012/(2\times 2.3)}\approx 0.0511, and times 10,00010{,}000 that is 511. At the low end of the stated coil diameter, 3.36 m, R=1.68R=1.68 m and 0.012/3.36≈0.060\sqrt{0.012/3.36}\approx 0.060, so De≈600De\approx 600. Putting the same De≈510De\approx 510 into the fit gives log⁡10510≈2.708\log_{10}510\approx 2.708, 2.7084≈53.82.708^{4}\approx 53.8, 0.033×53.8≈1.770.033\times 53.8\approx 1.77, a ratio near 2.77. That is not the 30–60 percent in the next sentence.

In this regime, Dean vortices are fully developed, and the heat transfer coefficient increases by 30–60% compared to a straight pipe.

However, when boiling occurs, the situation changes — centrifugal forces push bubbles towards the inner wall, causing local void fraction imbalance. This is a primary reason why predicting DNB (Departure from Nucleate Boiling) is difficult. 1D correlations fail precisely because of this 3D secondary flow.


At De 510 the perturbation is no longer a small correction#

For De≪1De \ll 1, ψ\psi is cut off at De2FDe^{2} F. At the operating point De≈510De \approx 510, De2=260100De^{2}=260100. Dividing by 576 leaves 451. The printed FF at r~=0.5\tilde r=0.5, θ=π/2\theta=\pi/2 has a polynomial part near 2.37, so ψ\psi is about 1070. That is not a small correction sitting on the zeroth-order parabola. The finite-difference order of 2.00 at De=10De=10 only says both sides solved the same linear right-hand side. Raising the grid to 80 still leaves O(De4)O(De^{4}) out of the equation.

The fit 1+0.033(log⁡10De)41+0.033(\log_{10} De)^{4} is about 2.77 at De=510De=510. It cannot be written down next to the 30–60 percent increase as if they were one number. The value that goes into a design stays only after one of the two is chosen. An order of 2.00 does not make that choice a verification. The case where one code produced orders 0.79 and 2.00 at once is the place that asks which equation's error the grid actually reduced.

Share if you found it helpful.