Remember when we computed uniform flow past a doublet? The stream-line pattern produced flow around a cylinder. When studying the pressure coefficient, we realized that the drag on the cylinder was exactly zero, leading to the D'Alembert paradox.
What about lift? Is it possible for a perfectly circular cylinder to experience lift? What if the cylinder is rotating? Have you heard about the Magnus effect?
You might be surprised to learn that all we need to do is add a vortex in the center of the cylinder. Let's see how that looks.
First, we recall the equations for the flow of a doublet. In Cartesian coordinates, a doublet located at the origin has a stream function and velocity components given by
$$\psi\left(x,y\right) = -\frac{\kappa}{2\pi}\frac{y}{x^2+y^2}$$$$u\left(x,y\right) = \frac{\partial\psi}{\partial y} = -\frac{\kappa}{2\pi}\frac{x^2-y^2}{\left(x^2+y^2\right)^2}$$$$v\left(x,y\right) = -\frac{\partial\psi}{\partial x} = -\frac{\kappa}{2\pi}\frac{2xy}{\left(x^2+y^2\right)^2}$$We'll place a doublet of strength $\kappa=1$ at the origin, and add a free stream $U_\infty=1$ (yes, we really like the number one). We can re-use the code we have written before; this is always a good thing.
import math
import numpy
from matplotlib import pyplot
# embed the figures into the notebook
%matplotlib inline
N = 50 # Number of points in each direction
x_start, x_end = -2.0, 2.0 # x-direction boundaries
y_start, y_end = -1.0, 1.0 # y-direction boundaries
x = numpy.linspace(x_start, x_end, N) # computes a 1D-array for x
y = numpy.linspace(y_start, y_end, N) # computes a 1D-array for y
X, Y = numpy.meshgrid(x, y) # generates a mesh grid
kappa = 1.0 # strength of the doublet
x_doublet, y_doublet = 0.0, 0.0 # location of the doublet
u_inf = 1.0 # freestream speed
Here are our function definitions for the doublet:
def get_velocity_doublet(strength, xd, yd, X, Y):
"""
Returns the velocity field generated by a doublet.
Parameters
----------
strength: float
Strength of the doublet.
xd: float
x-coordinate of the doublet.
yd: float
y-coordinate of the doublet.
X: 2D Numpy array of floats
x-coordinate of the mesh points.
Y: 2D Numpy array of floats
y-coordinate of the mesh points.
Returns
-------
u: 2D Numpy array of floats
x-component of the velocity vector field.
v: 2D Numpy array of floats
y-component of the velocity vector field.
"""
u = (-strength / (2 * math.pi) *
((X - xd)**2 - (Y - yd)**2) /
((X - xd)**2 + (Y - yd)**2)**2)
v = (-strength / (2 * math.pi) *
2 * (X - xd) * (Y - yd) /
((X - xd)**2 + (Y - yd)**2)**2)
return u, v
def get_stream_function_doublet(strength, xd, yd, X, Y):
"""
Returns the stream-function generated by a doublet.
Parameters
----------
strength: float
Strength of the doublet.
xd: float
x-coordinate of the doublet.
yd: float
y-coordinate of the doublet.
X: 2D Numpy array of floats
x-coordinate of the mesh points.
Y: 2D Numpy array of floats
y-coordinate of the mesh points.
Returns
-------
psi: 2D Numpy array of floats
The stream-function.
"""
psi = -strength / (2 * math.pi) * (Y - yd) / ((X - xd)**2 + (Y - yd)**2)
return psi
And now we compute everything to get the flow around a cylinder, by adding a free stream to the doublet:
# compute the velocity field on the mesh grid
u_doublet, v_doublet = get_velocity_doublet(kappa, x_doublet, y_doublet, X, Y)
# compute the stream-function on the mesh grid
psi_doublet = get_stream_function_doublet(kappa, x_doublet, y_doublet, X, Y)
# freestream velocity components
u_freestream = u_inf * numpy.ones((N, N), dtype=float)
v_freestream = numpy.zeros((N, N), dtype=float)
# stream-function of the freestream flow
psi_freestream = u_inf * Y
# superposition of the doublet on the freestream flow
u = u_freestream + u_doublet
v = v_freestream + v_doublet
psi = psi_freestream + psi_doublet
We are ready to do a nice visualization.
# plot the streamlines
width = 10
height = (y_end - y_start) / (x_end - x_start) * width
pyplot.figure(figsize=(width, height))
pyplot.xlabel('x', fontsize=16)
pyplot.ylabel('y', fontsize=16)
pyplot.xlim(x_start, x_end)
pyplot.ylim(y_start, y_end)
pyplot.streamplot(X, Y, u, v, density=2, linewidth=1, arrowsize=1, arrowstyle='->')
pyplot.scatter(x_doublet, y_doublet, color='#CD2305', s=80, marker='o')
# calculate the cylinder radius and add the cylinder to the figure
R = math.sqrt(kappa / (2 * math.pi * u_inf))
circle = pyplot.Circle((0, 0), radius=R, color='#CD2305', alpha=0.5)
pyplot.gca().add_patch(circle)
# calculate the stagnation points and add them to the figure
x_stagn1, y_stagn1 = +math.sqrt(kappa / (2 * math.pi * u_inf)), 0.0
x_stagn2, y_stagn2 = -math.sqrt(kappa / (2 * math.pi * u_inf)), 0.0
pyplot.scatter([x_stagn1, x_stagn2], [y_stagn1, y_stagn2],
color='g', s=80, marker='o');
Nice! We have cylinder flow.
Now, let's add a vortex located at the origin with a positive strength $\Gamma$. In Cartesian coordinates, the stream function and velocity components are given by:
$$\psi\left(x,y\right) = \frac{\Gamma}{4\pi}\ln\left(x^2+y^2\right)$$$$u\left(x,y\right) = \frac{\Gamma}{2\pi}\frac{y}{x^2+y^2} \qquad v\left(x,y\right) = -\frac{\Gamma}{2\pi}\frac{x}{x^2+y^2}$$Based on these equations, we define the functions get_velocity_vortex()
and get_stream_function_vortex()
to do ... well, what's obvious by the function names (you should always try to come up with obvious function names). Play around with the value of $\ \Gamma$ and recalculate the flow. See what happens.
gamma = 4.0 # strength of the vortex
x_vortex, y_vortex = 0.0, 0.0 # location of the vortex
def get_velocity_vortex(strength, xv, yv, X, Y):
"""
Returns the velocity field generated by a vortex.
Parameters
----------
strength: float
Strength of the vortex.
xv: float
x-coordinate of the vortex.
yv: float
y-coordinate of the vortex.
X: 2D Numpy array of floats
x-coordinate of the mesh points.
Y: 2D Numpy array of floats
y-coordinate of the mesh points.
Returns
-------
u: 2D Numpy array of floats
x-component of the velocity vector field.
v: 2D Numpy array of floats
y-component of the velocity vector field.
"""
u = +strength / (2 * math.pi) * (Y - yv) / ((X - xv)**2 + (Y - yv)**2)
v = -strength / (2 * math.pi) * (X - xv) / ((X - xv)**2 + (Y - yv)**2)
return u, v
def get_stream_function_vortex(strength, xv, yv, X, Y):
"""
Returns the stream-function generated by a vortex.
Parameters
----------
strength: float
Strength of the vortex.
xv: float
x-coordinate of the vortex.
yv: float
y-coordinate of the vortex.
X: 2D Numpy array of floats
x-coordinate of the mesh points.
Y: 2D Numpy array of floats
y-coordinate of the mesh points.
Returns
-------
psi: 2D Numpy array of floats
The stream-function.
"""
psi = strength / (4 * math.pi) * numpy.log((X - xv)**2 + (Y - yv)**2)
return psi
# compute the velocity field on the mesh grid
u_vortex, v_vortex = get_velocity_vortex(gamma, x_vortex, y_vortex, X, Y)
# compute the stream-function on the mesh grid
psi_vortex = get_stream_function_vortex(gamma, x_vortex, y_vortex, X, Y)
Now that we have all the necessary ingredients (uniform flow, doublet and vortex), we apply the principle of superposition, and then we make a nice plot.
# superposition of the doublet and the vortex on the freestream flow
u = u_freestream + u_doublet + u_vortex
v = v_freestream + v_doublet + v_vortex
psi = psi_freestream + psi_doublet + psi_vortex
# calculate the cylinder radius
R = math.sqrt(kappa / (2 * math.pi * u_inf))
# calculate the stagnation points
x_stagn1, y_stagn1 = (+math.sqrt(R**2 - (gamma / (4 * math.pi * u_inf))**2),
-gamma / (4 * math.pi * u_inf))
x_stagn2, y_stagn2 = (-math.sqrt(R**2 - (gamma / (4 * math.pi * u_inf))**2),
-gamma / (4 * math.pi * u_inf))
# plot the streamlines
width = 10
height = (y_end - y_start) / (x_end - x_start) * width
pyplot.figure(figsize=(width, height))
pyplot.xlabel('x', fontsize=16)
pyplot.ylabel('y', fontsize=16)
pyplot.xlim(x_start, x_end)
pyplot.ylim(y_start, y_end)
pyplot.streamplot(X, Y, u, v,
density=2, linewidth=1, arrowsize=1.5, arrowstyle='->')
circle = pyplot.Circle((0.0, 0.0), radius=R, color='#CD2305', alpha=0.5)
pyplot.gca().add_patch(circle)
pyplot.scatter(x_vortex, y_vortex, color='#CD2305', s=80, marker='o')
pyplot.scatter([x_stagn1, x_stagn2], [y_stagn1, y_stagn2],
color='g', s=80, marker='o');