{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c6b2e4d0-bf43-4a8a-a8e7-4fa081d61317",
   "metadata": {
    "extensions": {
     "jupyter_dashboards": {
      "activeView": "grid_default",
      "views": {
       "grid_default": {
        "col": 0,
        "height": 1,
        "hidden": false,
        "row": 0,
        "width": 1
       }
      }
     }
    },
    "tags": []
   },
   "source": [
    "## Hot air balloon - lift calculator\n",
    "### Inverted pyramid shape\n",
    "[Source code](https://gitea.citizen4.eu/sp9unb/balloon-calc) \n",
    "\n",
    "[Run in mybinder.org IDE](https://mybinder.org/v2/git/https%3A%2F%2Fgitea.citizen4.eu%2Fsp9unb%2Fballoon-calc/HEAD?labpath=work%2Fhotair-pyramid.ipynb) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "6ea87a15-f571-4611-8487-5b7556e2ef45",
   "metadata": {
    "extensions": {
     "jupyter_dashboards": {
      "activeView": "grid_default",
      "views": {
       "grid_default": {
        "col": null,
        "height": 2,
        "hidden": true,
        "row": null,
        "width": 2
       }
      }
     }
    }
   },
   "outputs": [],
   "source": [
    "# https://pythreejs.readthedocs.io\n",
    "from pythreejs import *\n",
    "from IPython.display import HTML,display\n",
    "from math import pi\n",
    "\n",
    "# https://ipywidgets.readthedocs.io\n",
    "import ipywidgets as widgets\n",
    "from ipywidgets import Layout\n",
    "\n",
    "# https://pypi.org/project/termcolor/\n",
    "from termcolor import colored\n",
    "\n",
    "#from scipy.interpolate import interp1d\n",
    "#import numpy as np\n",
    "\n",
    "#math\n",
    "from math import sin, tan, sqrt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "d075a994-76bb-46f7-bdfb-cc6ef449dc43",
   "metadata": {
    "extensions": {
     "jupyter_dashboards": {
      "activeView": "grid_default",
      "views": {
       "grid_default": {
        "col": null,
        "height": 2,
        "hidden": true,
        "row": null,
        "width": 2
       }
      }
     }
    }
   },
   "outputs": [],
   "source": [
    "# air density–temperature relationship at 1 atm or 101.325 kPa\n",
    "# https://en.wikipedia.org/wiki/Density_of_air\n",
    "# https://www.engineersedge.com/calculators/air-density.htm\n",
    "# temp in st.C   density in g/m3\n",
    "\n",
    "#temp = [-25,-20,-15,-10,-5,0,5,10,15,20,25,30,35]\n",
    "#dens = [1422.4,1394.3,1367.3,1341.3,1316.3,1292.2,1269.0,1246.6,1225.0,1204.1,1183.9,1164.4,1145.5]\n",
    "#dens_temp_func = interp1d(temp, dens)\n",
    "\n",
    "\n",
    "def airDensity(temp=0.0):\n",
    "    tempK = temp + 273.0   # absolute temperature [K]\n",
    "    p = 101325.0           # pressure [Pa]\n",
    "    rSpec = 287.0500676    # specific gas constant for dry air [J⋅kg−1⋅K−1]\n",
    "    return 1000.0 * p / ( rSpec * tempK )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "612a3232-e9cb-4405-86dd-bd60ac09cd2f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ascent rate at ground level\n",
    "# https://northstar-www.dartmouth.edu/~klynch/pmwiki-gc/uploads/BalloonCalulations.pdf\n",
    "\n",
    "# Coefficient of Drag assumed to 0.285 (spherical top)  \n",
    "Cd = 0.285\n",
    "g = 9.81 # [m/s2]\n",
    "\n",
    "def ascentRate(volume=1.0, airDens=1.0, liftForce=1.0, topArea=1.0): \n",
    "    val = ( 2 * liftForce * g  ) / ( Cd * airDens * topArea )\n",
    "    if val < 0.0:\n",
    "        val = 0.0\n",
    "    return sqrt( val )\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "229c0a36-bf1f-4c07-884c-f3522d5eaae5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# math: https://pl.wikipedia.org/wiki/Wielok%C4%85t_foremny\n",
    "\n",
    "# radius of the circle described on the polygon\n",
    "def outradius(a=1.0,n=3):\n",
    "    return a / ( 2 * sin( pi / n ))\n",
    "\n",
    "# radius of the circle inscribed in the polygon\n",
    "def inradius(a=1.0,n=3):\n",
    "    return a / ( 2 * tan( pi / n ))   \n",
    "\n",
    "# edge length\n",
    "def edge_len(r=1.0,h=1.0):\n",
    "    return sqrt(pow(r,2) + pow(h,2))\n",
    "\n",
    "#pyramid area/volume\n",
    "def base_area(a=1.0,n=3):\n",
    "    return 0.5 * n * pow(outradius(a,n),2) * sin( 2 * pi / n)\n",
    "\n",
    "def volume(a=1.0,h1=1.0,h2=1.0,n=3):\n",
    "    return base_area(a,n) * ( h1 + h2 ) / 3\n",
    "           \n",
    "def surface_area(a=1.0,h1=1.0,h2=1.0,n=3):\n",
    "    return 0.5 * a * n * ( sqrt( pow(h1,2) + pow(inradius(a,n),2)) + sqrt( pow(h2,2) + pow(inradius(a,n),2)) )\n",
    "            \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "2f3b63fd-d5ea-40b6-a2f9-b31c84cfef36",
   "metadata": {
    "extensions": {
     "jupyter_dashboards": {
      "activeView": "grid_default",
      "views": {
       "grid_default": {
        "col": null,
        "height": 2,
        "hidden": true,
        "row": null,
        "width": 2
       }
      }
     }
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       "     .widget-hbox  { margin-top: 20px; }\n",
       " </style>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "3db54b9e2ef24677a45a78c94e21ed79",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(IntSlider(value=3, description='Segment num:', layout=Layout(width='500px'), max=12, min=3, sty…"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def f(num,width,heightT,heightB,airTemp,hotAirTemp,coatDens,tapeDens,payloadWeight):\n",
    "\n",
    "    out=widgets.Output(layout={'margin': '10px 10px 10px 20px'})     \n",
    "    with out:        \n",
    "    # Volume\n",
    "        vol = volume(width,heightB,heightT,num)\n",
    "        print( \"{:<20}{:>6.1f} [m3]\".format(\"Volume:\",vol)) \n",
    "    # Area\n",
    "        area = surface_area(width,heightB,heightT,num)\n",
    "        print( \"{:<20}{:>6.1f} [m2]\".format(\"Area:\",area)) \n",
    "    # Coating density (from: protective film for painting): 300g / 4x5m = 15g/m2   \n",
    "        coatingWeight = coatDens * area\n",
    "        print( \"{:<20}{:>6.1f} [g]\".format(\"Coating weight:\",coatingWeight)) \n",
    "    # adhesive tape density : average 2g/m   \n",
    "        tapeLength = num * ( edge_len(outradius(width,num),heightT) + edge_len(outradius(width,num),heightB)  )\n",
    "        tapeWeight = tapeDens * tapeLength\n",
    "        print( \"{:<20}{:>6.1f} [m]\".format(\"Adh.tape length:\",tapeLength))          \n",
    "        print( \"{:<20}{:>6.1f} [g]\".format(\"Adh.tape weight:\",tapeWeight))    \n",
    "        totalWeight = coatingWeight + tapeWeight\n",
    "        print( \"{:<20}{:>6.1f} [g]\".format(\"Total ball. weight:\",totalWeight))        \n",
    "    # Lift per volume g/m3  \n",
    "    #    airDens = dens_temp_func( airTemp )\n",
    "    #    hotAirDens = dens_temp_func( hotAirTemp )    \n",
    "        airDens = airDensity( airTemp )\n",
    "        hotAirDens = airDensity( hotAirTemp )  \n",
    "        lift = airDens - hotAirDens\n",
    "        print( \"{:<20}{:>6.1f} [g/m3]\".format(\"Lift/volume:\",lift))      \n",
    "    # Total lift force  lift * volume \n",
    "        totalLiftForce = lift * vol\n",
    "        print( \"{:<20}{:>6.1f} [g]\".format(\"Total lift force:\",totalLiftForce))    \n",
    "    # Free lift force totalLiftForce - coatingWeight \n",
    "        freeLiftForce = totalLiftForce - totalWeight - payloadWeight\n",
    "        if freeLiftForce < 0:\n",
    "            color = 'red'\n",
    "        else:\n",
    "            color = None\n",
    "        print( colored(\"{:<20}{:>6.1f} [g]\".format(\"Free lift force:\",freeLiftForce),color))  \n",
    "    # Ascent rate        \n",
    "        topArea = base_area(width,num)\n",
    "        ascRate = ascentRate(volume, airDens/1000.0, freeLiftForce/1000.0, topArea)\n",
    "        print( \"{:<20}{:>6.1f} [m/s]\".format(\"Ascent rate:\",ascRate))  \n",
    "    \n",
    "# coating geometry    \n",
    "    bottomGeometry = CylinderBufferGeometry(\n",
    "                            radiusTop=outradius(width,num), \n",
    "                            radiusBottom=0.0, \n",
    "                            height=heightB, \n",
    "                            radialSegments=num, \n",
    "                            heightSegments=1, \n",
    "                            openEnded=True, \n",
    "                            thetaStart=0, \n",
    "                            thetaLength=2.0*pi)\n",
    "    \n",
    "    bottomCylinder = Mesh(bottomGeometry,\n",
    "                          material=MeshLambertMaterial(color='#c0c0c0'),\n",
    "                          position=[0,0,0]      \n",
    "                   )\n",
    "    \n",
    "    zTop = ( heightB + heightT ) / 2.0\n",
    "    topGeometry = CylinderBufferGeometry(\n",
    "                            radiusTop=0.0, \n",
    "                            radiusBottom=outradius(width,num), \n",
    "                            height=heightT, \n",
    "                            radialSegments=num, \n",
    "                            heightSegments=1, \n",
    "                            openEnded=True, \n",
    "                            thetaStart=0, \n",
    "                            thetaLength=2.0*pi)\n",
    "    \n",
    "    topCylinder = Mesh(topGeometry,\n",
    "                          material=MeshLambertMaterial(color='#c0c0c0'),\n",
    "                          position=[0,zTop,0]      \n",
    "                   )    \n",
    "    \n",
    "    keyLight = DirectionalLight(color='white', position=[3, 5, 1], intensity=0.5)\n",
    "\n",
    "    c = PerspectiveCamera(position=[2, 2, 6], up=[0, 1, 0], children=[keyLight])\n",
    "\n",
    "    scene = Scene(children=[bottomCylinder,topCylinder, c, AmbientLight(color='#777777')], background=None)\n",
    "\n",
    "    renderer = Renderer(camera=c,\n",
    "                        scene=scene,\n",
    "                        alpha=True,\n",
    "                        clearOpacity=1.0,\n",
    "                        clearColor='#62a0ea',\n",
    "                        controls=[OrbitControls(controlling=c)])\n",
    "    display(widgets.HBox([renderer, out]))  \n",
    "\n",
    "\n",
    "\n",
    "display(HTML('''<style>\n",
    "     .widget-hbox  { margin-top: 20px; }\n",
    " </style>'''))\n",
    "\n",
    "layout=Layout(width='500px')\n",
    "style = {'description_width': 'initial'}\n",
    "  \n",
    "num=widgets.IntSlider(min=3, max=12, step=1, value=3, description='Segment num:',layout=layout,style=style)     \n",
    "width=widgets.FloatSlider(min=0.1, max=5.0, step=0.1, value=3.0, description='Segment width [m]:',readout_format='.1f',layout=layout,style=style) \n",
    "heightT=widgets.FloatSlider(min=0.1, max=5.0, step=0.1, value=1.0, description='Height top [m]:',readout_format='.1f',layout=layout,style=style) \n",
    "heightB=widgets.FloatSlider(min=0.1, max=5.0, step=0.1, value=2.0, description='Height bottom [m]:',readout_format='.1f',layout=layout,style=style) \n",
    "airTemp=widgets.FloatSlider(min=-40, max=35, step=1.0, value=10.0, description='Air temp.[°C]:',readout_format='.0f',layout=layout,style=style) \n",
    "hotAirTemp=widgets.FloatSlider(min=-40, max=55, step=1.0, value=35.0, description='Hot air temp.[°C]:',readout_format='.0f',layout=layout,style=style)\n",
    "coatDens=widgets.FloatSlider(min=1, max=50, step=1.0, value=15.0, description='Coating density [g/m2]:',readout_format='.0f',layout=layout,style=style)   \n",
    "tapeDens=widgets.FloatSlider(min=0.5, max=10, step=0.5, value=2.0, description='Adh. tape density [g/m]:',readout_format='.1f',layout=layout,style=style)\n",
    "payloadWeight=widgets.FloatSlider(min=0.0, max=1000.0, step=1, value=0.0, description='Payload weight [g]:',readout_format='.0f',layout=layout,style=style)\n",
    "    \n",
    "w = widgets.interactive_output(f, { 'num' : num,\n",
    "                                    'width' : width,\n",
    "                                    'heightT' : heightT,\n",
    "                                    'heightB' : heightB,\n",
    "                                    'airTemp' : airTemp,\n",
    "                                    'hotAirTemp' : hotAirTemp,\n",
    "                                    'coatDens' : coatDens,\n",
    "                                    'tapeDens' : tapeDens,\n",
    "                                    'payloadWeight' : payloadWeight\n",
    "                                  }\n",
    "                              )\n",
    "\n",
    "widgets.VBox([num, width, heightT, heightB, airTemp, hotAirTemp, coatDens,tapeDens,payloadWeight, w])\n"
   ]
  }
 ],
 "metadata": {
  "extensions": {
   "jupyter_dashboards": {
    "activeView": "grid_default",
    "version": 1,
    "views": {
     "grid_default": {
      "cellMargin": 2,
      "defaultCellHeight": 60,
      "maxColumns": 12,
      "name": "grid",
      "type": "grid"
     }
    }
   }
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.9.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}