{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Gravity Drain Tank Modeling" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "%matplotlib widget" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from pysketcher import *" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from IPython.display import HTML, SVG, display, clear_output" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from ipywidgets import interact, FloatSlider, Button" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "from math import pi\n", "import numpy as np\n", "from ipywidgets import VBox, FloatSlider, Output, Button, Label" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from scipy.integrate import odeint\n", "import time" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Experiment, Simulation (x10)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "myfig={}\n", "sketch = Sketch(myfig)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sketch.file2Sketch(\"drainingtank.yml\")" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "def tank(h):\n", " sketch.container['h'] = h\n", " sketch.refresh('interieur')\n", " sketch.refresh('contenu')\n", " sketch.refresh('hc')\n", " sketch.refresh('V')\n", " sketch.refresh('Y')\n", " sketch.refresh('jet')\n", " sketch.refresh('dim')\n", " sketch.refresh('tank')\n", " drawing_tool.erase()\n", " sketch.container['tank'].draw()\n", " display(SVG(Sketch.matplotlib2SVG()))" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b45518327af34b0b8f3eb769326c7b3d", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(FloatSlider(value=1.5, description='h', max=2.0, min=0.12), Output()), _dom_classes=('wi…" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "024ee43ca2594a5b93ecccfc213eed8d", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Button(description='Simulate', style=ButtonStyle())" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "w = FloatSlider(min=sketch.container['d_o']*1.2, max=sketch.container['H'], step=0.1, value=sketch.container['h'])\n", "b = Button(description=\"Simulate\")\n", "i = interact(tank, h=w)\n", "display(i,b)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "xSIqWFm93OPg", "nbpages": { "level": 2, "link": "[2.2.2 Torricelli's law](https://jckantor.github.io/CBE30338/02.02-Gravity-Drained-Tank.html#2.2.2-Torricelli's-law)", "section": "2.2.2 Torricelli's law" } }, "source": [ "## Torricelli's law\n", "\n", "Torricelli's law states the velocity of an incompressible liquid stream exiting a liquid tank at level $h$ below the surface is \n", "\n", "$$v = \\sqrt{2gh}$$ \n", "\n", "This is the same velocity as an object dropped from a height $h$. The derivation is straightforward. From Bernoulli's principle,\n", "\n", "$$\\frac{v^2}{2} + gh + \\frac{P}{\\rho} = \\text{constant}$$\n", "\n", "Applying this principle, compare a drop of water just below the surface of the water at distance $h$ above the exit, to a drop of water exiting the tank\n", "\n", "$$gh + \\frac{P_{atm}}{\\rho} = \\frac{v^2}{2} + \\frac{P_{atm}}{\\rho}$$\n", "\n", "$$\\implies v^2 = 2gh$$\n", "$$\\implies v = \\sqrt{2gh}$$\n", "\n", "Torricelli's law is an approximation that doesn't account for the effects of fluid viscosity, the specific flow geometry near the exit, or other flow non-idealities. Nevertheless it is a useful first approximation for flow from a tank." ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "H8dmoxAJ3OPk", "nbpages": { "level": 2, "link": "[2.2.3 Mass Balance for Tank with Constant Cross-Sectional Area](https://jckantor.github.io/CBE30338/02.02-Gravity-Drained-Tank.html#2.2.3-Mass-Balance-for-Tank-with-Constant-Cross-Sectional-Area)", "section": "2.2.3 Mass Balance for Tank with Constant Cross-Sectional Area" } }, "source": [ "# Mass Balance for Tank with Constant Cross-Sectional Area\n", "\n", "For a tank with constant cross-sectional area, such as a cylindrical or rectangular tank, the liquid height is described by a differential equation\n", "\n", "$$A\\frac{dh}{dt} = q_{in}(t) - q_{out}(t)$$\n", "\n", "where $$q_{out}$$ is a function of liquid height. Torricelli's law tells the outlet flow from the tank is proportional to square root of the liquid height\n", "\n", "$$ q_{out}(h) = C_v\\sqrt{h} $$\n", "\n", "Dividing by area we obtain a nonlinear ordinary differential equation \n", "\n", "$$ \\frac{dh}{dt} = - \\frac{C_V}{A}\\sqrt{h} + \\frac{1}{A}q_{in}(t) $$\n", "\n", "in our standard form where the LHS derivative appears with a constant coefficient of 1." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Equation Solving" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Construction parameters" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "A = pi * sketch.container['l']**2 / 4 # Tank area [meter^2]\n", "h0 = sketch.container['h']-sketch.container['d_o'] # tank initial height\n", "Cv = 0.1/60 # Outlet valve constant [cubic meters/sec/meter^1/2]" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.4\n" ] } ], "source": [ "print(h0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Physical Constants" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "g = sketch.container['g'] # gravity constant" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Time Horizon" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "horizon = 10 * 60 # simulation period" ] }, { "cell_type": "markdown", "metadata": { "colab": {}, "colab_type": "code", "id": "qYEqVEsU3OPv", "nbpages": { "level": 3, "link": "[2.2.4.2 Step 2. Define parameters](https://jckantor.github.io/CBE30338/02.02-Gravity-Drained-Tank.html#2.2.4.2-Step-2.-Define-parameters)", "section": "2.2.4.2 Step 2. Define parameters" } }, "source": [ "## System Solving" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "colab": {}, "colab_type": "code", "id": "nuIhX1VO3OPy", "nbpages": { "level": 3, "link": "[2.2.4.3 Step 3. Write Functions for the RHS of the Differential Equations](https://jckantor.github.io/CBE30338/02.02-Gravity-Drained-Tank.html#2.2.4.3-Step-3.-Write-Functions-for-the-RHS-of-the-Differential-Equations)", "section": "2.2.4.3 Step 3. Write Functions for the RHS of the Differential Equations" } }, "outputs": [], "source": [ "# inlet flow rate in cubic meters/sec\n", "def qin(t):\n", " return 0.0\n", "\n", "def deriv(h,t):\n", " return qin(t)/A - Cv*np.sqrt(np.abs(h))/A" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "colab": {}, "colab_type": "code", "id": "HiA2HZcb3OP1", "nbpages": { "level": 3, "link": "[2.2.4.4 Step 4. Choose an Initial Condition, a Time Grid, and Integrate the Differential Equation](https://jckantor.github.io/CBE30338/02.02-Gravity-Drained-Tank.html#2.2.4.4-Step-4.-Choose-an-Initial-Condition,-a-Time-Grid,-and-Integrate-the-Differential-Equation)", "section": "2.2.4.4 Step 4. Choose an Initial Condition, a Time Grid, and Integrate the Differential Equation" } }, "outputs": [], "source": [ "def solver():\n", " global D,t,h,v0\n", " D = 2 * np.sqrt(A/pi)\n", " IC = [h0]\n", " t = np.linspace(0,horizon,101)\n", " h = odeint(deriv,IC,t)\n", " v0 = np.sqrt(2*g*np.abs(h))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "solver()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Animating Gravity Drain Tank" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "def simulation(b):\n", " global h, sketch, w\n", " for hin in [hauteur for hauteur in h if hauteur > sketch.container['d_o']]:\n", " w.value = hin\n", " time.sleep(0.1)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "b.on_click(simulation)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "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.8.2" } }, "nbformat": 4, "nbformat_minor": 4 }