{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Tutorial 14 - Procedure\n", "Welcome to this tutorial outlining the Procedure for an optimization problem in RCAIDE. This guide will walk you through the code, explain its components, and highlight where modifications can be made to customize the simulation for different vehicle designs. This tutorial is part of the Optimize tutorial. \n", "\n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Header and Imports\n", "\n", "\n", "The **Imports** section imports the necessary libraries and functions for the tutorial. These include, but are not limited to:\n", "\n", "- **numpy** for numerical operations and arrays\n", "- **Units** and **Data** for unit conversions and data handling\n", "- **design_turbofan** for turbofan design\n", "\n", "---" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Matplotlib created a temporary cache directory at /var/folders/r4/4h9x29hj6f95kyhjsltxc6_00000gn/T/matplotlib-a33yrch_ because the default path (/Users/aidanmolloy/.matplotlib) is not a writable directory; it is highly recommended to set the MPLCONFIGDIR environment variable to a writable directory, in particular to speed up the import of Matplotlib and to better support multiprocessing.\n", "Fontconfig warning: ignoring UTF-8: not a valid region tag\n", "Matplotlib is building the font cache; this may take a moment.\n" ] } ], "source": [ "import numpy as np\n", "\n", "import RCAIDE\n", "from RCAIDE.Framework.Core import Units, Data\n", "from RCAIDE.Framework.Analyses.Process import Process \n", "from RCAIDE.Library.Methods.Powertrain.Propulsors.Turbofan_Propulsor import design_turbofan\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup\n", "\n", "The **`setup`** function creates the analysis procedure. It first creates a process container and assigns the update_aircraft and weight functions to it. It then creates a second process container and assigns the design_mission function to it. Finally, it assigns the post_process function to the analysis procedure. \n", "\n", "---" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def setup():\n", " \n", " # ------------------------------------------------------------------\n", " # Analysis Procedure\n", " # ------------------------------------------------------------------ \n", " \n", " # size the base config\n", " procedure = Process()\n", " procedure.update_aircraft = update_aircraft\n", " \n", " # find the weights\n", " procedure.weights = weight \n", " \n", " # performance studies\n", " procedure.missions = Process()\n", " procedure.missions.design_mission = design_mission\n", "\n", " # post process the results\n", " procedure.post_process = post_process\n", " \n", " return procedure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Find Target Range\n", "\n", "The **`find_target_range`** function calculates the cruise range of the mission by summing the distances of the climb and descent segments and subtracting this from the design range. It unpacks each of the segments from the mission and assigns each to its own variable. It then uses trigonometric functions to calculate the distance of each segment. Finally, it assigns the cruise range to the cruise segment. \n", "\n", "---" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "\n", "def find_target_range(nexus,mission):\n", " \n", " segments = mission.segments\n", " climb_1 = segments['climb_1']\n", " climb_2 = segments['climb_2']\n", " climb_3 = segments['climb_3']\n", " \n", " descent_1 = segments['descent_1']\n", " descent_2 = segments['descent_2']\n", " descent_3 = segments['descent_3']\n", "\n", " x_climb_1 = climb_1.altitude_end/np.tan(np.arcsin(climb_1.climb_rate/climb_1.air_speed))\n", " x_climb_2 = (climb_2.altitude_end-climb_1.altitude_end)/np.tan(np.arcsin(climb_2.climb_rate/climb_2.air_speed))\n", " x_climb_3 = (climb_3.altitude_end-climb_2.altitude_end)/np.tan(np.arcsin(climb_3.climb_rate/climb_3.air_speed)) \n", " x_descent_1 = (climb_3.altitude_end-descent_1.altitude_end)/np.tan(np.arcsin(descent_1.descent_rate/descent_1.air_speed))\n", " x_descent_2 = (descent_1.altitude_end-descent_2.altitude_end)/np.tan(np.arcsin(descent_2.descent_rate/descent_2.air_speed))\n", " x_descent_3 = (descent_2.altitude_end-descent_3.altitude_end)/np.tan(np.arcsin(descent_3.descent_rate/descent_3.air_speed))\n", " \n", " cruise_range = mission.design_range-(x_climb_1+x_climb_2+x_climb_3+x_descent_1+x_descent_2+x_descent_3)\n", " \n", " segments['cruise'].distance = cruise_range\n", " \n", " return nexus\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Design Mission\n", "\n", "Design mission takes the base mission and sets the design range to 1500 nmi. It then calls the find_target_range function to calculate the cruise range and evaluate the mission. \n", "\n", "---" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "def design_mission(nexus):\n", " \n", " mission = nexus.missions.base\n", " mission.design_range = 1500.*Units.nmi\n", " find_target_range(nexus,mission)\n", " results = nexus.results\n", " results.base = mission.evaluate()\n", " \n", " return nexus" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Update Aircraft\n", "\n", "The update aircraft function updates the aircraft and engines based on the new airspeed and altitude from the otpimization problem. \n", "\n", "It first unpacks the vehicle and new conditions which are used to calculate derived values including the differential pressure and mach number. These updated values are then used to redesign the enginesnd wing. \n", "\n", "Overall, this function serves as the interface between the optimization problem and the aircraft design which is being adjusted.\n", "\n", "---" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def update_aircraft(nexus):\n", " configs = nexus.vehicle_configurations\n", " base = configs.base\n", " \n", " # find conditions\n", " air_speed = nexus.missions.base.segments['cruise'].air_speed \n", " altitude = nexus.missions.base.segments['climb_3'].altitude_end\n", " atmosphere = RCAIDE.Framework.Analyses.Atmospheric.US_Standard_1976() \n", " freestream = atmosphere.compute_values(altitude)\n", " freestream0 = atmosphere.compute_values(6000.*Units.ft) #cabin altitude\n", " \n", " diff_pressure = np.max(freestream0.pressure-freestream.pressure,0)\n", " fuselage = base.fuselages['tube_fuselage']\n", " fuselage.differential_pressure = diff_pressure \n", " \n", " # now size engine\n", " mach_number = air_speed/freestream.speed_of_sound \n", " \n", " for config in configs:\n", " config.wings.horizontal_stabilizer.areas.reference = (26.0/92.0)*config.wings.main_wing.areas.reference\n", " \n", " for wing in config.wings:\n", " wing = RCAIDE.Library.Methods.Geometry.Planform.wing_planform(wing)\n", " wing.areas.exposed = 0.8 * wing.areas.wetted\n", " wing.areas.affected = 0.6 * wing.areas.reference\n", " \n", " # redesign turbofan \n", " for network in config.networks: \n", " for propulsor in network.propulsors: \n", " propulsor.design_mach_number = mach_number \n", " design_turbofan(propulsor) \n", "\n", " return nexus" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Weight\n", "\n", "This function recalculates the weight of the vehicle using the Weights_Transport analysis. The first line unpacks the vehicle from the optimization container, nexus, and then runs this new vehicle through the weight analysis. \n", "\n", "---" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def weight(nexus):\n", " \n", " vehicle = nexus.vehicle_configurations.base\n", "\n", " weight_analysis = RCAIDE.Framework.Analyses.Weights.Transport()\n", " weight_analysis.vehicle = vehicle \n", " weight = weight_analysis.evaluate()\n", " \n", " return nexus" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Post Process Results\n", "\n", "The **`post_process`** function takes the optimization problem and its results and calculates various parameters including x_zero_fuel_margin and base_mission_fuelburn which are appended to the summary section of nexus. Nexus is then returned.\n", "\n", "---" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "def post_process(nexus):\n", " \n", " # Unpack data\n", " vehicle = nexus.vehicle_configurations.base\n", " results = nexus.results\n", " summary = nexus.summary\n", " nexus.total_number_of_iterations +=1\n", " \n", " #throttle in design mission\n", " max_throttle = 0 \n", " for i in range(len(results.base.segments)): \n", " for network in results.base.segments[i].analyses.vehicle.networks: \n", " for j , propulsor in enumerate(network.propulsors):\n", " max_segment_throttle = np.max(results.base.segments[i].conditions.energy[propulsor.tag].throttle[:,0])\n", " if max_segment_throttle > max_throttle:\n", " max_throttle = max_segment_throttle\n", " \n", " summary.max_throttle = max_throttle\n", " \n", " # Fuel margin and base fuel calculations\n", " design_landing_weight = results.base.segments[-1].conditions.weights.total_mass[-1]\n", " design_takeoff_weight = vehicle.mass_properties.takeoff\n", " zero_fuel_weight = vehicle.mass_properties.operating_empty \n", " \n", " summary.max_zero_fuel_margin = (design_landing_weight - zero_fuel_weight)/zero_fuel_weight\n", " summary.base_mission_fuelburn = design_takeoff_weight - results.base.segments['descent_3'].conditions.weights.total_mass[-1]\n", " \n", " return nexus " ] } ], "metadata": { "kernelspec": { "display_name": ".rcaideenv", "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.11.6" } }, "nbformat": 4, "nbformat_minor": 2 }