Skip to content

Instantly share code, notes, and snippets.

@natronics
Last active December 20, 2015 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save natronics/6148258 to your computer and use it in GitHub Desktop.
Save natronics/6148258 to your computer and use it in GitHub Desktop.
Liquid Rocket back of the envelope
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"name": "liquid_motor_envelope"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Liquid Rocket Motor\n",
"\n",
"# Back Of The Envelope Calculations\n",
"\n",
"---\n",
"\n",
"Get some basic plausible numbers for a small liquid motor suitible for suborbital flight.\n",
"\n",
"#### Includes and Constants\n",
"\n",
"Just to start off let's import `math` and set $g_0$."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from math import pi\n",
"g_0 = 9.8066"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 1
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Define The Motor\n",
"\n",
"We want a ~2 kN LOX/kerosene motor. We can use the program [Rocket Propulsion Analysis](http://www.propulsion-analysis.com/) to do the basic thermodynamic and chemical equilibrium calculations for the given fuels. So we can start off with the following numbers:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"v_e = 2601.0 # m s-1 - Effective propellant velocity (from RPA)\n",
"T = 2500.0 # N - Thrust (set by us)\n",
"OF = 2.56 # - O/F Ratio, standard for LOX/kero\n",
"P = 3.5e6 # Pa - Chamber pressure (set by us)\n",
"O_r = 1146.0 # kg m-3 - Density of LOX\n",
"F_r = 819.0 # kg m-3 - Density of T-1 (rocket grade kero, used in RPA sim)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 2
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Mass Flow Rate\n",
"\n",
"Thrust is directly proportional to the effective velocity, $v_e$ and the mass flow rate of the motor, $\\dot m$.\n",
"\n",
"$$\\text T = \\dot m v_e$$\n",
"\n",
"So we can solve for total $\\dot m$. Since we also know O/F (by mass) we can get out $\\dot m_{ox}$ and $\\dot m_f$ as well."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"mdot = T / v_e\n",
"\n",
"mdot_O = mdot / (1 + (1/OF))\n",
"mdot_F = mdot / (1 + OF)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 3
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Using the density of the fluids involved we can find that"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"flow_O = mdot_O / O_r # m^3\n",
"flow_F = mdot_F / F_r # m^3\n",
"print \"> We eat %0.2f L/s of LOX and %0.2f L/s of kerosene\" % (flow_O * 1.0e3, flow_F * 1.0e3) # convert m3 to L"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"> We eat 0.60 L/s of LOX and 0.33 L/s of kerosene\n"
]
}
],
"prompt_number": 4
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Motor Power\n",
"\n",
"Knowing the total $\\dot m$ and $v_e$ also gets us the motor power. This is rarely used in rocketry because it's much more meaningful and direct to deal with thrust in Newtons instead of 'power' in Watts. Still, it's an interesting aside.\n",
"\n",
"The motor power is effectivly the work done by the exiting gas:\n",
"\n",
"$$\\text P = \\frac{1}{2}\\dot m {v_e}^2$$"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"P_T = 0.5 * mdot * v_e**2\n",
"print \"> Motor ouput power: %0.1f MW\" % (P_T / 1.0e6)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"> Motor ouput power: 3.3 MW\n"
]
}
],
"prompt_number": 5
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Burn Time\n",
"\n",
"Let's take a guess that we need 30 seconds of burn time to get to a reasonable altitude. We can then calculate the total mass of volume of fuel. Assuming a ~6 inch diameter airframe we can guess at tank size too."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"t_bo = 50 # s - Burn time\n",
"A_id = 0.146 # m - airframe ID (6\" OD, 1/8\" walls)\n",
"\n",
"Ox_m = (mdot_O * t_bo) # kg\n",
"Fu_m = (mdot_F * t_bo) # kg\n",
"Ox_v = (Ox_m/O_r) # m3\n",
"Fu_v = (Fu_m/F_r) # m3\n",
"\n",
"h = (Ox_v + Fu_v)/(pi*(A_id/2.0)**2) # Total tank lenght\n",
"\n",
"print \"> Fuel for a %0.0f second burn time:\" % t_bo\n",
"print \"> LOX: %0.1f Liters (%4.1f kg)\" % (Ox_v * 1.0e3, Ox_m)\n",
"print \"> Kerosene: %0.1f Liters (%4.1f kg)\" % (Fu_v * 1.0e3, Fu_m)\n",
"print \"> Total prop: %0.1f Liters (%4.1f kg)\" % ((Ox_v + Fu_v) * 1.0e3, Ox_m+Fu_m)\n",
"print \"> Propelent tank height at %0.0f mm OD: %0.1f m\" % (A_id*1.0e3, h)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"> Fuel for a 50 second burn time:\n",
"> LOX: 30.2 Liters (34.6 kg)\n",
"> Kerosene: 16.5 Liters (13.5 kg)\n",
"> Total prop: 46.6 Liters (48.1 kg)\n",
"> Propelent tank height at 146 mm OD: 2.8 m\n"
]
}
],
"prompt_number": 6
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pump Power\n",
"\n",
"Most interestingly we can guess at the pump shaft power nessisary for a motor of this class. This should help scaling the problem.\n",
"\n",
"All pumps are less than ideal. So we always divide our moving propellant energy by a pump/motor efficiency scaler, $\\eta$.\n",
"\n",
"$$\\text{Pow} = \\frac{\\Delta \\text{P} Q}{\\eta}$$\n",
"\n",
"Where $\\Delta\\text P$ is the differental pressure (pump inlet -> outlet) and $Q$ is the fluid flowrate (m<sup>3</sup>&middot;s<sup>-1</sup>)"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"nu = 0.6 # pump efficiency\n",
"\n",
"Pow_O = (P*(mdot_O/O_r))/nu\n",
"Pow_F = (P*(mdot_F/F_r))/nu\n",
"\n",
"print \"> Pump Stats:\"\n",
"print \"> Oxidizer pump shaft power: %0.1f kW\" % (Pow_O / 1e3)\n",
"print \"> Fuel pump shaft power: %0.1f kW\" % (Pow_F / 1e3)\n",
"print \"> Total Power: %0.1f kW\" % ((Pow_O + Pow_F) / 1e3)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"> Pump Stats:\n",
"> Oxidizer pump shaft power: 3.5 kW\n",
"> Fuel pump shaft power: 1.9 kW\n",
"> Total Power: 5.4 kW\n"
]
}
],
"prompt_number": 7
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Other Numbers\n",
"\n",
"We can also make a stab at less important but fun numbers.\n",
"\n",
"## Propellant Costs\n",
"\n",
"Fuel is cheap. Insanely cheap. The best numbers I can find are bulk rates, so taking them and multiplying by 5 seems safe. The kerosene number is ~5$/gallon. Bascially pump prices. I hope these numbers a large overestimates. Still about a 200 times cheaper than AP!"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"pc_O = 1.0 # $/kg\n",
"pc_F = 10.0 # $/kg\n",
"\n",
"print \"> Price:\"\n",
"print \"> LOX: $%0.2f\" % (Ox_m*pc_O)\n",
"print \"> Kerosene: $%0.2f\" % (Fu_m*pc_F)\n",
"print \"> Total: $%0.2f\" % ((Ox_m*pc_O) + (Fu_m*pc_F))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"> Price:\n",
"> LOX: $34.56\n",
"> Kerosene: $135.00\n",
"> Total: $169.55\n"
]
}
],
"prompt_number": 8
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## NAR Letter Code\n",
"\n",
"What kind of motor is this?"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"NS = T * t_bo # Ns - total impulse\n",
"\n",
"l = 2.5\n",
"for i in xrange(26):\n",
" if NS < l:\n",
" letter = chr(ord('A')+i)\n",
" break\n",
" l = l*2\n",
" \n",
"print \"> Motor letter designation: %s (%0.0f Ns)\" % (letter, NS)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"> Motor letter designation: Q (125000 Ns)\n"
]
}
],
"prompt_number": 9
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# OpenRocket Performance\n",
"\n",
"This reqires making a hypothetical rocket and a fake thrust curve. Here we generate a .rse file:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"motor_type = \"Liquid\"\n",
"manufacturer = \"natronics\"\n",
"\n",
"casing_mass = 5.0 #kg - Lets say the motor and tanks weighs at least 5 kg\n",
"impulse_description = letter+str(T)\n",
"average_Isp = v_e / g_0\n",
"\n",
"def motor():\n",
" b = \"\"\n",
" dt = 1.0\n",
" mass = Ox_m + Fu_m\n",
" for i in xrange(int(t_bo/dt)):\n",
" t = i*dt\n",
" mass -= mdot * dt\n",
" b += ' <eng-data cg=\"600\" f=\"%0.1f\" m=\"%0.1f\" t=\"%0.1f\" />\\n' % (T, mass*1.0e3, t)\n",
" return b\n",
"\n",
" \n",
"buf = \"\"\"<engine-database>\n",
" <engine-list>\\n\"\"\"\n",
"\n",
"buf += ' <engine '\n",
"buf += 'mfg=\"%s\" ' % manufacturer\n",
"buf += 'Type=\"%s\" ' % motor_type\n",
"buf += 'code=\"%s\" ' % impulse_description\n",
"buf += 'Isp=\"%0.1f\" ' % average_Isp\n",
"buf += 'Itot=\"%0.1f\" ' % NS\n",
"buf += 'avgThrust=\"%0.1f\" ' % T\n",
"buf += 'peakThrust=\"%0.1f\" ' % T\n",
"buf += 'burn-time=\"%0.2f\" ' % t_bo\n",
"\n",
"buf += 'dia=\"%0.1f\" ' % (A_id*1.0e3) #mm\n",
"buf += 'len=\"%0.1f\" ' % (h*1.0e3) #mm\n",
"\n",
"buf += 'initWt=\"%0.1f\" ' % ((casing_mass + Ox_m + Fu_m)*1.0e3) # g\n",
"buf += 'propWt=\"%0.1f\" ' % ((Ox_m+Fu_m)*1.0e3) # g\n",
"buf += 'massFrac=\"%.1f\" ' % ((Ox_m+Fu_m) / casing_mass)\n",
"\n",
"buf += 'exitDia=\"0.\" '\n",
"buf += 'throatDia=\"0.\" '\n",
"buf += 'auto-calc-cg=\"1\" auto-calc-mass=\"1\" '\n",
"buf += 'FDiv=\"10\" FFix=\"1\" FStep=\"-1.\" '\n",
"buf += 'mDiv=\"10\" mFix=\"1\" mStep=\"-1.\" '\n",
"buf += 'cgDiv=\"10\" cgFix=\"1\" cgStep=\"-1.\" '\n",
"buf += 'tDiv=\"10\" tFix=\"1\" tStep=\"-1.\" '\n",
"buf += 'delays=\"1000\" ' # No delay\n",
"buf += '>\\n'\n",
"\n",
"buf += \"\"\" <comments>\n",
" This is a back of envelope motor.\n",
" </comments>\\n\"\"\"\n",
"\n",
"buf += ' <data>\\n'\n",
"buf += motor()\n",
"buf += ' </data>\\n'\n",
"\n",
"buf += \"\"\" </engine>\n",
" </engine-list>\n",
"</engine-database>\"\"\"\n",
"\n",
"with open('back-env-liquid.rse', 'w') as rse:\n",
" rse.write(buf)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 10
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Import the file in OpenRocket by placeing in `~/.openrocket/ThrustCurves`\n",
"\n",
"Using this motor on the bottom of an PSAS LV2.3 rocket we get the following performance data:\n",
"\n",
" - GLOW: 87 kg\n",
" - T/W: 3\n",
" - Max velocty: 1224 m/s (Mach 3.7)\n",
" - Altitude: 100 km"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from IPython.core.display import HTML\n",
"def css_styling():\n",
" styles = open(\"./local.css\", \"r\").read()\n",
" return HTML(styles)\n",
"css_styling()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<style>\n",
" div.cell{\n",
" width:800px;\n",
" margin-left:16% !important;\n",
" margin-right:auto;\n",
" }\n",
" div.text_cell_render{\n",
" width:800px;\n",
" margin-left:auto;\n",
" margin-right:auto;\n",
" }\n",
" .prompt {\n",
" display: none;\n",
" }\n",
"\n",
" .MathJax_Display {\n",
" font-size: 130%;\n",
" padding-left: 100px;\n",
" }\n",
"\n",
" .cell h1 {\n",
" font-size: 2.5em;\n",
" }\n",
"\n",
" .cell p {\n",
" font-size: 1.2em;\n",
" }\n",
"\n",
" .output {\n",
" font-weight: bold;\n",
" }\n",
"</style>\n"
],
"output_type": "pyout",
"prompt_number": 11,
"text": [
"<IPython.core.display.HTML at 0x2045110>"
]
}
],
"prompt_number": 11
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 11
}
],
"metadata": {}
}
]
}
<style>
div.cell{
width:800px;
margin-left:16% !important;
margin-right:auto;
}
div.text_cell_render{
width:800px;
margin-left:auto;
margin-right:auto;
}
.prompt {
display: none;
}
.MathJax_Display {
font-size: 130%;
padding-left: 100px;
}
.cell h1 {
font-size: 2.5em;
}
.cell p {
font-size: 1.2em;
}
.output {
font-weight: bold;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment