Source code for aerosandbox.library.weights.torenbeek_weights

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


# From Torenbeek: "Synthesis of Subsonic Airplane Design", 1976, Delft University Press
# Chapter 8: "Airplane Weight and Balance"

[docs]def mass_wing_simple( wing: asb.Wing, design_mass_TOGW: float, ultimate_load_factor: float, suspended_mass: float, main_gear_mounted_to_wing: bool = True, ) -> float: """ Computes the mass of a wing of an aircraft, according to Torenbeek's "Synthesis of Subsonic Airplane Design". This is the simple version of the wing weight model, which is found in: Section 8.4: Weight Prediction Data and Methods 8.4.1: Airframe Structure Eq. 8-12 A more detailed version of the wing weight model is available in the `mass_wing()` function in this same module. Args: wing: The wing object. Should be an AeroSandbox Wing object. design_mass_TOGW: The design takeoff gross weight of the entire aircraft [kg]. ultimate_load_factor: The ultimate load factor of the aircraft. 1.5x the limit load factor. suspended_mass: The mass of the aircraft that is suspended from the wing [kg]. main_gear_mounted_to_wing: Whether the main gear is mounted to the wing structure. Returns: The total mass of the wing [kg]. """ k_w = np.blend( (design_mass_TOGW - 5670) / 2000, 6.67e-3, 4.90e-3 ) span = wing.span() / np.cosd(wing.mean_sweep_angle(x_nondim=0.5)) wing_root_thickness = wing.xsecs[0].airfoil.max_thickness() * wing.xsecs[0].chord return suspended_mass * ( k_w * span ** 0.75 * (1 + (1.905 / span) ** 0.5) * ultimate_load_factor ** 0.55 * ((span / wing_root_thickness) / (suspended_mass / wing.area())) ** 0.30 * (1 if main_gear_mounted_to_wing else 0.95) )
[docs]def mass_wing_high_lift_devices( wing: asb.Wing, max_airspeed_for_flaps: float, flap_deflection_angle: float = 30, k_f1: float = 1.0, k_f2: float = 1.0 ) -> float: """ The function mass_high_lift() is designed to estimate the weight of the high-lift devices on an airplane wing. It uses Torenbeek's method, which is based on multiple factors like wing design and flap deflection. Args: wing, an instance of AeroSandbox's Wing class, max_airspeed_for_flaps, the maximum airspeed at which the flaps can be deployed [m/s] flap_deflection_angle, the angle to which the flaps can be deflected [deg]. Default value is 30 degrees. k_f1, configuration factor 1, with values: = 1.0 for single-slotted; double-slotted, fixed hinge = 1.15 for double: slotted, 4-bar movement; single-slotted Fowler = 1.3 for double-slotted Fowler = 1.45 for triple-slotted Fowler k_f2, configuration factor 2, with values: = 1.0 for slotted flaps with fixed vanes = 1.25 for double-slotted flaps with "variable geometry", i.e., extending flaps with separately moving vanes or auxiliary flaps Returns: Mass of the wing's high-lift system only [kg] """ # S_flaps represents the total area of the control surfaces (flaps) on the wing. S_flaps = wing.control_surface_area() # Wing span span = wing.span() # Sweep at 50% chord sweep_half_chord = wing.mean_sweep_angle(x_nondim=0.5) cos_sweep_half_chord = np.cosd(sweep_half_chord) # span_structural is the "structural" wing span, which takes into account the wing's sweep angle. span_structural = span / cos_sweep_half_chord # Airfoil thickness over chord ratio at root root_t_over_c = wing.xsecs[0].airfoil.max_thickness() # Torenbeek Eq. C-10 k_f = k_f1 * k_f2 mass_trailing_edge_flaps = S_flaps * ( 2.706 * k_f * (S_flaps * span_structural) ** (3 / 16) * ( (max_airspeed_for_flaps / 100) ** 2 * np.sind(flap_deflection_angle) * np.cosd(wing.mean_sweep_angle(x_nondim=1)) / root_t_over_c ) ** (3 / 4) ) mass_leading_edge_devices = 0 mass_high_lift_devices = mass_trailing_edge_flaps + mass_leading_edge_devices return mass_high_lift_devices
[docs]def mass_wing_basic_structure( wing: asb.Wing, design_mass_TOGW: float, ultimate_load_factor: float, suspended_mass: float, never_exceed_airspeed: float, main_gear_mounted_to_wing: bool = True, strut_y_location: float = None, k_e: float = 0.95, return_dict: bool = False, ) -> Union[float, Dict[str, float]]: """ Computes the mass of the basic structure of the wing of an aircraft, according to Torenbeek's "Synthesis of Subsonic Airplane Design", 1976, Appendix C: "Prediction of Wing Structural Weight". This is the basic wing structure without movables like spoilers, high-lift devices, etc. Likely more accurate than the Raymer wing weight models. Args: wing: The wing object. design_mass_TOGW: The design takeoff gross weight of the entire aircraft [kg]. ultimate_load_factor: The ultimate load factor of the aircraft [-]. 1.5x the limit load factor. suspended_mass: The mass of the aircraft that is suspended from the wing [kg]. It should exclude any wing attachments that are not part of the wing structure. never_exceed_airspeed: The never-exceed airspeed of the aircraft [m/s]. Used for flutter calculations. main_gear_mounted_to_wing: Whether the main gear is mounted to the wing structure. Boolean. strut_y_location: The spanwise-location of the strut (if any), as measured from the wing root [meters]. If None, it is assumed that there is no strut (i.e., the wing is a cantilever beam). k_e: represents weight knockdowns due to bending moment relief from engines mounted in front of elastic axis. see Torenbeek unlabeled equations, between C-3 and C-4. k_e = 1.0 if engines are not wing mounted, k_e = 0.95 (default) two wing mounted engines in front of the elastic axis and k_e = 0.90 four wing-mounted engines in front of the elastic axis return_dict: Whether to return a dictionary of all the intermediate values, or just the final mass. Defaults to False, which returns just the final mass [kg]. Returns: If return_dict is False (default), returns a single value: the mass of the basic wing [kg]. If return_dict is True, returns a dictionary of all the intermediate values. """ # Wing span span = wing.span() # Sweep at 50% chord sweep_half_chord = wing.mean_sweep_angle(x_nondim=0.5) cos_sweep_half_chord = np.cosd(sweep_half_chord) # Structural wing span span_structural = span / cos_sweep_half_chord # Airfoil thickness over chord ratio at root root_t_over_c = wing.xsecs[0].airfoil.max_thickness() # Torenbeek Eq. C-2 # `k_no` represents penalties due to skin joints, non-tapered skin, minimum gauge, etc. k_no = 1 + (1.905 / span_structural) ** 0.5 # Torenbeek Eq. C-3 # `k_lambda` represents penalties due to taper ratio k_lambda = (1 + wing.taper_ratio()) ** 0.4 # `k_uc` represents weight knockdowns due to undercarriage. k_uc = 1 if main_gear_mounted_to_wing else 0.95 # Torenbeek Eq. C-4 # `k_st` represents weight excrescence due to structural stiffness against flutter. k_st = ( 1 + 9.06e-4 * ( (span * np.cosd(wing.mean_sweep_angle(x_nondim=0))) ** 3 / design_mass_TOGW ) * ( never_exceed_airspeed / 100 / root_t_over_c ) ** 2 * cos_sweep_half_chord ) # Torenbeek Eq. C-5 # `k_b` represents weight knockdowns due to bending moment relief from strut location. if strut_y_location is None: k_b = 1 else: k_b = 1 - (strut_y_location / (wing.span() / 2)) ** 2 ### Use all the above to compute the basic wing structural mass mass_wing_basic = ( 4.58e-3 * k_no * k_lambda * k_e * k_uc * k_st * ( k_b * ultimate_load_factor * (0.8 * suspended_mass + 0.2 * design_mass_TOGW) ) ** 0.55 * span ** 1.675 * root_t_over_c ** -0.45 * cos_sweep_half_chord ** -1.325 ) if return_dict: return locals() else: return mass_wing_basic
[docs]def mass_wing_spoilers_and_speedbrakes( wing: asb.Wing, mass_basic_wing: float ) -> float: """ The function mass_spoilers_and_speedbrakes() estimates the weight of the spoilers and speedbrakes according to Torenbeek's "Synthesis of Subsonic Airplane Design", 1976, Appendix C: "Prediction of Wing Structural Weight". N.B. the weight is coming out unrealistic and approx. 20-30% of the weight of the wing. This needs a correction. It uses normally the 12.2 kg/m^2 wing area. Args: wing: an instance of AeroSandbox's Wing class. mass_basic_wing: The basic weight of the wing (without spoilers, speedbrakes, flaps, slats) [kg] Returns: The mass of the spoilers and speed brakes only [kg] N.B. the weight estimation using the 12.2 kg/m^2 figure comes out too high if using the wing as a referenced area. Reduced to 1.5% of the basic wing mass. """ # mass_spoilers_and_speedbrakes = np.softmax( # 12.2 * wing.area(), # 0.015 * mass_basic_wing # ) mass_spoilers_and_speedbrakes = 0.015 * mass_basic_wing return mass_spoilers_and_speedbrakes
[docs]def mass_wing( wing: asb.Wing, design_mass_TOGW: float, ultimate_load_factor: float, suspended_mass: float, never_exceed_airspeed: float, max_airspeed_for_flaps: float, main_gear_mounted_to_wing: bool = True, flap_deflection_angle: float = 30, strut_y_location: float = None, return_dict: bool = False, ) -> Union[float, Dict[str, float]]: """ Computes the mass of a wing of an aircraft, according to Torenbeek's "Synthesis of Subsonic Airplane Design", 1976, Appendix C: "Prediction of Wing Structural Weight". Likely more accurate than the Raymer wing weight models. Args: wing: The wing object. design_mass_TOGW: The design takeoff gross weight of the entire aircraft [kg]. ultimate_load_factor: The ultimate load factor of the aircraft. 1.5x the limit load factor. suspended_mass: The mass of the aircraft that is suspended from the wing [kg]. never_exceed_airspeed: The never-exceed airspeed of the aircraft [m/s]. Used for flutter calculations. max_airspeed_for_flaps: The maximum airspeed at which the flaps are allowed to be deployed [m/s]. In the absence of other information, 1.8x stall speed is a good guess. main_gear_mounted_to_wing: Whether the main gear is mounted to the wing structure. flap_deflection_angle: The maximum deflection angle of the flaps [deg]. strut_y_location: The y-location of the strut (if any), relative to the wing's leading edge [m]. If None, it is assumed that there is no strut (i.e., the wing is a cantilever beam). return_dict: Whether to return a dictionary of all the intermediate values, or just the final mass. Defaults to False, which returns just the final mass. Returns: If return_dict is False (default), returns a single value: the total mass of the wing [kg]. If return_dict is True, returns a dictionary of all the intermediate values. """ # High-lift mass estimation mass_high_lift_devices = mass_wing_high_lift_devices( wing=wing, max_airspeed_for_flaps=max_airspeed_for_flaps, flap_deflection_angle=flap_deflection_angle, ) # Basic wing structure mass estimation mass_basic_wing = mass_wing_basic_structure( wing=wing, design_mass_TOGW=design_mass_TOGW, ultimate_load_factor=ultimate_load_factor, suspended_mass=suspended_mass, never_exceed_airspeed=never_exceed_airspeed, main_gear_mounted_to_wing=main_gear_mounted_to_wing, strut_y_location=strut_y_location, ) # spoilers and speedbrake mass estimation mass_spoilers_speedbrakes = mass_wing_spoilers_and_speedbrakes( wing=wing, mass_basic_wing=mass_basic_wing ) mass_wing_total = ( mass_basic_wing + 1.2 * (mass_high_lift_devices + mass_spoilers_speedbrakes) ) if return_dict: return locals() else: return mass_wing_total
# def mass_hstab( # hstab: asb.Wing, # design_mass_TOGW: float, # ultimate_load_factor: float, # suspended_mass: float, # main_gear_mounted_to_wing: bool = True, # ) -> float: # # k_wt = 0.64
[docs]def mass_fuselage_simple( fuselage: asb.Fuselage, never_exceed_airspeed: float, wing_to_tail_distance: float, ): """ Computes the mass of the fuselage, using Torenbeek's simple version of the calculation. Source: Torenbeek: "Synthesis of Subsonic Airplane Design", 1976 Section 8.4: Weight Prediction Data and Methods 8.4.1: Airframe Structure Eq. 8-16 Args: fuselage: The fuselage object. Should be an AeroSandbox Fuselage object. never_exceed_airspeed: The never-exceed airspeed of the aircraft, in m/s. wing_to_tail_distance: The distance from the quarter-chord of the wing to the quarter-chord of the tail, in meters. Returns: The mass of the fuselage, in kg. """ widths = [ xsec.width for xsec in fuselage.xsecs ] max_width = np.softmax( *widths, softness=np.mean(np.array(widths)) * 0.01 ) heights = [ xsec.height for xsec in fuselage.xsecs ] max_height = np.softmax( *heights, softness=np.mean(np.array(heights)) * 0.01 ) return ( 0.23 * ( never_exceed_airspeed * wing_to_tail_distance / (max_width + max_height) ) ** 0.5 * fuselage.area_wetted() ** 1.2 )
[docs]def mass_fuselage( fuselage: asb.Fuselage, design_mass_TOGW: float, ultimate_load_factor: float, never_exceed_airspeed: float, wing_to_tail_distance: float, ): # TODO Torenbeek Appendix D (PDF page 477) # Stage 1: Calculate the weight of the fuselage shell, which carries the primary loads and contributes # approximately 1/3 to 1/2 of the fuselage weight ("gross shell weight"). # Torenbeek Eq. D-3 fuselage.fineness_ratio() fuselage_quasi_slenderness_ratio = fuselage.fineness_ratio(assumed_shape="sears_haack") k_lambda = np.softmin( 0.56 * fuselage.fineness_ratio(assumed_shape="sears_haack") ) W_sk = 0.05428 * k_lambda * S_g ** 1.07 * never_exceed_airspeed ** 0.743 W_g = W_sk + W_str + W_fr
[docs]def mass_propeller( propeller_diameter: float, propeller_power: float, n_blades: int, ) -> float: """ Computes the mass of a propeller. From Torenbeek: "Synthesis of Subsonic Airplane Design", 1976, Delft University Press. Table 8-9 (pg. 286, PDF page 306) Args: propeller_diameter: Propeller diameter, in meters. propeller_power: Propeller power, in watts. n_blades: Number of propeller blades. Returns: Propeller mass, in kilograms. """ return ( 0.108 * n_blades * ( (propeller_diameter / u.foot) * (propeller_power / u.horsepower) ) ** 0.78174 ) * u.lbm