diff --git a/pyturbo/notebooks/gas_generator.ipynb b/pyturbo/notebooks/gas_generator.ipynb index bacd55c..8fbf562 100644 --- a/pyturbo/notebooks/gas_generator.ipynb +++ b/pyturbo/notebooks/gas_generator.ipynb @@ -1,138 +1,138 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "6cd009f7-9662-4cf5-860c-e411371ebf0a", - "metadata": {}, - "outputs": [], - "source": [ - "from pyturbo.systems.gas_generator import GasGenerator" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "6cd009f7-9662-4cf5-860c-e411371ebf0a", + "metadata": {}, + "outputs": [], + "source": [ + "from pyturbo.systems.gas_generator import GasGenerator" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d223162a-7ef1-4796-9d30-ef513ad99697", + "metadata": {}, + "outputs": [], + "source": [ + "from cosapp.drivers import NonLinearSolver\n", + "from cosapp.utils import LogLevel, set_log\n", + "from cosapp.recorders import DataFrameRecorder\n", + "\n", + "set_log()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af960069-e1fe-40dd-b8c1-cadf1d9ecbf7", + "metadata": {}, + "outputs": [], + "source": [ + "core = GasGenerator(\"core\")\n", + "\n", + "core.kp.inlet_tip[0] = 0.25\n", + "core.kp.exit_tip[0] = 0.25\n", + "core.fuel_W = 0.11\n", + "core.turbine.aero.Ncdes = 15.\n", + "core.compressor.geom.blade_hub_to_tip_ratio = 0.8\n", + "core.turbine.sh_out.power = 30e6\n", + "core.turbine.geom.blade_height_ratio = 0.1\n", + "\n", + "core.fl_in.Tt = 409.\n", + "core.fl_in.Pt = 2.67e5\n", + "\n", + "core.compressor.sh_in.power = core.turbine.sh_out.power\n", + "\n", + "core.compressor.geom.blade_hub_to_tip_ratio = 0.7\n", + "\n", + "run = core.add_driver(NonLinearSolver('run', factor=0.8, history=True))\n", + "\n", + "run.add_equation(\"turbine.aero.Ncqdes == 100.\").add_unknown(\"compressor.fl_in.W\", max_rel_step=0.8)\n", + "run.add_equation(\"compressor.aero.phi == 0.45\").add_unknown(\"compressor.aero.phiP\")\n", + "run.add_equation(\"compressor.aero.pr == 11.\")\n", + "run.add_equation(\"combustor.aero.Tcomb == 1650.\").add_unknown(\"fuel_W\")\n", + "run.add_equation(\"compressor.aero.utip == 450.\").add_unknown(\"turbine.aero.Ncdes\")\n", + "run.add_unknown(\"kp.inlet_tip[0]\", lower_bound=1e-5, max_rel_step=0.8).add_target(\"turbine.sh_out.power\")\n", + "run.add_unknown(\"kp.exit_tip[0]\", lower_bound=1e-5, max_rel_step=0.8).add_equation(\"turbine.aero.psi == 1.1\")\n", + "run.add_unknown(\"compressor.aero.xnd\").add_equation(\"compressor.aero.pcnr == 98.\")\n", + "run.add_unknown(\"turbine.geom.blade_height_ratio\", lower_bound=0., upper_bound=1.)\n", + "\n", + "rec = run.runner.add_recorder(DataFrameRecorder(includes='*'))\n", + " \n", + "core.run_drivers()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53e5d904-449d-43da-8070-2a61fcfd2d2f", + "metadata": {}, + "outputs": [], + "source": [ + "run.problem" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1fc2dc38-136c-46ef-b2eb-cb13b1b71e41", + "metadata": {}, + "outputs": [], + "source": [ + "run = core.add_driver(NonLinearSolver('run', factor=0.95, history=True))\n", + "run.add_unknown(\"fuel_W\").add_equation(\"compressor.aero.pcnr == 95.\")\n", + "run.add_unknown(\"compressor.fl_in.W\").add_target(\"turbine.aero.fl_out.Wc\")\n", + "\n", + "core.run_drivers()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6d19600-4e41-44cf-b58b-58557800d491", + "metadata": {}, + "outputs": [], + "source": [ + "core.turbine.aero.dhqt, core.turbine.aero.Ncqdes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29bd570c-984a-4fd8-9680-3d088a612493", + "metadata": {}, + "outputs": [], + "source": [ + "core.jupyter_view(options={\n", + " \"compressor\": dict(opacity=0.8),\n", + " \"combustor\": dict(face_color=\"red\", opacity=0.8),\n", + " \"turbine\": dict(opacity=0.8),\n", + "})" + ] + } + ], + "metadata": { + "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.12" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "d223162a-7ef1-4796-9d30-ef513ad99697", - "metadata": {}, - "outputs": [], - "source": [ - "from cosapp.drivers import NonLinearSolver\n", - "from cosapp.utils import LogLevel, set_log\n", - "from cosapp.recorders import DataFrameRecorder\n", - "\n", - "set_log()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "af960069-e1fe-40dd-b8c1-cadf1d9ecbf7", - "metadata": {}, - "outputs": [], - "source": [ - "core = GasGenerator(\"core\")\n", - "\n", - "core.kp.inlet_tip[0] = 0.25\n", - "core.kp.exit_tip[0] = 0.25\n", - "core.fuel_W = 0.11\n", - "core.turbine.aero.Ncdes = 15.\n", - "core.compressor.geom.blade_hub_to_tip_ratio = 0.8\n", - "core.turbine.sh_out.power = 30e6\n", - "core.turbine.geom.blade_height_ratio = 0.1\n", - "\n", - "core.fl_in.Tt = 409.\n", - "core.fl_in.Pt = 2.67e5\n", - "\n", - "core.compressor.sh_in.power = core.turbine.sh_out.power\n", - "\n", - "core.compressor.geom.blade_hub_to_tip_ratio = 0.7\n", - "\n", - "run = core.add_driver(NonLinearSolver('run', factor=0.8, history=True))\n", - "\n", - "run.add_equation(\"turbine.aero.Ncqdes == 100.\").add_unknown(\"compressor.fl_in.W\", max_rel_step=0.8)\n", - "run.add_equation(\"compressor.aero.phi == 0.45\").add_unknown(\"compressor.aero.phiP\")\n", - "run.add_equation(\"compressor.aero.pr == 11.\")\n", - "run.add_equation(\"combustor.aero.Tcomb == 1650.\").add_unknown(\"fuel_W\")\n", - "run.add_equation(\"compressor.aero.utip == 450.\").add_unknown(\"turbine.aero.Ncdes\")\n", - "run.add_unknown(\"kp.inlet_tip[0]\", lower_bound=1e-5, max_rel_step=0.8).add_target(\"turbine.sh_out.power\")\n", - "run.add_unknown(\"kp.exit_tip[0]\", lower_bound=1e-5, max_rel_step=0.8).add_equation(\"turbine.aero.psi == 1.1\")\n", - "run.add_unknown(\"compressor.aero.xnd\").add_equation(\"compressor.aero.pcnr == 98.\")\n", - "run.add_unknown(\"turbine.geom.blade_height_ratio\", lower_bound=0., upper_bound=1.)\n", - "\n", - "rec = run.runner.add_recorder(DataFrameRecorder(includes='*'))\n", - " \n", - "core.run_drivers()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "53e5d904-449d-43da-8070-2a61fcfd2d2f", - "metadata": {}, - "outputs": [], - "source": [ - "run.problem" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1fc2dc38-136c-46ef-b2eb-cb13b1b71e41", - "metadata": {}, - "outputs": [], - "source": [ - "run = core.add_driver(NonLinearSolver('run', factor=0.95, history=True))\n", - "run.add_unknown(\"fuel_W\").add_equation(\"compressor.aero.pcnr == 95.\")\n", - "run.add_unknown(\"compressor.fl_in.W\").add_target(\"turbine.aero.fl_out.Wc\")\n", - "\n", - "core.run_drivers()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b6d19600-4e41-44cf-b58b-58557800d491", - "metadata": {}, - "outputs": [], - "source": [ - "core.turbine.aero.dhqt, core.turbine.aero.Ncqdes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "29bd570c-984a-4fd8-9680-3d088a612493", - "metadata": {}, - "outputs": [], - "source": [ - "core.jupyter_view(options={\n", - " \"compressor\": dict(opacity=0.8),\n", - " \"combustor\": dict(face_color=\"red\", opacity=0.8),\n", - " \"turbine\": dict(opacity=0.8),\n", - "})" - ] - } - ], - "metadata": { - "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.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/pyturbo/notebooks/turbofan.ipynb b/pyturbo/notebooks/turbofan.ipynb index 72f7a38..b396c83 100644 --- a/pyturbo/notebooks/turbofan.ipynb +++ b/pyturbo/notebooks/turbofan.ipynb @@ -1,570 +1,570 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "ba8b4132-1828-48fa-bf8c-1a1edbcac52f", - "metadata": {}, - "source": [ - "# Turbofan Tutorial\n", - "`pyturbo` library is provided by twiinIT to assembly a simple turbofan system.\n", - "\n", - "The library is made of components: \n", - "\n", - "- `compressor` : fluid out is computed from fluid in and power provided by shaft with constant efficiency. \n", - "- `combustor` : combustion is made considering constant FHV.\n", - "- `turbine` : power is extracted from fluid in considering a given expansion ratio and constant efficiency. \n", - "- `inlet` and `nozzle` are computing `drag` and `thrust` from fluid conditions, ambiant pressure and throat section. \n", - "- `nacelle`: envelop over the engine.\n", - "- `ogv`, `intermediate_casing` and `trf` and structures with aero channels. \n", - "\n", - "They are numerical components:\n", - "\n", - "- `fluid_spitter` is used to split the flow into primary and secondary flow\n", - "- `shaft_spitter` is used to split the shaft power into booster and fan compressor\n", - "\n", - "Aero 0D and simplified geometry are considered. " - ] + "cells": [ + { + "cell_type": "markdown", + "id": "ba8b4132-1828-48fa-bf8c-1a1edbcac52f", + "metadata": {}, + "source": [ + "# Turbofan Tutorial\n", + "`pyturbo` library is provided by twiinIT to assembly a simple turbofan system.\n", + "\n", + "The library is made of components: \n", + "\n", + "- `compressor` : fluid out is computed from fluid in and power provided by shaft with constant efficiency. \n", + "- `combustor` : combustion is made considering constant FHV.\n", + "- `turbine` : power is extracted from fluid in considering a given expansion ratio and constant efficiency. \n", + "- `inlet` and `nozzle` are computing `drag` and `thrust` from fluid conditions, ambiant pressure and throat section. \n", + "- `nacelle`: envelop over the engine.\n", + "- `ogv`, `intermediate_casing` and `trf` and structures with aero channels. \n", + "\n", + "They are numerical components:\n", + "\n", + "- `fluid_spitter` is used to split the flow into primary and secondary flow\n", + "- `shaft_spitter` is used to split the shaft power into booster and fan compressor\n", + "\n", + "Aero 0D and simplified geometry are considered. " + ] + }, + { + "cell_type": "markdown", + "id": "11f89b4c-b79b-45f7-8194-02d6a492fd23", + "metadata": {}, + "source": [ + "A turbofan system is generated." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65abf53d-7674-489d-bdaf-cf2b8444b0b0", + "metadata": {}, + "outputs": [], + "source": [ + "from pyturbo.systems.turbofan import TurbofanWithAtm\n", + "sys = TurbofanWithAtm(\"sys\")\n", + "tf = sys.tf\n", + "atm = sys.atm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6dc31fdb-6dd3-404c-afb4-6fb8c44c488c", + "metadata": {}, + "outputs": [], + "source": [ + "sys" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "481ed621-4d1e-49fa-8b78-abdc27885a1b", + "metadata": {}, + "outputs": [], + "source": [ + "# geometrical view\n", + "sys.run_once()\n", + "\n", + "tf.jupyter_view(options={\n", + " \"fan_module\": dict(opacity=0.7, face_color=\"#92B4EC\"),\n", + " \"fan_module.spinner\": dict(face_color=\"#E1E5EA\", opacity=1.),\n", + " \"fan_duct\": dict(opacity=0.7),\n", + " \"core_cowl\": dict(opacity=0.7),\n", + " \"nacelle\": dict(face_color=\"#E1E5EA\", opacity=0.6),\n", + " \"inlet\": dict(opacity=1.),\n", + " \"gas_generator\": dict(face_color=\"red\", opacity=0.9),\n", + " \"turbine\": dict(face_color=\"#92B4EC\", opacity=0.9),\n", + " \"trf\": dict(opacity=0.6),\n", + "})" + ] + }, + { + "cell_type": "markdown", + "id": "f1d08e45-272d-4ae3-a844-902853f6f2d5", + "metadata": {}, + "source": [ + "# Simulation\n", + "\n", + "The turbofan system has a couple of equations/unknowns to solve. We use `cosapp` non-linear solver for this purpose." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f0935df-92d0-48f9-a1b1-ef2f46220c2a", + "metadata": {}, + "outputs": [], + "source": [ + "from cosapp.drivers import NonLinearSolver" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e58ec415-51ed-4926-9ebe-3aca8204503a", + "metadata": {}, + "outputs": [], + "source": [ + "from cosapp.utils import set_log, LogLevel\n", + "\n", + "set_log(level=LogLevel.INFO)" + ] + }, + { + "cell_type": "markdown", + "id": "34ea90e8-ecac-4e4f-8c64-14eeb25d7084", + "metadata": {}, + "source": [ + "## Direct mode\n", + "`thrust` is computed from `fuel_W`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a194311f-7756-46f2-a851-5693e8688703", + "metadata": {}, + "outputs": [], + "source": [ + "# off-design solver\n", + "run = sys.add_driver(NonLinearSolver('run', max_iter=50, factor=0.8, history=False))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c17a20c-05f7-40ad-9f99-02d02d0f3fd4", + "metadata": {}, + "outputs": [], + "source": [ + "# environment conditions\n", + "atm.altitude = 0.\n", + "atm.mach = 0.\n", + "atm.dtamb = 15.\n", + "\n", + "# use case\n", + "tf.fuel_W = .5\n", + "\n", + "sys.run_drivers()\n", + "\n", + "print('mach =', atm.mach)\n", + "print('pamb =', tf.pamb, 'Pa')\n", + "print('thrust =', round(tf.thrust * 0.224809/1e3, 1), 'klbf')\n", + "print('N1 =', round(tf.N1), \"rpm\")\n", + "print('N2 =', round(tf.N2), \"rpm\")\n", + "print('bpr =', round(tf.bpr, 1))\n", + "print('opr =', round(tf.opr, 1))\n", + "print('T41 =', round(tf.core.turbine.fl_in.Tt), 'K')\n", + "print('sfc =', round(tf.sfc, 3), 'kg/(h*kN)')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7779338-88a7-416a-aea2-c0c2675cc1dc", + "metadata": {}, + "outputs": [], + "source": [ + "run.problem" + ] + }, + { + "cell_type": "markdown", + "id": "913e49f5-1c9f-4c19-a282-466462946e01", + "metadata": {}, + "source": [ + "## control mode\n", + "`fuel_W` is computed to match functional request, here the fan rotational speed `N1` value." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "272747f3-79f4-47d6-a777-bfe03dbcfbf1", + "metadata": {}, + "outputs": [], + "source": [ + "# control solver\n", + "sys.drivers.clear()\n", + "run = sys.add_driver(NonLinearSolver('run'))\n", + "run.runner.add_unknown('tf.fuel_W')\n", + "run.runner.add_target('tf.N1')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "506c3481-bffe-4d99-9a13-931f8800a00d", + "metadata": {}, + "outputs": [], + "source": [ + "# environment conditions\n", + "atm.altitude = 0.\n", + "atm.mach = 0.\n", + "atm.dtamb = 15.\n", + "\n", + "# use case\n", + "tf.N1 = 5000.\n", + "\n", + "sys.run_drivers()\n", + "\n", + "print('mach =', atm.mach)\n", + "print('pamb =', atm.pamb, 'Pa')\n", + "print('thrust =', round(tf.thrust * 0.224809/1e3, 1), 'klbf')\n", + "print('N1 =', round(tf.N1), \"rpm\")\n", + "print('N2 =', round(tf.N2), \"rpm\")\n", + "print('bpr =', round(tf.bpr, 1))\n", + "print('opr =', round(tf.opr, 1))\n", + "print('T41 =', round(tf.core.turbine.fl_in.Tt), 'K')\n", + "print('sfc =', round(tf.sfc, 3), 'kg/(h*kN)')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51a8e27f-0f1b-4267-a6a3-aca650b27fff", + "metadata": {}, + "outputs": [], + "source": [ + "run.problem" + ] + }, + { + "cell_type": "markdown", + "id": "80bf30dd", + "metadata": {}, + "source": [ + "## design mode\n", + "\n", + "Turbofan design characteristics are related to components and physical properties.\n", + "\n", + "### Using design methods" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f5717f1-aafd-4642-a218-562c26f5b467", + "metadata": {}, + "outputs": [], + "source": [ + "# design solver\n", + "sys.drivers.clear()\n", + "design = sys.add_driver(NonLinearSolver('design', max_iter=50, factor=0.8))\n", + "design.runner.add_unknown('tf.fuel_W')\n", + "design.runner.add_target('tf.thrust')\n", + "\n", + "# engine\n", + "tf.thrust = 120e3\n", + "tf.bpr = 5.\n", + "tf.pr_nozzle = 1.1\n", + "\n", + "# inlet\n", + "tf.inlet.aero.mach = 0.5\n", + "\n", + "# fan module\n", + "tf.fan_module.fan.aero.pcnr = 0.95\n", + "tf.fan_module.fan.aero.utip = 420.\n", + "\n", + "# booster\n", + "tf.fan_module.booster.aero.phi = 0.45\n", + "tf.fan_module.booster.aero.psi = 0.35\n", + "tf.fan_module.booster.aero.spec_flow = 180.\n", + "tf.fan_module.booster.aero.pcnr = 95.\n", + "\n", + "# lpt\n", + "tf.turbine.aero.Ncqdes = 100.\n", + "tf.turbine.aero.psi = 1.25\n", + "\n", + "# hpc\n", + "tf.core.compressor.aero.pr = 11.\n", + "tf.core.compressor.aero.utip = 420.\n", + "tf.core.compressor.aero.phi = 0.5\n", + "tf.core.compressor.aero.pcnr = 95.\n", + "\n", + "# hpt\n", + "tf.core.turbine.aero.psi = 1.2\n", + "tf.core.turbine.aero.Ncqdes = 100.\n", + "\n", + "# combustor\n", + "tf.core.combustor.aero.Tcomb = 1700.\n", + "\n", + "# design method (using targets)\n", + "design.runner.design.extend(tf.design_methods['scaling'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ea41fa5-3f9f-4181-9d5f-18c9b5608005", + "metadata": {}, + "outputs": [], + "source": [ + "# environment conditions\n", + "atm.altitude = 0.\n", + "atm.mach = 0.\n", + "atm.dtamb = 15.\n", + "\n", + "sys.run_drivers()\n", + "\n", + "print('mach =', atm.mach)\n", + "print('pamb =', atm.pamb, 'Pa')\n", + "print('W =', round(tf.fl_in.W), 'Kg/s')\n", + "print('thrust =', round(tf.thrust * 0.224809/1e3, 1), 'klbf')\n", + "print('N1 =', round(tf.N1), \"rpm\")\n", + "print('N2 =', round(tf.N2), \"rpm\")\n", + "print('bpr =', round(tf.bpr, 1))\n", + "print('opr =', round(tf.opr, 1))\n", + "print('T41 =', round(tf.core.turbine.fl_in.Tt), 'K')\n", + "print('sfc =', round(tf.sfc, 3), 'kg/(h*kN)')\n", + "print('psi fan =', round(tf.fan_module.fan.aero.psi, 2))\n", + "print('psi booster =', round(tf.fan_module.booster.aero.psi, 2))\n", + "print('psi hpc =', round(tf.core.compressor.aero.psi, 2))" + ] + }, + { + "cell_type": "markdown", + "id": "588a2015-ee95-4c0f-bcbb-e02ef2055a0b", + "metadata": {}, + "source": [ + "### Using raw equations/unknowns (detailed)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e525a11-cfbc-4269-9094-f7a86f37d7ef", + "metadata": {}, + "outputs": [], + "source": [ + "# design mode solver\n", + "sys.drivers.clear()\n", + "design = sys.add_driver(NonLinearSolver('design', max_iter=50, factor=0.8))\n", + "\n", + "# engine\n", + "tf.thrust = 120e3\n", + "tf.bpr = 5.\n", + "tf.pr_nozzle = 1.1\n", + "\n", + "# inlet\n", + "tf.inlet.aero.mach = 0.5\n", + "\n", + "# fan module\n", + "tf.fan_module.fan.aero.pcnr = 0.95\n", + "tf.fan_module.fan.aero.utip = 420.\n", + "\n", + "# booster\n", + "tf.fan_module.booster.aero.phi = 0.45\n", + "tf.fan_module.booster.aero.psi = 0.35\n", + "tf.fan_module.booster.aero.spec_flow = 180.\n", + "tf.fan_module.booster.aero.pcnr = 95.\n", + "\n", + "# lpt\n", + "tf.turbine.aero.Ncqdes = 100.\n", + "tf.turbine.aero.psi = 1.25\n", + "\n", + "# hpc\n", + "tf.core.compressor.aero.pr = 11.\n", + "tf.core.compressor.aero.utip = 420.\n", + "tf.core.compressor.aero.phi = 0.5\n", + "tf.core.compressor.aero.pcnr = 95.\n", + "\n", + "# hpt\n", + "tf.core.turbine.aero.psi = 1.2\n", + "tf.core.turbine.aero.Ncqdes = 100.\n", + "\n", + "# combustor\n", + "tf.core.combustor.aero.Tcomb = 1700.\n", + "\n", + "# engine\n", + "design.runner.add_unknown('tf.fuel_W')\n", + "design.runner.add_target('tf.thrust')\n", + "design.add_unknown('tf.fan_diameter')\n", + "\n", + "# inlet\n", + "design.runner.add_target('tf.inlet.aero.mach')\n", + "\n", + "# fan\n", + "design.add_unknown(\"tf.fan_module.fan.aero.xnd\", max_rel_step=0.5)\n", + "design.add_unknown('tf.fan_module.fan.aero.phiP', lower_bound=0.1, upper_bound=1.5)\n", + "\n", + "design.runner.add_target(\"tf.fan_module.fan.aero.pcnr\")\n", + "design.runner.add_target('tf.fan_module.fan.aero.utip')\n", + "design.runner.add_target('tf.bpr')\n", + "\n", + "# booster\n", + "design.add_unknown('tf.fan_module.geom.booster_radius_ratio')\n", + "design.add_unknown('tf.fan_module.booster.geom.blade_hub_to_tip_ratio', lower_bound=1e-5, upper_bound=1.)\n", + "design.add_unknown('tf.fan_module.booster.aero.phiP')\n", + "design.add_unknown(\"tf.fan_module.booster.aero.xnd\", max_rel_step=0.5)\n", + "\n", + "design.runner.add_target('tf.fan_module.booster.aero.phi')\n", + "design.runner.add_target('tf.fan_module.booster.aero.psi')\n", + "design.runner.add_target('tf.fan_module.booster.aero.spec_flow')\n", + "design.runner.add_target(\"tf.fan_module.booster.aero.pcnr\")\n", + "\n", + "# lpt\n", + "design.add_unknown('tf.geom.turbine_radius_ratio')\n", + "design.add_unknown(\"tf.turbine.geom.blade_height_ratio\", lower_bound=0., upper_bound=1.)\n", + "design.add_unknown('tf.turbine.aero.Ncdes')\n", + "\n", + "design.runner.add_target('tf.turbine.aero.psi')\n", + "design.runner.add_target('tf.turbine.aero.Ncqdes')\n", + "\n", + "# hpc\n", + "design.add_unknown('tf.geom.core_inlet_radius_ratio', max_rel_step=0.8)\n", + "design.add_unknown(\"tf.core.compressor.aero.xnd\", max_rel_step=0.5)\n", + "design.add_unknown(\"tf.core.compressor.aero.phiP\")\n", + "\n", + "design.runner.add_target(\"tf.core.compressor.aero.pcnr\")\n", + "design.runner.add_target(\"tf.core.compressor.aero.phi\")\n", + "design.runner.add_target(\"tf.core.compressor.aero.utip\")\n", + "design.runner.add_target(\"tf.core.compressor.aero.pr\")\n", + "\n", + "# combustor\n", + "design.runner.add_target(\"tf.core.combustor.aero.Tcomb\")\n", + "\n", + "# hpt\n", + "design.add_unknown('tf.geom.core_exit_radius_ratio', max_rel_step=0.8)\n", + "design.add_unknown(\"tf.core.turbine.geom.blade_height_ratio\", lower_bound=0., upper_bound=1.)\n", + "design.add_unknown(\"tf.core.turbine.aero.Ncdes\")\n", + "\n", + "design.runner.add_target(\"tf.core.turbine.aero.psi\")\n", + "design.runner.add_target(\"tf.core.turbine.aero.Ncqdes\")\n", + "\n", + "# nozzle\n", + "design.add_unknown('tf.geom.pri_nozzle_area_ratio', lower_bound=0.05)\n", + "design.add_unknown('tf.geom.sec_nozzle_area_ratio', upper_bound=1.)\n", + "\n", + "design.runner.add_target('tf.pr_nozzle')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b464d54b-90d6-4663-b4c3-bb3ff4e5f108", + "metadata": {}, + "outputs": [], + "source": [ + "# environment conditions\n", + "atm.altitude = 0.\n", + "atm.mach = 0.\n", + "atm.dtamb = 15.\n", + "\n", + "sys.run_drivers()\n", + "\n", + "print('mach =', atm.mach)\n", + "print('pamb =', atm.pamb, 'Pa')\n", + "print('fan diameter =', round(tf.geom.fan_diameter / 0.0254, 1), 'in')\n", + "print('W =', round(tf.fl_in.W), 'Kg/s')\n", + "print('thrust =', round(tf.thrust * 0.224809/1e3, 1), 'klbf')\n", + "print('N1 =', round(tf.N1), \"rpm\")\n", + "print('N2 =', round(tf.N2), \"rpm\")\n", + "print('bpr =', round(tf.bpr, 1))\n", + "print('opr =', round(tf.opr, 1))\n", + "print('T41 =', round(tf.core.turbine.fl_in.Tt), 'K')\n", + "print('sfc =', round(tf.sfc, 3), 'kg/(h*kN)')\n", + "print('psi fan =', round(tf.fan_module.fan.aero.psi, 2))\n", + "print('psi booster =', round(tf.fan_module.booster.aero.psi, 2))\n", + "print('psi hpc =', round(tf.core.compressor.aero.psi, 2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2d53099f-6fd0-4953-90eb-2b3348a4ddfb", + "metadata": {}, + "outputs": [], + "source": [ + "tf.jupyter_view(options={\n", + " \"fan_module\": dict(opacity=0.7, face_color=\"#92B4EC\"),\n", + " \"fan_module.spinner\": dict(face_color=\"#E1E5EA\", opacity=1.),\n", + " \"fan_duct\": dict(opacity=0.3),\n", + " \"core_cowl\": dict(opacity=0.7),\n", + " \"nacelle\": dict(face_color=\"#E1E5EA\", opacity=0.6),\n", + " \"inlet\": dict(opacity=1.),\n", + " \"gas_generator\": dict(face_color=\"red\", opacity=0.9),\n", + " \"tcf\": dict(face_color=\"#92B4EC\", opacity=0.9),\n", + " \"turbine\": dict(face_color=\"#92B4EC\", opacity=0.9),\n", + " \"trf\": dict(opacity=0.6),\n", + "})" + ] + }, + { + "cell_type": "markdown", + "id": "71866bce", + "metadata": {}, + "source": [ + "### off-design computation after design\n", + "\n", + "Fuel consumption for a given altitude/mach/dtamb and thrust. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f83def4e-0b28-4415-a842-226806a4b604", + "metadata": {}, + "outputs": [], + "source": [ + "# off-design mode\n", + "sys.drivers.clear()\n", + "run = sys.add_driver(NonLinearSolver('run'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8de8788d-9b5f-4b39-b377-2b025ad43672", + "metadata": {}, + "outputs": [], + "source": [ + "# environment conditions\n", + "atm.altitude = 10000.\n", + "atm.mach = 0.8\n", + "atm.dtamb = 0.\n", + "\n", + "# requirement\n", + "tf.thrust = 10e3 / 0.224809\n", + "# tf.N1 = 5000.\n", + "\n", + "run.runner.add_unknown('tf.fuel_W')\n", + "run.runner.add_target('tf.thrust')\n", + "# run.runner.add_target('tf.N1')\n", + "\n", + "sys.run_drivers()\n", + "\n", + "print('fuel flow =', round(tf.fuel_W, 1), 'kg/s')\n", + "print('N1 =', round(tf.N1), \"rpm\")\n", + "print('sfc ', round(tf.sfc, 3), 'kg/(h*kN)')" + ] + } + ], + "metadata": { + "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.10.8" + }, + "vscode": { + "interpreter": { + "hash": "0c1f2e9e0acff612fbff7be31f0ea913f7f20427d1d1231e188b099d6c58c52b" + } + } }, - { - "cell_type": "markdown", - "id": "11f89b4c-b79b-45f7-8194-02d6a492fd23", - "metadata": {}, - "source": [ - "A turbofan system is generated." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "65abf53d-7674-489d-bdaf-cf2b8444b0b0", - "metadata": {}, - "outputs": [], - "source": [ - "from pyturbo.systems.turbofan import TurbofanWithAtm\n", - "sys = TurbofanWithAtm(\"sys\")\n", - "tf = sys.tf\n", - "atm = sys.atm" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6dc31fdb-6dd3-404c-afb4-6fb8c44c488c", - "metadata": {}, - "outputs": [], - "source": [ - "sys" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "481ed621-4d1e-49fa-8b78-abdc27885a1b", - "metadata": {}, - "outputs": [], - "source": [ - "# geometrical view\n", - "sys.run_once()\n", - "\n", - "tf.jupyter_view(options={\n", - " \"fan_module\": dict(opacity=0.7, face_color=\"#92B4EC\"),\n", - " \"fan_module.spinner\": dict(face_color=\"#E1E5EA\", opacity=1.),\n", - " \"fan_duct\": dict(opacity=0.7),\n", - " \"core_cowl\": dict(opacity=0.7),\n", - " \"nacelle\": dict(face_color=\"#E1E5EA\", opacity=0.6),\n", - " \"inlet\": dict(opacity=1.),\n", - " \"gas_generator\": dict(face_color=\"red\", opacity=0.9),\n", - " \"turbine\": dict(face_color=\"#92B4EC\", opacity=0.9),\n", - " \"trf\": dict(opacity=0.6),\n", - "})" - ] - }, - { - "cell_type": "markdown", - "id": "f1d08e45-272d-4ae3-a844-902853f6f2d5", - "metadata": {}, - "source": [ - "# Simulation\n", - "\n", - "The turbofan system has a couple of equations/unknowns to solve. We use `cosapp` non-linear solver for this purpose." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1f0935df-92d0-48f9-a1b1-ef2f46220c2a", - "metadata": {}, - "outputs": [], - "source": [ - "from cosapp.drivers import NonLinearSolver" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e58ec415-51ed-4926-9ebe-3aca8204503a", - "metadata": {}, - "outputs": [], - "source": [ - "from cosapp.utils import set_log, LogLevel\n", - "\n", - "set_log(level=LogLevel.INFO)" - ] - }, - { - "cell_type": "markdown", - "id": "34ea90e8-ecac-4e4f-8c64-14eeb25d7084", - "metadata": {}, - "source": [ - "## Direct mode\n", - "`thrust` is computed from `fuel_W`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a194311f-7756-46f2-a851-5693e8688703", - "metadata": {}, - "outputs": [], - "source": [ - "# off-design solver\n", - "run = sys.add_driver(NonLinearSolver('run', max_iter=50, factor=0.8, history=False))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7c17a20c-05f7-40ad-9f99-02d02d0f3fd4", - "metadata": {}, - "outputs": [], - "source": [ - "# environment conditions\n", - "atm.altitude = 0.\n", - "atm.mach = 0.\n", - "atm.dtamb = 15.\n", - "\n", - "# use case\n", - "tf.fuel_W = .5\n", - "\n", - "sys.run_drivers()\n", - "\n", - "print('mach =', atm.mach)\n", - "print('pamb =', tf.pamb, 'Pa')\n", - "print('thrust =', round(tf.thrust * 0.224809/1e3, 1), 'klbf')\n", - "print('N1 =', round(tf.N1), \"rpm\")\n", - "print('N2 =', round(tf.N2), \"rpm\")\n", - "print('bpr =', round(tf.bpr, 1))\n", - "print('opr =', round(tf.opr, 1))\n", - "print('T41 =', round(tf.core.turbine.fl_in.Tt), 'K')\n", - "print('sfc =', round(tf.sfc, 3), 'kg/(h*kN)')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f7779338-88a7-416a-aea2-c0c2675cc1dc", - "metadata": {}, - "outputs": [], - "source": [ - "run.problem" - ] - }, - { - "cell_type": "markdown", - "id": "913e49f5-1c9f-4c19-a282-466462946e01", - "metadata": {}, - "source": [ - "## control mode\n", - "`fuel_W` is computed to match functional request, here the fan rotational speed `N1` value." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "272747f3-79f4-47d6-a777-bfe03dbcfbf1", - "metadata": {}, - "outputs": [], - "source": [ - "# control solver\n", - "sys.drivers.clear()\n", - "run = sys.add_driver(NonLinearSolver('run'))\n", - "run.runner.add_unknown('tf.fuel_W')\n", - "run.runner.add_target('tf.N1')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "506c3481-bffe-4d99-9a13-931f8800a00d", - "metadata": {}, - "outputs": [], - "source": [ - "# environment conditions\n", - "atm.altitude = 0.\n", - "atm.mach = 0.\n", - "atm.dtamb = 15.\n", - "\n", - "# use case\n", - "tf.N1 = 5000.\n", - "\n", - "sys.run_drivers()\n", - "\n", - "print('mach =', atm.mach)\n", - "print('pamb =', atm.pamb, 'Pa')\n", - "print('thrust =', round(tf.thrust * 0.224809/1e3, 1), 'klbf')\n", - "print('N1 =', round(tf.N1), \"rpm\")\n", - "print('N2 =', round(tf.N2), \"rpm\")\n", - "print('bpr =', round(tf.bpr, 1))\n", - "print('opr =', round(tf.opr, 1))\n", - "print('T41 =', round(tf.core.turbine.fl_in.Tt), 'K')\n", - "print('sfc =', round(tf.sfc, 3), 'kg/(h*kN)')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "51a8e27f-0f1b-4267-a6a3-aca650b27fff", - "metadata": {}, - "outputs": [], - "source": [ - "run.problem" - ] - }, - { - "cell_type": "markdown", - "id": "80bf30dd", - "metadata": {}, - "source": [ - "## design mode\n", - "\n", - "Turbofan design characteristics are related to components and physical properties.\n", - "\n", - "### Using design methods" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1f5717f1-aafd-4642-a218-562c26f5b467", - "metadata": {}, - "outputs": [], - "source": [ - "# design solver\n", - "sys.drivers.clear()\n", - "design = sys.add_driver(NonLinearSolver('design', max_iter=50, factor=0.8))\n", - "design.runner.add_unknown('tf.fuel_W')\n", - "design.runner.add_target('tf.thrust')\n", - "\n", - "# engine\n", - "tf.thrust = 120e3\n", - "tf.bpr = 5.\n", - "tf.pr_nozzle = 1.1\n", - "\n", - "# inlet\n", - "tf.inlet.aero.mach = 0.5\n", - "\n", - "# fan module\n", - "tf.fan_module.fan.aero.pcnr = 0.95\n", - "tf.fan_module.fan.aero.utip = 420.\n", - "\n", - "# booster\n", - "tf.fan_module.booster.aero.phi = 0.45\n", - "tf.fan_module.booster.aero.psi = 0.35\n", - "tf.fan_module.booster.aero.spec_flow = 180.\n", - "tf.fan_module.booster.aero.pcnr = 95.\n", - "\n", - "# lpt\n", - "tf.turbine.aero.Ncqdes = 100.\n", - "tf.turbine.aero.psi = 1.25\n", - "\n", - "# hpc\n", - "tf.core.compressor.aero.pr = 11.\n", - "tf.core.compressor.aero.utip = 420.\n", - "tf.core.compressor.aero.phi = 0.5\n", - "tf.core.compressor.aero.pcnr = 95.\n", - "\n", - "# hpt\n", - "tf.core.turbine.aero.psi = 1.2\n", - "tf.core.turbine.aero.Ncqdes = 100.\n", - "\n", - "# combustor\n", - "tf.core.combustor.aero.Tcomb = 1700.\n", - "\n", - "# design method (using targets)\n", - "design.runner.design.extend(tf.design_methods['scaling'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4ea41fa5-3f9f-4181-9d5f-18c9b5608005", - "metadata": {}, - "outputs": [], - "source": [ - "# environment conditions\n", - "atm.altitude = 0.\n", - "atm.mach = 0.\n", - "atm.dtamb = 15.\n", - "\n", - "sys.run_drivers()\n", - "\n", - "print('mach =', atm.mach)\n", - "print('pamb =', atm.pamb, 'Pa')\n", - "print('W =', round(tf.fl_in.W), 'Kg/s')\n", - "print('thrust =', round(tf.thrust * 0.224809/1e3, 1), 'klbf')\n", - "print('N1 =', round(tf.N1), \"rpm\")\n", - "print('N2 =', round(tf.N2), \"rpm\")\n", - "print('bpr =', round(tf.bpr, 1))\n", - "print('opr =', round(tf.opr, 1))\n", - "print('T41 =', round(tf.core.turbine.fl_in.Tt), 'K')\n", - "print('sfc =', round(tf.sfc, 3), 'kg/(h*kN)')\n", - "print('psi fan =', round(tf.fan_module.fan.aero.psi, 2))\n", - "print('psi booster =', round(tf.fan_module.booster.aero.psi, 2))\n", - "print('psi hpc =', round(tf.core.compressor.aero.psi, 2))" - ] - }, - { - "cell_type": "markdown", - "id": "588a2015-ee95-4c0f-bcbb-e02ef2055a0b", - "metadata": {}, - "source": [ - "### Using raw equations/unknowns (detailed)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3e525a11-cfbc-4269-9094-f7a86f37d7ef", - "metadata": {}, - "outputs": [], - "source": [ - "# design mode solver\n", - "sys.drivers.clear()\n", - "design = sys.add_driver(NonLinearSolver('design', max_iter=50, factor=0.8))\n", - "\n", - "# engine\n", - "tf.thrust = 120e3\n", - "tf.bpr = 5.\n", - "tf.pr_nozzle = 1.1\n", - "\n", - "# inlet\n", - "tf.inlet.aero.mach = 0.5\n", - "\n", - "# fan module\n", - "tf.fan_module.fan.aero.pcnr = 0.95\n", - "tf.fan_module.fan.aero.utip = 420.\n", - "\n", - "# booster\n", - "tf.fan_module.booster.aero.phi = 0.45\n", - "tf.fan_module.booster.aero.psi = 0.35\n", - "tf.fan_module.booster.aero.spec_flow = 180.\n", - "tf.fan_module.booster.aero.pcnr = 95.\n", - "\n", - "# lpt\n", - "tf.turbine.aero.Ncqdes = 100.\n", - "tf.turbine.aero.psi = 1.25\n", - "\n", - "# hpc\n", - "tf.core.compressor.aero.pr = 11.\n", - "tf.core.compressor.aero.utip = 420.\n", - "tf.core.compressor.aero.phi = 0.5\n", - "tf.core.compressor.aero.pcnr = 95.\n", - "\n", - "# hpt\n", - "tf.core.turbine.aero.psi = 1.2\n", - "tf.core.turbine.aero.Ncqdes = 100.\n", - "\n", - "# combustor\n", - "tf.core.combustor.aero.Tcomb = 1700.\n", - "\n", - "# engine\n", - "design.runner.add_unknown('tf.fuel_W')\n", - "design.runner.add_target('tf.thrust')\n", - "design.add_unknown('tf.fan_diameter')\n", - "\n", - "# inlet\n", - "design.runner.add_target('tf.inlet.aero.mach')\n", - "\n", - "# fan\n", - "design.add_unknown(\"tf.fan_module.fan.aero.xnd\", max_rel_step=0.5)\n", - "design.add_unknown('tf.fan_module.fan.aero.phiP', lower_bound=0.1, upper_bound=1.5)\n", - "\n", - "design.runner.add_target(\"tf.fan_module.fan.aero.pcnr\")\n", - "design.runner.add_target('tf.fan_module.fan.aero.utip')\n", - "design.runner.add_target('tf.bpr')\n", - "\n", - "# booster\n", - "design.add_unknown('tf.fan_module.geom.booster_radius_ratio')\n", - "design.add_unknown('tf.fan_module.booster.geom.blade_hub_to_tip_ratio', lower_bound=1e-5, upper_bound=1.)\n", - "design.add_unknown('tf.fan_module.booster.aero.phiP')\n", - "design.add_unknown(\"tf.fan_module.booster.aero.xnd\", max_rel_step=0.5)\n", - "\n", - "design.runner.add_target('tf.fan_module.booster.aero.phi')\n", - "design.runner.add_target('tf.fan_module.booster.aero.psi')\n", - "design.runner.add_target('tf.fan_module.booster.aero.spec_flow')\n", - "design.runner.add_target(\"tf.fan_module.booster.aero.pcnr\")\n", - "\n", - "# lpt\n", - "design.add_unknown('tf.geom.turbine_radius_ratio')\n", - "design.add_unknown(\"tf.turbine.geom.blade_height_ratio\", lower_bound=0., upper_bound=1.)\n", - "design.add_unknown('tf.turbine.aero.Ncdes')\n", - "\n", - "design.runner.add_target('tf.turbine.aero.psi')\n", - "design.runner.add_target('tf.turbine.aero.Ncqdes')\n", - "\n", - "# hpc\n", - "design.add_unknown('tf.geom.core_inlet_radius_ratio', max_rel_step=0.8)\n", - "design.add_unknown(\"tf.core.compressor.aero.xnd\", max_rel_step=0.5)\n", - "design.add_unknown(\"tf.core.compressor.aero.phiP\")\n", - "\n", - "design.runner.add_target(\"tf.core.compressor.aero.pcnr\")\n", - "design.runner.add_target(\"tf.core.compressor.aero.phi\")\n", - "design.runner.add_target(\"tf.core.compressor.aero.utip\")\n", - "design.runner.add_target(\"tf.core.compressor.aero.pr\")\n", - "\n", - "# combustor\n", - "design.runner.add_target(\"tf.core.combustor.aero.Tcomb\")\n", - "\n", - "# hpt\n", - "design.add_unknown('tf.geom.core_exit_radius_ratio', max_rel_step=0.8)\n", - "design.add_unknown(\"tf.core.turbine.geom.blade_height_ratio\", lower_bound=0., upper_bound=1.)\n", - "design.add_unknown(\"tf.core.turbine.aero.Ncdes\")\n", - "\n", - "design.runner.add_target(\"tf.core.turbine.aero.psi\")\n", - "design.runner.add_target(\"tf.core.turbine.aero.Ncqdes\")\n", - "\n", - "# nozzle\n", - "design.add_unknown('tf.geom.pri_nozzle_area_ratio', lower_bound=0.05)\n", - "design.add_unknown('tf.geom.sec_nozzle_area_ratio', upper_bound=1.)\n", - "\n", - "design.runner.add_target('tf.pr_nozzle')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b464d54b-90d6-4663-b4c3-bb3ff4e5f108", - "metadata": {}, - "outputs": [], - "source": [ - "# environment conditions\n", - "atm.altitude = 0.\n", - "atm.mach = 0.\n", - "atm.dtamb = 15.\n", - "\n", - "sys.run_drivers()\n", - "\n", - "print('mach =', atm.mach)\n", - "print('pamb =', atm.pamb, 'Pa')\n", - "print('fan diameter =', round(tf.geom.fan_diameter / 0.0254, 1), 'in')\n", - "print('W =', round(tf.fl_in.W), 'Kg/s')\n", - "print('thrust =', round(tf.thrust * 0.224809/1e3, 1), 'klbf')\n", - "print('N1 =', round(tf.N1), \"rpm\")\n", - "print('N2 =', round(tf.N2), \"rpm\")\n", - "print('bpr =', round(tf.bpr, 1))\n", - "print('opr =', round(tf.opr, 1))\n", - "print('T41 =', round(tf.core.turbine.fl_in.Tt), 'K')\n", - "print('sfc =', round(tf.sfc, 3), 'kg/(h*kN)')\n", - "print('psi fan =', round(tf.fan_module.fan.aero.psi, 2))\n", - "print('psi booster =', round(tf.fan_module.booster.aero.psi, 2))\n", - "print('psi hpc =', round(tf.core.compressor.aero.psi, 2))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2d53099f-6fd0-4953-90eb-2b3348a4ddfb", - "metadata": {}, - "outputs": [], - "source": [ - "tf.jupyter_view(options={\n", - " \"fan_module\": dict(opacity=0.7, face_color=\"#92B4EC\"),\n", - " \"fan_module.spinner\": dict(face_color=\"#E1E5EA\", opacity=1.),\n", - " \"fan_duct\": dict(opacity=0.3),\n", - " \"core_cowl\": dict(opacity=0.7),\n", - " \"nacelle\": dict(face_color=\"#E1E5EA\", opacity=0.6),\n", - " \"inlet\": dict(opacity=1.),\n", - " \"gas_generator\": dict(face_color=\"red\", opacity=0.9),\n", - " \"tcf\": dict(face_color=\"#92B4EC\", opacity=0.9),\n", - " \"turbine\": dict(face_color=\"#92B4EC\", opacity=0.9),\n", - " \"trf\": dict(opacity=0.6),\n", - "})" - ] - }, - { - "cell_type": "markdown", - "id": "71866bce", - "metadata": {}, - "source": [ - "### off-design computation after design\n", - "\n", - "Fuel consumption for a given altitude/mach/dtamb and thrust. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f83def4e-0b28-4415-a842-226806a4b604", - "metadata": {}, - "outputs": [], - "source": [ - "# off-design mode\n", - "sys.drivers.clear()\n", - "run = sys.add_driver(NonLinearSolver('run'))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8de8788d-9b5f-4b39-b377-2b025ad43672", - "metadata": {}, - "outputs": [], - "source": [ - "# environment conditions\n", - "atm.altitude = 10000.\n", - "atm.mach = 0.8\n", - "atm.dtamb = 0.\n", - "\n", - "# requirement\n", - "tf.thrust = 10e3 / 0.224809\n", - "# tf.N1 = 5000.\n", - "\n", - "run.runner.add_unknown('tf.fuel_W')\n", - "run.runner.add_target('tf.thrust')\n", - "# run.runner.add_target('tf.N1')\n", - "\n", - "sys.run_drivers()\n", - "\n", - "print('fuel flow =', round(tf.fuel_W, 1), 'kg/s')\n", - "print('N1 =', round(tf.N1), \"rpm\")\n", - "print('sfc ', round(tf.sfc, 3), 'kg/(h*kN)')" - ] - } - ], - "metadata": { - "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.10.8" - }, - "vscode": { - "interpreter": { - "hash": "0c1f2e9e0acff612fbff7be31f0ea913f7f20427d1d1231e188b099d6c58c52b" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/pyturbo/systems/nozzle/nozzle_aero.py b/pyturbo/systems/nozzle/nozzle_aero.py index 4f2cd24..a9b0136 100644 --- a/pyturbo/systems/nozzle/nozzle_aero.py +++ b/pyturbo/systems/nozzle/nozzle_aero.py @@ -1,6 +1,7 @@ # Copyright (C) 2022-2023, twiinIT # SPDX-License-Identifier: BSD-3-Clause +import numpy as np from cosapp.systems import System from pyturbo.ports import FluidPort @@ -66,17 +67,19 @@ def setup(self, FluidLaw=IdealDryAir): self.add_inward("pamb", 101325.0, unit="Pa", desc="ambient static pressure") # geom - self.add_inward("area_in", 10.0, unit="m**2", desc="inlet aero section") - self.add_inward("area_exit", 10.0, unit="m**2", desc="exit aero section") - self.add_inward("area", 1.0, unit="m**2", desc="choked/exit area") + self.add_inward("area_in", (0.25**2) * np.pi, unit="m**2", desc="inlet aero section") + self.add_inward("area_exit", (0.15**2) * np.pi, unit="m**2", desc="exit aero section") + self.add_inward("area", (0.15**2) * np.pi, unit="m**2", desc="choked/exit area") # outwards - self.add_outward("ps", 0.0, unit="pa", desc="static pressure at throat") - self.add_outward("mach", 0.0, unit="", desc="mach at throat") - self.add_outward("speed", 0.0, unit="m/s", desc="fluid flow speed at throat") + self.add_outward("speed", 1.0, unit="m/s", desc="exhaust gas speed") self.add_outward("thrust", unit="N") + self.add_outward("mach", 0.5, unit="", desc="mach at outlet") # off design + self.add_inward("mach_exit", 0.5) + self.add_unknown("mach_exit") + self.add_equation("mach == mach_exit") self.add_equation("fl_in.W == fl_out.W") # init @@ -87,17 +90,21 @@ def compute(self): self.fl_out.Pt = self.fl_in.Pt self.fl_out.Tt = self.fl_in.Tt - # assumes convergent nozzle (throat at exit) - self.mach = self.gas.mach_f_ptpstt(self.fl_in.Pt, self.pamb, self.fl_in.Tt, tol=1e-6) + # Outlet gas flow properties + ts_exit = self.gas.static_t(self.fl_out.Tt, self.mach_exit, tol=1e-6) - ts = self.gas.static_t(self.fl_in.Tt, self.mach, tol=1e-6) - self.ps = self.gas.static_p(self.fl_in.Pt, self.fl_in.Tt, self.mach, tol=1e-6) - rho = self.gas.density(self.ps, ts) - self.speed = self.gas.c(ts) * self.mach + ps_crit = self.gas.static_p( + self.fl_out.Pt, self.fl_out.Tt, 1, tol=1e-6 + ) # Static critical pressure is static presure when Mach = 1.0 - if self.mach > 1.0: - self.fl_out.W = self.gas.wqa_crit(self.fl_in.Pt, self.fl_in.Tt, tol=1e-6) * self.area - else: - self.fl_out.W = rho * self.speed * self.area + ps_exit = max(ps_crit, self.pamb) - self.thrust = self.fl_out.W * self.speed + (self.ps - self.pamb) * self.area + self.mach = self.gas.mach_f_ptpstt(self.fl_in.Pt, ps_exit, self.fl_in.Tt, tol=1e-6) + + self.speed = self.gas.c(ts_exit) * self.mach + + density_exit = self.gas.density(ps_exit, ts_exit) + + self.fl_out.W = density_exit * self.speed * self.area_exit + + self.thrust = self.fl_out.W * self.speed + self.area_exit * (ps_exit - self.pamb) diff --git a/tests/test_nozzle.py b/tests/test_nozzle.py index f0b44cc..3019622 100644 --- a/tests/test_nozzle.py +++ b/tests/test_nozzle.py @@ -44,7 +44,7 @@ def test_run_solver(self): sys = NozzleAero("noz") run = sys.add_driver(NonLinearSolver("run")) - run.add_unknown("area", max_rel_step=0.1) + run.add_unknown("area_exit", max_rel_step=0.1) sys.pamb = 1.01e5 sys.fl_in.Tt = 530.0 @@ -55,4 +55,4 @@ def test_run_solver(self): assert sys.speed == pytest.approx(308.3, 0.01) assert sys.mach == pytest.approx(0.7, 0.01) assert sys.thrust == pytest.approx(9250.0, 0.01) - assert sys.area == pytest.approx(0.133, 0.01) + assert sys.area_exit == pytest.approx(0.133, 0.01) diff --git a/tests/test_nozzle_aero.py b/tests/test_nozzle_aero.py new file mode 100644 index 0000000..1810768 --- /dev/null +++ b/tests/test_nozzle_aero.py @@ -0,0 +1,133 @@ +# Copyright (C) 2022-2023, twiinIT +# SPDX-License-Identifier: BSD-3-Clause + +import pytest +from cosapp.drivers import EulerExplicit, NonLinearSolver + +from pyturbo.systems.nozzle import Nozzle, NozzleAero + + +class TestNozzleAero: + """Define tests for the nozzle.""" + + def test_system_setup(self): + # default constructor + sys = NozzleAero("noz") + + data_input = ["fl_in"] + data_inwards = ["pamb", "area_in", "area_exit"] + data_output = ["fl_out"] + data_outwards = ["thrust", "speed"] + + for data in data_input: + assert data in sys.inputs + for data in data_output: + assert data in sys.outputs + for data in data_outwards: + assert data in sys.outwards + for data in data_inwards: + assert data in sys.inwards + + @pytest.mark.skip("not relevant") + def test_run_once(self): + # basic run + sys = Nozzle("noz") + + sys.fl_in.W = 400.0 + sys.pamb = 1e5 + sys.run_once() + + assert sys.thrust == pytest.approx(30093.0, 0.1) + + def test_run_solver(self): + # basic run + sys = NozzleAero("noz") + run = sys.add_driver(NonLinearSolver("run")) + + run.add_unknown("area_exit", max_rel_step=0.1) + + sys.pamb = 1.01e5 + sys.fl_in.Tt = 530.0 + sys.fl_in.Pt = 1.405e5 + sys.fl_in.W = 30.0 + sys.run_drivers() + + assert sys.speed == pytest.approx(308.3, 0.01) + assert sys.mach == pytest.approx(0.7, 0.01) + assert sys.thrust == pytest.approx(9250.0, 0.01) + assert sys.area_exit == pytest.approx(0.133, 0.01) + + def test_run_simulation(self): + # basic run + sys = NozzleAero("noz") + run = sys.add_driver(NonLinearSolver("run")) + + run.add_unknown("fl_in.W", max_rel_step=0.1) + + sys.pamb = 1.01e5 + sys.fl_in.W = 30.0 + sys.area_exit = 0.133 + sys.fl_in.Tt = 530.0 + sys.fl_in.Pt = 1.405e5 + sys.run_drivers() + + assert sys.speed == pytest.approx(308.3, 0.01) + assert sys.mach == pytest.approx(0.7, 0.01) + assert sys.thrust == pytest.approx(9250.0, 0.01) + assert sys.fl_in.W == pytest.approx(30.0, 0.01) + + def test_run_simulation_choked(self): + # basic run + sys = NozzleAero("noz") + run = sys.add_driver(NonLinearSolver("run")) + + run.add_unknown("fl_in.W", max_rel_step=0.1) + + sys.pamb = 1.01e2 + sys.fl_in.W = 30.0 + sys.area_exit = 0.133 + sys.fl_in.Tt = 530.0 + sys.fl_in.Pt = 1.405e5 + sys.run_drivers() + + sys2 = NozzleAero("noz2") + run2 = sys2.add_driver(NonLinearSolver("run2")) + + run2.add_unknown("fl_in.W", max_rel_step=0.1) + + sys2.area = 0.133 + sys2.fl_in.Tt = 530.0 + sys2.fl_in.Pt = 1.405e5 + sys2.run_drivers() + + assert sys.mach == pytest.approx(1.0, 0.01) + + def test_run_transient(self): + # basic run + sys = NozzleAero("noz") + time_driver = sys.add_driver(EulerExplicit("euler_explicit", dt=0.1, time_interval=(0, 10))) + run = time_driver.add_driver(NonLinearSolver("run")) + + run.add_unknown("fl_in.W", max_rel_step=0.1) + + time_driver.set_scenario(values={"pamb": "1.01e5 - 1e4 * time"}) + sys.area = 0.133 + sys.fl_in.Tt = 530.0 + sys.fl_in.Pt = 1.405e5 + sys.run_drivers() + + sys2 = NozzleAero("noz2") + time_driver2 = sys2.add_driver( + EulerExplicit("euler_explicit", dt=0.1, time_interval=(0, 10)) + ) + run2 = time_driver2.add_driver(NonLinearSolver("run2")) + + run2.add_unknown("fl_in.W", max_rel_step=0.1) + + time_driver2.set_scenario(values={"pamb": "1.01e5 - 1e4 * time"}) + sys2.area = 0.133 + sys2.fl_in.Tt = 530.0 + sys2.fl_in.Pt = 1.405e5 + sys2.run_drivers() + + assert sys.mach == pytest.approx(1.0, 0.01)