.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/aerodynamics/circulation/plot_01_kutta_joukowski_spinning_cylinder.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code or to run this example in your browser via JupyterLite. .. rst-class:: sphx-glr-example-title .. _sphx_glr_api_gallery_aerodynamics_circulation_plot_01_kutta_joukowski_spinning_cylinder.py: The Kutta-Joukowski theorem on a spinning cylinder ================================================== Martin Kutta (1902) and Nikolai Joukowski (1906) showed that the lift per unit span on any two-dimensional body in a steady inviscid stream is :math:`L' = \rho V \Gamma`, where :math:`\Gamma` is the circulation around the body (:func:`~engineeringkit.aerodynamics.kutta_joukowski_lift`). The cleanest check is potential flow past a cylinder with circulation (a model of the Magnus effect): integrating the Bernoulli surface pressure around the cylinder reproduces :math:`\rho V\Gamma` exactly, and the drag integrates to zero (d'Alembert's paradox). .. GENERATED FROM PYTHON SOURCE LINES 16-24 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from scipy.integrate import quad from engineeringkit.aerodynamics import dynamic_pressure, kutta_joukowski_lift rho, V, R = 1.225, 20.0, 0.5 .. GENERATED FROM PYTHON SOURCE LINES 25-31 Integrate the surface pressure ------------------------------ On the surface the tangential speed is :math:`u_\theta = -2V\sin\theta - \Gamma/(2\pi R)`, and Bernoulli gives :math:`p - p_\infty = q\,(1 - u_\theta^2/V^2)`. .. GENERATED FROM PYTHON SOURCE LINES 31-53 .. code-block:: Python def surface_forces(gamma): q = float(dynamic_pressure(rho, V)) def dp(theta): u = -2 * V * np.sin(theta) - gamma / (2 * np.pi * R) return q * (1 - (u / V) ** 2) lift_per_span = -quad(lambda t: dp(t) * np.sin(t) * R, 0, 2 * np.pi)[0] drag_per_span = -quad(lambda t: dp(t) * np.cos(t) * R, 0, 2 * np.pi)[0] return lift_per_span, drag_per_span gammas = np.linspace(0, 4 * np.pi * R * V, 9) integrated = np.array([surface_forces(g) for g in gammas]) theory = kutta_joukowski_lift(rho, V, gammas) for g, (L, D), Lkj in zip(gammas, integrated, theory, strict=True): print(f"Gamma = {g:6.1f} m^2/s: integrated L' = {L:8.2f}, rho V Gamma = {Lkj:8.2f}, D' = {D:+.1e} N/m") assert np.allclose(integrated[:, 0], theory, rtol=1e-9, atol=1e-9) assert np.allclose(integrated[:, 1], 0, atol=1e-8) .. rst-class:: sphx-glr-script-out .. code-block:: none Gamma = 0.0 m^2/s: integrated L' = 0.00, rho V Gamma = 0.00, D' = +1.0e-14 N/m Gamma = 15.7 m^2/s: integrated L' = 384.85, rho V Gamma = 384.85, D' = +3.3e-14 N/m Gamma = 31.4 m^2/s: integrated L' = 769.69, rho V Gamma = 769.69, D' = +3.2e-14 N/m Gamma = 47.1 m^2/s: integrated L' = 1154.54, rho V Gamma = 1154.54, D' = +3.6e-14 N/m Gamma = 62.8 m^2/s: integrated L' = 1539.38, rho V Gamma = 1539.38, D' = -1.3e-14 N/m Gamma = 78.5 m^2/s: integrated L' = 1924.23, rho V Gamma = 1924.23, D' = +2.0e-14 N/m Gamma = 94.2 m^2/s: integrated L' = 2309.07, rho V Gamma = 2309.07, D' = -5.4e-14 N/m Gamma = 110.0 m^2/s: integrated L' = 2693.92, rho V Gamma = 2693.92, D' = -5.6e-14 N/m Gamma = 125.7 m^2/s: integrated L' = 3078.76, rho V Gamma = 3078.76, D' = -6.0e-14 N/m .. GENERATED FROM PYTHON SOURCE LINES 54-59 Streamlines with and without circulation ---------------------------------------- Circulation moves the stagnation points down; once :math:`\Gamma > 4\pi R V` they leave the surface. .. GENERATED FROM PYTHON SOURCE LINES 59-76 .. code-block:: Python x, y = np.meshgrid(np.linspace(-2, 2, 400), np.linspace(-1.5, 1.5, 300)) r, th = np.hypot(x, y), np.arctan2(y, x) fig, axes = plt.subplots(1, 3, figsize=(11, 3.3)) for ax, g in zip(axes, [0.0, 2 * np.pi * R * V, 4 * np.pi * R * V], strict=True): with np.errstate(divide="ignore"): psi = V * r * np.sin(th) * (1 - R**2 / r**2) + g / (2 * np.pi) * np.log(r / R) psi = np.where(r < R, np.nan, psi) ax.contour(x, y, psi, levels=np.linspace(-30, 30, 31), colors="C0", linewidths=0.7) ax.add_patch(plt.Circle((0, 0), R, color="0.6")) ax.set_aspect("equal") ax.set_title(f"Gamma / (pi R V) = {g / (np.pi * R * V):.0f}") ax.set_xticks([]) ax.set_yticks([]) fig.tight_layout() plt.show() .. image-sg:: /api/gallery/aerodynamics/circulation/images/sphx_glr_plot_01_kutta_joukowski_spinning_cylinder_001.png :alt: Gamma / (pi R V) = 0, Gamma / (pi R V) = 2, Gamma / (pi R V) = 4 :srcset: /api/gallery/aerodynamics/circulation/images/sphx_glr_plot_01_kutta_joukowski_spinning_cylinder_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.215 seconds) .. _sphx_glr_download_api_gallery_aerodynamics_circulation_plot_01_kutta_joukowski_spinning_cylinder.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: lite-badge .. image:: images/jupyterlite_badge_logo.svg :target: ../../../../lite/lab/index.html?path=api/gallery/aerodynamics/circulation/plot_01_kutta_joukowski_spinning_cylinder.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_01_kutta_joukowski_spinning_cylinder.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_01_kutta_joukowski_spinning_cylinder.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_01_kutta_joukowski_spinning_cylinder.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_