Source code for aerosandbox.library.field_lengths

import aerosandbox as asb
import aerosandbox.numpy as np
import aerosandbox.tools.units as u
from typing import Dict


[docs]def field_length_analysis_torenbeek( design_mass_TOGW: float, thrust_at_liftoff: float, lift_over_drag_climb: float, CL_max: float, s_ref: float, n_engines: int, atmosphere: asb.Atmosphere = None, CD_zero_lift: float = 0.03, obstacle_height: float = 35 * u.foot, friction_coefficient: float = 0.02, V_obstacle_over_V_stall: float = 1.3, minimum_V_liftoff_over_V_stall: float = 1.2, V_approach_over_V_stall: float = 1.3, maximum_braking_deceleration_g: float = 0.37, inertia_time: float = 4.5, approach_angle_deg: float = 3, ) -> Dict[str, float]: """ Performs a field length analysis on an aircraft, returning a dictionary of field length parameters. Citations: * "Torenbeek": Egbert Torenbeek, "Synthesis of Subsonic Airplane Design", 1976. (Generally, section 5.4.5: Takeoff) Args: design_mass_TOGW: The takeoff gross weight of the entire aircraft [kg]. thrust_at_liftoff: The thrust of the aircraft at the moment of liftoff [N]. lift_over_drag_climb: The lift-to-drag ratio of the aircraft during the climb phase of takeoff [dimensionless]. CL_max: The maximum lift coefficient of the aircraft [dimensionless]. Assumes any lift-augmentation devices ( e.g., slats, flaps) are deployed. s_ref: The reference area of the aircraft [m^2]. atmosphere: The atmosphere object to use for the analysis. Defaults to sea level. n_engines: The number of engines on the aircraft. Used during balanced field length calculation, which involves a single-engine-failure assumption. CD_zero_lift: The zero-lift drag coefficient of the aircraft [dimensionless]. obstacle_height: The height of the obstacle clearance [m]. Note: * FAR 23 requires a 50 foot obstacle clearance height. * FAR 25 requires a 35 foot obstacle clearance height. friction_coefficient: The coefficient of friction between the wheels and the runway. * 0.02 is a good value for a dry concrete runway. * 0.045 is a good value for short grass. V_obstacle_over_V_stall: The ratio of the airspeed while flying over the obstacle to the stall airspeed. minimum_V_liftoff_over_V_stall: The minimum-allowable ratio of the liftoff airspeed to the stall airspeed. V_approach_over_V_stall: The ratio of the approach airspeed to the stall airspeed. maximum_braking_deceleration_g: The maximum deceleration of the aircraft during braking [G]. This is used when calculating the "brake" portion of the "accelerate-brake" balanced field length, as well as the braking during normal landing. * Standard brakes are around 0.37 G on dry concrete. * Advanced brakes with optimum brake pressure control, lift dumpers, and nosewheel braking can be as high as 0.5 G on dry concrete. inertia_time: The time it takes for the pilot and aircraft to collectively react to an engine failure during takeoff [seconds]. This is collectively the sum of: * The pilot's reaction time * The time it takes the other engines to spool down, in the event of a rejected takeoff and deceleration on the ground. Returns: A dictionary of field length parameters, including: * "takeoff_ground_roll_distance": The distance the aircraft will roll on the ground during a normal takeoff before liftoff [meters]. * "takeoff_airborne_distance": The distance the aircraft will travel in the air during a normal takeoff [ meters]. This is after liftoff, but before the aircraft has reached the obstacle clearance height. * "takeoff_total_distance": The total field length required during a normal takeoff [meters]. This includes both the ground roll itself, as well as the airborne distance before the obstacle clearance height is reached. * "balanced_field_length": The field length required for takeoff and obstacle clearance when one engine fails at "decision speed" [meters]. Decision speed is the speed during the ground roll at which, if an engine fails, the aircraft can either continue the takeoff or brake to a complete stop in the same total distance. * "landing_airborne_distance": The distance the aircraft will travel in the air during a normal landing before touchdown [meters]. Note that a normal landing involves passing the runway threshold at the specified obstacle clearance height. * "landing_ground_roll_distance": The distance the aircraft will roll on the ground after touchdown during a normal landing [meters]. * "landing_total_distance": The total field length required during a normal landing [meters]. This includes both the airborne distance beyond the threshold that is required for obstacle clearance, as well as the ground roll distance after touchdown. * "V_stall": The stall speed of the aircraft at its takeoff gross weight [m/s]. * "V_liftoff": The airspeed at the moment of liftoff during a normal takeoff [m/s]. * "V_obstacle": The airspeed when the aircraft reaches the obstacle clearance height during a normal takeoff [m/s]. * "V_approach": The airspeed when the aircraft reaches the runway threshold during a normal landing. * "V_touchdown": The airspeed when the aircraft touches down during a normal landing. * "flight_path_angle_climb": The flight path angle during a normal takeoff at the point when the airplane reaches the obstacle clearance height [radians]. * "flight_path_angle_climb_one_engine_out": The flight path angle during a critical-engine-out takeoff at the point when the airplane reaches the obstacle clearance height [radians]. If this number is negative, engine failure results in inability to climb. """ ### Set defaults if atmosphere is None: atmosphere = asb.Atmosphere(altitude=0) ### Constants g = 9.81 # m/s^2, gravitational acceleration ##### Normal takeoff analysis ##### ### Compute TWR and climb physics thrust_over_weight_takeoff = thrust_at_liftoff / (design_mass_TOGW * g) flight_path_angle_climb = ( thrust_over_weight_takeoff - 1 / lift_over_drag_climb ) ### V_stall is the stall speed of the airplane. V_stall = np.sqrt( 2 * design_mass_TOGW * g / (atmosphere.density() * s_ref * CL_max) ) ### V_obstacle is the airspeed at the obstacle clearance height V_obstacle = V_obstacle_over_V_stall * V_stall ### V_liftoff is the airspeed at the moment of liftoff V_liftoff = V_obstacle * np.softmax( (1 + flight_path_angle_climb * 2 ** 0.5) ** -0.5, # From Torenbeek minimum_V_liftoff_over_V_stall / V_obstacle_over_V_stall, hardness=1 / 0.01 ) takeoff_effective_friction_coefficient = ( # From Torenbeek, Eq. 5-76; an approximation friction_coefficient + 0.72 * (CD_zero_lift / CL_max) ) # From Torenbeek, Eq. 5-74 takeoff_acceleration_g = thrust_over_weight_takeoff - takeoff_effective_friction_coefficient takeoff_ground_roll_distance = V_liftoff ** 2 / ( 2 * g * takeoff_acceleration_g ) ### Compute the airborne distance required to clear the obstacle # From Torenbeek. Assumes an air maneuver after liftoff with CL=CL_liftoff and constant (thrust - drag). takeoff_airborne_distance = ( ( V_liftoff ** 2 / (g * 2 ** 0.5) ) + ( obstacle_height / flight_path_angle_climb ) ) ### Compute the total distance required for normal takeoff, including obstacle clearance takeoff_total_distance = takeoff_ground_roll_distance + takeoff_airborne_distance ##### Balanced field length analysis ##### if n_engines == 1: # If there is only one engine, the worst time *during the ground roll* for the engine to fail is right at liftoff. balanced_field_length = takeoff_ground_roll_distance + ( V_liftoff ** 2 / (2 * g * maximum_braking_deceleration_g) ) flight_path_angle_climb_one_engine_out = -1 / lift_over_drag_climb else: ### The flight path angle during a climb with one engine inoperative. flight_path_angle_climb_one_engine_out = ( thrust_over_weight_takeoff * (n_engines - 1) / n_engines - 1 / lift_over_drag_climb ) if n_engines == 2: minimum_allowable_flight_path_angle = 0.024 elif n_engines == 3: minimum_allowable_flight_path_angle = 0.027 elif n_engines >= 4: minimum_allowable_flight_path_angle = 0.030 else: raise ValueError("`n_engines` must be an integer >= 1") # This is an approximation made by Torenbeek (Eq. 5-90, see citation in docstring) gamma_bar_takeoff = 0.06 + (flight_path_angle_climb_one_engine_out - minimum_allowable_flight_path_angle) air_density_ratio = atmosphere.density() / asb.Atmosphere(altitude=0).density() balanced_field_length = ( # From Torenbeek, Eq. 5-89, modified to have inertia distance scale with V_liftoff (V_liftoff ** 2 / (2 * g * (1 + gamma_bar_takeoff / maximum_braking_deceleration_g))) * (1 / takeoff_acceleration_g + 1 / maximum_braking_deceleration_g) * (1 + (2 * g * obstacle_height) / V_liftoff ** 2) + inertia_time * V_liftoff ) # Do a softmax to make sure that the BFL is never shorter than the normal takeoff distance. balanced_field_length = np.softmax( balanced_field_length, takeoff_total_distance, softness=takeoff_total_distance / 100, ) ##### Landing analysis ##### # The factor of 2 is an approximation factor from Torenbeek, Section 5.4.6 gamma_bar_landing = 2 * np.tand(approach_angle_deg) ### Compute the landing distance V_approach = V_approach_over_V_stall * V_stall V_touchdown = V_liftoff landing_airborne_distance = ( # From Torenbeek (V_approach ** 2 - V_touchdown ** 2) / (2 * g) + obstacle_height ) / gamma_bar_landing landing_ground_roll_distance = ( inertia_time * V_touchdown + V_touchdown ** 2 / (2 * g * maximum_braking_deceleration_g) ) landing_total_distance = ( landing_airborne_distance + landing_ground_roll_distance ) return { "takeoff_ground_roll_distance" : takeoff_ground_roll_distance, "takeoff_airborne_distance" : takeoff_airborne_distance, "takeoff_total_distance" : takeoff_total_distance, "balanced_field_length" : balanced_field_length, "landing_airborne_distance" : landing_airborne_distance, "landing_ground_roll_distance" : landing_ground_roll_distance, "landing_total_distance" : landing_total_distance, "V_stall" : V_stall, "V_liftoff" : V_liftoff, "V_obstacle" : V_obstacle, "V_approach" : V_approach, "V_touchdown" : V_touchdown, "flight_path_angle_climb" : flight_path_angle_climb, "flight_path_angle_climb_one_engine_out": flight_path_angle_climb_one_engine_out, }
[docs]def field_length_analysis( design_mass_TOGW: float, thrust_at_liftoff: float, lift_over_drag_climb: float, CL_max: float, s_ref: float, n_engines: int, V_engine_failure_balanced_field_length: float, atmosphere: asb.Atmosphere = None, CD_zero_lift: float = 0.03, obstacle_height: float = 35 * u.foot, friction_coefficient: float = 0.02, minimum_V_liftoff_over_V_stall: float = 1.2, maximum_braking_deceleration_g: float = 0.37, inertia_time: float = 2, approach_angle_deg: float = 3, ) -> Dict[str, float]: ### Set defaults if atmosphere is None: atmosphere = asb.Atmosphere(altitude=0) ### Constants g = 9.81 # m/s^2, gravitational acceleration ### Compute TWR and climb physics thrust_over_weight_takeoff = thrust_at_liftoff / (design_mass_TOGW * g) ##### Compute various accelerations acceleration_friction_and_drag = -g * ( # Based on Torenbeek, Eq. 5-76; an approximation friction_coefficient + 0.72 * (CD_zero_lift / CL_max) ) acceleration_braking = -g * maximum_braking_deceleration_g acceleration_engines = thrust_at_liftoff / design_mass_TOGW acceleration_takeoff = acceleration_engines + acceleration_friction_and_drag acceleration_coasting = acceleration_friction_and_drag acceleration_landing = acceleration_braking ##### Normal takeoff analysis ##### ### V_stall is the stall speed of the airplane. V_stall = np.sqrt( 2 * design_mass_TOGW * g / (atmosphere.density() * s_ref * CL_max) ) ### V_liftoff is the airspeed at the moment of liftoff V_liftoff = minimum_V_liftoff_over_V_stall * V_stall takeoff_ground_roll_distance = V_liftoff ** 2 / (2 * acceleration_takeoff) ### Compute the airborne distance required to clear the obstacle flight_path_angle_climb = ( # radians, small angle approximation thrust_over_weight_takeoff - 1 / lift_over_drag_climb ) flight_path_angle_climb = np.softmax(flight_path_angle_climb, 0, softness=0.001) takeoff_airborne_distance = obstacle_height / flight_path_angle_climb ### Compute the total distance required for normal takeoff, including obstacle clearance takeoff_total_distance = takeoff_ground_roll_distance + takeoff_airborne_distance ##### Normal landing analysis ##### landing_airborne_distance = obstacle_height / np.tand(approach_angle_deg) V_touchdown = V_liftoff landing_ground_roll_distance = ( inertia_time * V_touchdown + V_touchdown ** 2 / (2 * -acceleration_landing) ) landing_total_distance = landing_airborne_distance + landing_ground_roll_distance ##### Balanced field length analysis ##### if n_engines == 1: # If there is only one engine, the worst time *during the ground roll* for the engine to fail is right at liftoff. balanced_field_length = takeoff_ground_roll_distance + ( V_liftoff ** 2 / (2 * -acceleration_landing) ) balanced_field_length_accept = balanced_field_length balanced_field_length_reject = balanced_field_length flight_path_angle_climb_one_engine_out = -1 / lift_over_drag_climb else: acceleration_takeoff_one_engine_out = acceleration_engines * ( n_engines - 1) / n_engines + acceleration_friction_and_drag ### The flight path angle during a climb with one engine inoperative. flight_path_angle_climb_one_engine_out = ( thrust_over_weight_takeoff * (n_engines - 1) / n_engines - 1 / lift_over_drag_climb ) flight_path_angle_climb_one_engine_out = np.softmax(flight_path_angle_climb_one_engine_out, 0, softness=0.001) balanced_field_length_accept = ( (V_engine_failure_balanced_field_length ** 2 / (2 * acceleration_takeoff)) + # Both engines working ((V_liftoff ** 2 - V_engine_failure_balanced_field_length ** 2) / ( 2 * acceleration_takeoff_one_engine_out)) + (obstacle_height / flight_path_angle_climb_one_engine_out) ) balanced_field_length_reject = ( (V_engine_failure_balanced_field_length ** 2 / (2 * acceleration_takeoff)) + # Both engines working (inertia_time * V_engine_failure_balanced_field_length) + # Reaction time for pilot / engines (V_engine_failure_balanced_field_length ** 2 / (2 * -acceleration_landing)) # Braking time ) return { "takeoff_ground_roll_distance" : takeoff_ground_roll_distance, "takeoff_airborne_distance" : takeoff_airborne_distance, "takeoff_total_distance" : takeoff_total_distance, "balanced_field_length_accept" : balanced_field_length_accept, "balanced_field_length_reject" : balanced_field_length_reject, "landing_airborne_distance" : landing_airborne_distance, "landing_ground_roll_distance" : landing_ground_roll_distance, "landing_total_distance" : landing_total_distance, "V_stall" : V_stall, "V_liftoff" : V_liftoff, "V_touchdown" : V_touchdown, "flight_path_angle_climb" : flight_path_angle_climb, "flight_path_angle_climb_one_engine_out": flight_path_angle_climb_one_engine_out, }
if __name__ == '__main__': from aerosandbox.tools import units as u
[docs] results = field_length_analysis( design_mass_TOGW=19000 * u.lbm, thrust_at_liftoff=19000 * u.lbf * 0.3, lift_over_drag_climb=20, CL_max=1.9, s_ref=24, n_engines=2, V_engine_failure_balanced_field_length=70, atmosphere=asb.Atmosphere(altitude=0), )
results_torenbeek= field_length_analysis_torenbeek( design_mass_TOGW=19000 * u.lbm, thrust_at_liftoff=19000 * u.lbf * 0.3, lift_over_drag_climb=20, CL_max=1.9, s_ref=24, n_engines=2, atmosphere=asb.Atmosphere(altitude=0), ) from pprint import pprint pprint(results)