{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Vorticity-Streamfunction FD Python\n", "## CH EN 6355 - Computational Fluid Dynamics\n", "**Prof. Tony Saad (www.tsaad.net)
Department of Chemical Engineering
University of Utah**\n", "
" ] }, { "cell_type": "markdown", "metadata": { "variables": { "2\\Delta x": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "2\\Delta y": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\Delta t": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\Delta {x^2": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\Delta {y^2": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\omega _{i + 1,j}^n - 2\\omega _{i,j}^n + \\omega _{i - 1,j}^n": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\omega _{i + 1,j}^n - \\omega _{i - 1,j}^n": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\omega _{i,j + 1}^n - 2\\omega _{i,j}^n + \\omega _{i,j - 1}^n": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\omega _{i,j + 1}^n - \\omega _{i,j - 1}^n": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\omega _{i,j}^{n + 1} - \\omega _{i,j}^n": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\partial \\psi ": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\partial t": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\partial x": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\partial y": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\partial {\\omega _z": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\psi _{i + 1,j}^n - \\psi _{i - 1,j}^n": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n", "\\psi _{i,j + 1}^n - \\psi _{i,j - 1}^n": "

SyntaxError: unexpected character after line continuation character (, line 1)

\n" } }, "source": [ "## Synopsis\n", "This notebook implements a finite difference solution of the 2D vorticity streamfunction approach for the constant density incompressible Navier-Stokes equations. These are given by\n", "\\begin{equation}\n", "\\frac{{\\partial {\\omega _z}}}{{\\partial t}} = - \\frac{{\\partial \\psi }}{{\\partial y}}\\frac{{\\partial {\\omega _z}}}{{\\partial x}} + \\frac{{\\partial \\psi }}{{\\partial x}}\\frac{{\\partial {\\omega _z}}}{{\\partial y}} + \\nu {\\nabla ^2}{\\omega _z}\n", "\\end{equation}\n", "\\begin{equation}\n", "\\nabla ^2 \\psi = -\\omega_z\n", "\\end{equation}\n", "along with $u = \\frac{\\partial \\psi}{\\partial y}$ and $v =- \\frac{\\partial \\psi}{\\partial x}$. \n", "\n", "The solution procedure consists of the following steps:\n", "1. Initialize $\\omega$ in the domain and set $\\omega$ and $\\psi$ appropriately at the boundaries\n", "2. Compute the streamfunction by solving the Poisson equation for the streamfunction-vorticity relation\n", "3. Solve the vorticity transport equation using an appropriate discretization scheme in time and space\n", "\n", "For this notebook, we will use an FTCS scheme (Forward in Time, Central in Space). We have\n", "\\begin{equation}\n", "\\frac{{\\partial {\\omega _z}}}{{\\partial t}} \\approx \\frac{{\\omega _{i,j}^{n + 1} - \\omega _{i,j}^n}}{{\\Delta t}}\n", "\\end{equation}\n", "\\begin{equation}\n", "- \\frac{{\\partial \\psi }}{{\\partial y}}\\frac{{\\partial {\\omega _z}}}{{\\partial x}} = - \\left( {\\frac{{\\psi _{i,j + 1}^n - \\psi _{i,j - 1}^n}}{{2\\Delta y}}} \\right)\\left( {\\frac{{\\omega _{i + 1,j}^n - \\omega _{i - 1,j}^n}}{{2\\Delta x}}} \\right)\n", "\\end{equation}\n", "\\begin{equation}\n", "\\frac{{\\partial \\psi }}{{\\partial x}}\\frac{{\\partial {\\omega _z}}}{{\\partial y}} = \\left( {\\frac{{\\psi _{i + 1,j}^n - \\psi _{i - 1,j}^n}}{{2\\Delta x}}} \\right)\\left( {\\frac{{\\omega _{i,j + 1}^n - \\omega _{i,j - 1}^n}}{{2\\Delta y}}} \\right)\n", "\\end{equation}\n", "and\n", "\\begin{equation}\n", "{\\nabla ^2}{\\omega _z} = \\frac{{\\omega _{i + 1,j}^n - 2\\omega _{i,j}^n + \\omega _{i - 1,j}^n}}{{\\Delta {x^2}}} + \\frac{{\\omega _{i,j + 1}^n - 2\\omega _{i,j}^n + \\omega _{i,j - 1}^n}}{{\\Delta {y^2}}}\n", "\\end{equation}" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "%matplotlib inline\n", "%config InlineBackend.figure_format = 'svg'\n", "import matplotlib.pyplot as plt\n", "import matplotlib.animation as animation\n", "from matplotlib import cm\n", "plt.rcParams['animation.html'] = 'html5'" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "nx = 21\n", "ny = 21\n", "lx = 1.0\n", "ly = 1.0\n", "dx = lx/(nx-1)\n", "dy = ly/(ny-1)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "Ut = 5.0 # u top wall\n", "Ub = 0.0 # u bottom wall\n", "Vl = 0.0 # V left wall\n", "Vr = 0.0 # V right wall" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dt = 0.008\n", "Reynods = 100.0\n" ] } ], "source": [ "ψ0 = np.zeros([ny,nx])\n", "ω0 = np.zeros([ny,nx])\n", "\n", "# apply boundary conditions\n", "ω0[0,1:-1] = -2.0*ψ0[1, 1:-1]/dy/dy + 2.0*Ub/dy # vorticity on bottom wall (stationary)\n", "ω0[-1,1:-1] = -2.0*ψ0[-2, 1:-1]/dy/dy - 2.0*Ut/dy # vorticity on top wall (moving at Uwall)\n", "ω0[1:-1,-1] = -2.0*ψ0[1:-1,-2]/dx/dx # right wall\n", "ω0[1:-1,0] = -2.0*ψ0[1:-1,1]/dx/dx # left wall\n", "\n", "# ω0[ny//2,:nx//2] = 1.0/dy\n", "# ω0[ny//2-1,nx//2:] = 1/dy\n", "\n", "t = 0.0\n", "dt = 0.001\n", "\n", "tol = 1e-3\n", "maxIt = 30\n", "β = 1.5\n", "ν = 0.05\n", "psi = []\n", "psi.append(ψ0)\n", "omega = []\n", "omega.append(ω0)\n", "# plt.contour(omega[0])\n", "dt = min(0.25*dx*dx/ν, 4.0*ν/Ut/Ut)\n", "tend = 4*dt\n", "# print(ν*dt/dx/dx <= 0.25)\n", "# print(Ut**2*dt/ν <= 4.0)\n", "print('dt =', dt)\n", "print('Reynods =', Ut*lx/ν)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "while t < tend:\n", "\n", " # first find ψ by solving the Poisson equation Δψ = -ω\n", " it = 0\n", " err = 1e3\n", " ψ = psi[-1].copy() \n", " ωn = omega[-1] \n", " while err > tol and it < maxIt:\n", " ψk = np.zeros_like(ψ)\n", " ψk[1:-1,1:-1] = ψ[1:-1,1:-1] # set interior values\n", " for i in range(1,nx-1):\n", " for j in range(1,ny-1):\n", " rhs = (dx*dy)**2 * ωn[j,i] + dx**2 * (ψ[j+1,i] + ψ[j-1,i]) + dy**2 * (ψ[j,i+1] + ψ[j,i-1])\n", " rhs *= β/2.0/(dx**2 + dy**2)\n", " ψ[j,i] = rhs + (1-β)*ψ[j,i]\n", " err = np.linalg.norm(ψ - ψk)\n", " it += 1\n", " psi.append(ψ)\n", "\n", " # declare new array for ω\n", " ω = np.zeros_like(ωn)\n", " \n", " Cx = -(ψ[2:,1:-1] - ψ[:-2,1:-1])/2.0/dy * (ωn[1:-1,2:] - ωn[1:-1,:-2])/2.0/dx\n", " Cy = (ωn[2:,1:-1] - ωn[:-2,1:-1])/2.0/dy * (ψ[1:-1,2:] - ψ[1:-1,:-2])/2.0/dx\n", " Dxy = (ωn[1:-1,2:] - 2.0*ωn[1:-1,1:-1] + ωn[1:-1,:-2])/dx/dx + (ωn[2:,1:-1] -2.0*ωn[1:-1,1:-1] + ωn[:-2,1:-1])/dy/dy\n", " rhs = Cx + Cy + ν*Dxy\n", " ω[1:-1,1:-1] = ωn[1:-1,1:-1] + dt*rhs\n", " \n", " # apply boundary conditions\n", " ω[0,1:-1] = -2.0*ψ[1, 1:-1]/dy/dy + 2.0*Ub/dy # vorticity on bottom wall (stationary)\n", " ω[-1,1:-1] = -2.0*ψ[-2, 1:-1]/dy/dy - 2.0*Ut/dy # vorticity on top wall (moving at Uwall)\n", " ω[1:-1,-1] = -2.0*ψ[1:-1,-2]/dx/dx # right wall\n", " ω[1:-1,0] = -2.0*ψ[1:-1,1]/dx/dx # left wall\n", " \n", " omega.append(ω)\n", " t += dt" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solution = psi[-1]\n", "plt.contour(solution,levels=20)\n", "plt.colorbar()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Calculate the velocity vector field\n", "The next step is to calculate the velocity vector field and double check that we are divergence free" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ψ = psi[-1]\n", "u = np.zeros([ny,nx])\n", "v = np.zeros([ny,nx])\n", "\n", "u[1:-1,1:-1] = (ψ[2:,1:-1] - ψ[:-2,1:-1])/2.0/dy\n", "v[1:-1,1:-1] = -(ψ[1:-1, 2:] - ψ[1:-1,:-2])/2.0/dx\n", "\n", "# now will in the boundary values - they are all zero except at the top boundary where u = Ut\n", "u[-1,1:-1] = Ut\n", "divu = (u[1:-1,2:] - u[1:-1,:-2])/2.0/dx + (v[2:,1:-1] - v[:-2,1:-1])/2.0/dy\n", "print(np.linalg.norm(divu.ravel()))\n", "\n", "# note the size of the velocity vector field - it does not have points at the boundaries." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create some beautiful plots" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0,1,nx)\n", "y = np.linspace(0,1,ny)\n", "xx,yy = np.meshgrid(x,y)\n", "nn = 1\n", "plt.quiver(xx[::nn,::nn],yy[::nn,::nn],u[::nn,::nn],v[::nn,::nn])\n", "# plt.quiver(xx[1:-1:nn,1:-1:nn],yy[1:-1:nn,1:-1:nn],u[::nn,::nn],v[::nn,::nn])\n", "# plt.axis('equal')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=[6,6],dpi=600)\n", "ax = fig.add_subplot(111)\n", "ax.contourf(xx, yy, np.sqrt(u*u + v*v), levels = 100, cmap=plt.cm.jet)\n", "ax.streamplot(xx,yy,u, v, color=np.sqrt(u*u + v*v),density=1.1,cmap=plt.cm.autumn,linewidth=1.5)\n", "# ax.set_xlim([xx[0,0],xx[0,-1]])\n", "# ax.set_ylim([0,1])\n", "# ax.set_aspect(1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0,1,nx)\n", "y = np.linspace(0,1,ny)\n", "xx,yy = np.meshgrid(x,y)\n", "nn = 3\n", "\n", "fig = plt.figure(figsize=(6.1,5),facecolor='w',dpi=150)\n", "ax = fig.add_subplot(111)\n", "\n", "\n", "ims = []\n", "# levs = np.linspace(-1,1,20)\n", "i = 0\n", "t = 0.0\n", "for sol in psi: \n", " if (i%10==0): \n", " u = (sol[2:,1:-1] - sol[:-2,1:-1])/2.0/dy\n", " v = -(sol[1:-1, 2:] - sol[1:-1,:-2])/2.0/dx\n", " im = ax.contourf(xx[1:-1,1:-1], yy[1:-1,1:-1], np.sqrt(u*u + v*v), levels = 100, cmap=plt.cm.jet)\n", "# im = ax.streamplot(xx[1:-1,1:-1],yy[1:-1,1:-1],u,v, color=abs(u*u + v*v),cmap=plt.cm.autumn,linewidth=1.0)\n", "# im = plt.contourf(solution,cmap=cm.jet,levels=100)\n", " ims.append(im.collections)\n", " i+=1\n", " t += dt\n", "\n", "ax.set_xlim([0,1])\n", "ax.set_ylim([0,1])\n", "ax.set_aspect(1)\n", "\n", "# cbar = plt.colorbar()\n", "# plt.clim(-10,10)\n", "# cbar.set_ticks(np.linspace(0,10,5))\n", "\n", "ani = animation.ArtistAnimation(fig, ims, interval=35, blit=True,repeat_delay=1000)\n", "\n", "ani" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "divu = (u[1:-1, 2:] - u[1:-1,:-2])/2.0/dx + (v[2:,1:-1] - v[:-2,1:-1])/2.0/dy" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.imshow(divu)\n", "plt.colorbar()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import urllib\n", "import requests\n", "from IPython.core.display import HTML\n", "def css_styling():\n", " styles = requests.get(\"https://raw.githubusercontent.com/saadtony/NumericalMethods/master/styles/custom.css\")\n", " return HTML(styles.text)\n", "css_styling()\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.8" } }, "nbformat": 4, "nbformat_minor": 2 }