Note
Go to the end to download the full example code.
D’Alembert’s paradox: zero net drag on a cylinder in inviscid flow#
Jean le Rond d’Alembert applied the equations of inviscid, irrotational flow
to steady motion past a closed body and found – to his own evident
discomfort – that the theory predicts exactly zero net drag on the body no
matter its shape, a flatly unphysical result for anything actually moving
through a real fluid.
flow_past_cylinder() with
its default circulation=0.0 builds exactly this symmetric, inviscid flow:
a uniform stream past a circular cylinder with no circulation added, and
therefore no front-back (or top-bottom) asymmetry anywhere in the pressure
field. Integrating the resulting surface pressure coefficient
(pressure_coefficient()) all
the way around the cylinder gives exactly zero net force along the
free-stream direction, however finely the integral is resolved – the
viscosity that would break that symmetry, and finally produce drag, is
simply absent from the calculation. See the 1902-1906 Kutta-Joukowski entry
for what happens once circulation is added back in, breaking the same
front-back symmetry to produce lift instead.
import matplotlib.pyplot as plt
import numpy as np
from physicskit.fluids.systems.potential_flow import flow_past_cylinder
from physicskit.fluids.visualizers.flow_fields import plot_streamlines
from physicskit.fluids.visualizers.potential_flow import plot_pressure_coefficient
Build the symmetric, circulation-free flow#
With no circulation, the flow pattern is exactly front-back and top-bottom symmetric about the cylinder – no mechanism exists here for the pressure to favor pushing the cylinder in any direction at all.
U_inf, R, rho = 1.0, 1.0, 1.0
flow = flow_past_cylinder(U_inf=U_inf, radius=R, circulation=0.0)
x = np.linspace(-3, 3, 300)
X, Y = np.meshgrid(x, x, indexing="ij")
u, v = flow.velocity(X, Y)
inside = X**2 + Y**2 < R**2
u[inside] = np.nan
v[inside] = np.nan
fig, ax = plot_streamlines(X, Y, u, v)
theta_circle = np.linspace(0, 2 * np.pi, 200)
ax.plot(R * np.cos(theta_circle), R * np.sin(theta_circle), color="black", lw=1.5)
ax.set_title("Cylinder with no circulation: front-back symmetric flow")
fig.tight_layout()

Surface pressure coefficient is symmetric fore and aft#
Two stagnation points (\(C_p=1\)) sit exactly at the front and back of the cylinder, and the suction peaks on top and bottom are identical in magnitude – there is no pressure asymmetry anywhere for a net force to come from.

Integrating the surface pressure gives exactly zero net drag#
However finely the surface integral is resolved, the drag component (along the free-stream direction) comes out zero to numerical precision – the paradox itself, stated as a direct calculation rather than a symmetry argument.
theta_fine = np.linspace(0, 2 * np.pi, 4000, endpoint=False)
x_fine, y_fine = R * np.cos(theta_fine), R * np.sin(theta_fine)
Cp_fine = flow.pressure_coefficient(x_fine, y_fine)
p_fine = Cp_fine * (0.5 * rho * U_inf**2)
dtheta = theta_fine[1] - theta_fine[0]
# Drag is the pressure force resolved along the free-stream (x) direction;
# the outward normal on a circle of radius R has x-component cos(theta).
drag_numeric = np.sum(p_fine * np.cos(theta_fine) * R) * dtheta
lift_numeric = np.sum(p_fine * np.sin(theta_fine) * R) * dtheta
print(f"net drag from the pressure integral: {drag_numeric:.2e} (theory: exactly 0)")
print(f"net lift from the pressure integral: {lift_numeric:.2e} (theory: exactly 0, no circulation)")
plt.show()
net drag from the pressure integral: 2.51e-12 (theory: exactly 0)
net lift from the pressure integral: 6.26e-12 (theory: exactly 0, no circulation)
Total running time of the script: (0 minutes 0.173 seconds)