Source code for RCAIDE.Library.Methods.Performance.compute_load_and_trim_diagram

# RCAIDE/Methods/Performance/compute_load_and_trim_diagram.py
# 
# 
# Created:  Dec 2024, M. Clarke

# ----------------------------------------------------------------------------------------------------------------------
#  IMPORT
# ----------------------------------------------------------------------------------------------------------------------

# RCAIDE imports 
import RCAIDE
from RCAIDE.Framework.Core import  Data,  Units 
from RCAIDE.Library.Methods.Mass_Properties.estimate_maximum_landing_weight import estimate_maximum_landing_weight
from RCAIDE.Library.Methods.Mass_Properties.Center_of_Gravity  import compute_vehicle_center_of_gravity 
from RCAIDE.Library.Mission.Common.Pre_Process import  geometry, mass_properties
import pandas as pd

# Pacakge imports 
import numpy as np
from copy import  deepcopy

#------------------------------------------------------------------------------
# compute_load_and_trim_diagram
#------------------------------------------------------------------------------  
[docs] def compute_load_and_trim_diagram(mission = None, cruise_segment_tag = "cruise",discretization = 5): """ Computes the loading dragram of an aircraft Parameters -------- Notes ----- # total number of sumulations = 2 * discretization ^ N # where 2: is for the loading and unloading cases # and N is the number of object types i.e cabin,cargo and fuel """ #------------------------------------------------------------------------ # Remove Takeoff mass #------------------------------------------------------------------------ for segment in mission.segments: segment.analyses.vehicle.mass_properties.takeoff = None segment.analyses.geometry.settings.compute_fuel_volume = True segment.analyses.weights.print_weight_analysis_report = True segment.analyses.weights.settings.run_center_of_gravity_analysis = True segment.analyses.weights.settings.overwrite_center_of_gravity = True segment.analyses.weights.settings.run_moments_of_inertia_analysis = True segment.analyses.stability.print_stability_analysis_report = True segment.analyses.stability.settings.compute_neutral_point = True #------------------------------------------------------------------------ # Check Input Args #------------------------------------------------------------------------ vehicle = mission.segments[cruise_segment_tag].analyses.vehicle cabin_class_check = False for fuselage in vehicle.fuselages: for cabin in fuselage.cabins: for _ in cabin.classes: cabin_class_check = True for wing in vehicle.wings: if isinstance(wing, RCAIDE.Library.Components.Wings.Blended_Wing_Body): for cabin in wing.cabins: for _ in cabin.classes: cabin_class_check = True if cabin_class_check == False: raise AttributeError('At least one cabin class must be defined to create Aircraft mass-C.G. envelope ') #------------------------------------------------------------------------ # Run Baseline mission #------------------------------------------------------------------------ results = mission.evaluate() # compute mass properties of aircraft to get weight distribution vehicle_0 = mission.segments[cruise_segment_tag].analyses.vehicle x_cg_0 = mission.segments[cruise_segment_tag].analyses.vehicle.mass_properties.center_of_gravity weight_breakdown = mission.segments[cruise_segment_tag].analyses.vehicle.mass_properties.weight_breakdown neutral_point_0 = mission.segments[cruise_segment_tag].analyses.vehicle.neutral_point W_PAX = weight_breakdown.payload.passengers PAX = vehicle_0.number_of_passengers W_PAX_per_pax = W_PAX / PAX MTOW = vehicle_0.mass_properties.max_takeoff MZFW = vehicle_0.mass_properties.max_zero_fuel FUEL = vehicle_0.mass_properties.max_fuel PLD = vehicle_0.mass_properties.max_payload vehicle_0.mass_properties.payload = PLD PLD_per_pax = (weight_breakdown.payload.passengers + weight_breakdown.payload.baggage) / PAX OEW = vehicle_0.mass_properties.operating_empty if mission.segments[cruise_segment_tag].analyses.weights.propulsion_architecture == 'Cryogenic': MLW = (vehicle_0.mass_properties.max_takeoff - 0.65 * vehicle_0.mass_properties.max_fuel) else: MLW = estimate_maximum_landing_weight(MTOW) W_CARGO = 0 m_c = [] for cargo_bay in vehicle_0.cargo_bays: W_CARGO += cargo_bay.mass_properties.mass m_c.append(cargo_bay.mass_properties.mass) cargo_bay_ratio = np.array(m_c) / sum(np.array(m_c)) W_CARGO += PLD-W_PAX m_f = [] for network in vehicle_0.networks: for fuel_line in network.fuel_lines: for fuel_tank in fuel_line.fuel_tanks: m_f.append(fuel_tank.fuel.mass_properties.mass) fuel_tank_ratio = np.array(m_f) / sum(np.array(m_f)) #------------------------------------------------------------------------ # Compute Loading Points #------------------------------------------------------------------------ # determine number is weight simulations # total number of sumulations = 2 * discretization ^ N # where 2: is for the loading and unloading cases # and N is the number of object types i.e cabin,cargo and fuel total_sims = 2 * discretization **3 # define discretization percent_fuel = np.linspace(0.0, 1,discretization) percent_pax = np.linspace(0.0, 1,discretization) percent_cargo = np.linspace(0.0, 1,discretization) percent_weight = np.linspace(0.0, 1,discretization) percent_cg_shift = np.linspace(0.8,1.2,discretization) filling_order = ['ascending','descending'] # create empty data structures RES = Data() RES.loading_results = Data() RES.trim_results = Data() RES.discretization = discretization RES.loading_results.mass = np.zeros((2,discretization,discretization,discretization)) RES.loading_results.CG_location = np.zeros((2,discretization,discretization,discretization)) RES.loading_results.LEMAC_location = np.zeros((2,discretization,discretization,discretization)) RES.loading_results.CG_percent_of_LEMAC_location= np.zeros((2,discretization,discretization,discretization)) RES.loading_results.static_margin = np.zeros((2,discretization,discretization,discretization)) RES.percent_cargo = np.zeros((2,discretization,discretization,discretization)) RES.percent_pax = np.zeros((2,discretization,discretization,discretization)) RES.percent_cargo = np.zeros((2,discretization,discretization,discretization)) RES.trim_results.neutral_point = np.zeros((discretization,discretization)) RES.trim_results.static_margin = np.zeros((discretization,discretization)) RES.trim_results.mass = np.zeros((discretization,discretization)) RES.trim_results.LEMAC_location = np.zeros((discretization,discretization)) RES.trim_results.CG_percent_of_LEMAC_location = np.zeros((discretization,discretization)) RES.MTOW = MTOW RES.OEW = OEW RES.MLW = MLW #============================================================================================ # Load Diagram Data #============================================================================================ counter = 0 for f_o in range(len(filling_order)): for p_i in range(len(percent_pax)): for c_i in range(len(percent_cargo)): for f_i in range(len(percent_fuel)): #------------------------------------------------------------------------ # Reset all weights to 0 #------------------------------------------------------------------------ vehicle = deepcopy(vehicle_0) vehicle.mass_properties.max_zero_fuel = MZFW vehicle.mass_properties.takeoff = None vehicle.mass_properties.payload = 0 vehicle.mass_properties.cargo = 0 vehicle.number_of_passengers = 1 vehicle.mass_properties.fuel = 0 for network in vehicle.networks: for fuel_line in network.fuel_lines: for fuel_tank in fuel_line.fuel_tanks: fuel_tank.fuel.mass_properties.mass = 0 for cargo_bay in vehicle.cargo_bays: cargo_bay.mass_properties.mass = 0 for fuselage in vehicle.fuselages: for cabin in fuselage.cabins: cabin.number_of_passengers = 0 for wing in vehicle.wings: if isinstance(wing, RCAIDE.Library.Components.Wings.Blended_Wing_Body): for cabin in wing.cabins: cabin.number_of_passengers = 0 #------------------------------------------------------------------------ # Update Passengers #------------------------------------------------------------------------ # run weights analysis and store results vehicle.number_of_passengers = pax = 1 if p_i == 0 else int(percent_pax[p_i] * PAX ) for fuselage in vehicle.fuselages: for cabin in fuselage.cabins: cabin.filled_seats_arrangement = filling_order[f_o] pax = 1 if p_i == 0 else int(percent_pax[p_i] * vehicle_0.fuselages[fuselage.tag].cabins[cabin.tag].number_of_passengers) cabin_mass = 1E-6 if p_i == 0 else pax * W_PAX_per_pax cabin.number_of_passengers = pax cabin.mass_properties.mass = cabin_mass for wing in vehicle.wings: if isinstance(wing, RCAIDE.Library.Components.Wings.Blended_Wing_Body): for cabin in wing.cabins: cabin.filled_seats_arrangement = filling_order[f_o] pax = 1 if p_i == 0 else int(percent_pax[p_i] * vehicle_0.wings[wing.tag].cabins[cabin.tag].number_of_passengers) cabin_mass = 1E-6 if p_i == 0 else pax * W_PAX_per_pax cabin.number_of_passengers = pax cabin.mass_properties.mass = cabin_mass #------------------------------------------------------------------------ # Update Cargo #------------------------------------------------------------------------ for cargo_bay_i , cargo_bay in enumerate(vehicle.cargo_bays): cargo_bay.mass_properties.mass = percent_cargo[c_i] * W_CARGO * cargo_bay_ratio[cargo_bay_i] vehicle.mass_properties.cargo = percent_cargo[c_i] * W_CARGO vehicle.mass_properties.payload = (W_PAX_per_pax) *vehicle.number_of_passengers + vehicle.mass_properties.cargo # loop through fuel tanks for network in vehicle.networks: for fuel_line in network.fuel_lines: for fuel_tank_i , fuel_tank in enumerate(fuel_line.fuel_tanks): fuel_tank.fuel.mass_properties.mass = percent_fuel[f_i] * FUEL * fuel_tank_ratio[fuel_tank_i] # run weights analysis and store results vehicle.mass_properties.fuel = percent_fuel[f_i] * FUEL #------------------------------------------------------------------------ # run weights analysis and store results #------------------------------------------------------------------------ counter = compute_aircraft_load_data_point(vehicle,cruise_segment_tag,RES,counter, total_sims, neutral_point_0,percent_pax[p_i], percent_cargo[c_i], percent_fuel[f_i], f_o,p_i, c_i, f_i) #============================================================================================ # Trim Diagram Data #============================================================================================ total_sims = discretization ** 2 counter = 0 for w_i in range(discretization): for c_g_i in range(discretization): vehicle = deepcopy(vehicle_0) # Aircraft-Level Properties vehicle.mass_properties.payload = PLD_per_pax if w_i == 0 else percent_weight[w_i] * PLD vehicle.mass_properties.cargo = percent_weight[w_i] * W_CARGO vehicle.mass_properties.center_of_gravity[0][0] = x_cg_0[0][0] * percent_cg_shift[c_g_i] vehicle.number_of_passengers = 1 if w_i == 0 else int(vehicle_0.number_of_passengers * percent_weight[w_i]) vehicle.mass_properties.fuel = percent_weight[w_i] * FUEL for network in vehicle.networks: for fuel_line in network.fuel_lines: for fuel_tank in fuel_line.fuel_tanks: fuel_tank.fuel.mass_properties.mass = 0 for cargo_bay in vehicle.cargo_bays: cargo_bay.mass_properties.mass = 0 for fuselage in vehicle.fuselages: for cabin in fuselage.cabins: cabin.number_of_passengers = 0 for wing in vehicle.wings: if isinstance(wing, RCAIDE.Library.Components.Wings.Blended_Wing_Body): for cabin in wing.cabins: cabin.number_of_passengers = 0 #------------------------------------------------------------------------ # Update Passengers #------------------------------------------------------------------------ # run weights analysis and store results vehicle.number_of_passengers = 1 if w_i == 0 else int(percent_weight[w_i] * PAX ) for fuselage in vehicle.fuselages: for cabin in fuselage.cabins: pax = 1 if w_i == 0 else int(percent_weight[w_i] * vehicle_0.fuselages[fuselage.tag].cabins[cabin.tag].number_of_passengers) cabin.number_of_passengers = pax for wing in vehicle.wings: if isinstance(wing, RCAIDE.Library.Components.Wings.Blended_Wing_Body): for cabin in wing.cabins: pax = 1 if w_i == 0 else int(percent_weight[w_i] * vehicle_0.wings[wing.tag].cabins[cabin.tag].number_of_passengers) cabin.number_of_passengers = pax #------------------------------------------------------------------------ # Update Cargo #------------------------------------------------------------------------ for cargo_bay in vehicle.cargo_bays: cargo_bay.mass_properties.mass = percent_cargo[w_i] * W_CARGO * cargo_bay_ratio[cargo_bay_i] vehicle.mass_properties.cargo = percent_cargo[w_i] * W_CARGO vehicle.mass_properties.payload = (W_PAX_per_pax) *vehicle.number_of_passengers + vehicle.mass_properties.cargo # loop through fuel tanks for network in vehicle.networks: for fuel_line in network.fuel_lines: for fuel_tank in fuel_line.fuel_tanks: fuel_tank.fuel.mass_properties.mass = percent_fuel[w_i] * FUEL * fuel_tank_ratio[fuel_tank_i] # run weights analysis and store results vehicle.mass_properties.fuel = percent_fuel[w_i] * FUEL cargo_weight = percent_cargo[w_i] * W_CARGO pax_weight = percent_fuel[w_i] * W_PAX fuel_weight = percent_fuel[w_i] * FUEL vehicle.mass_properties.takeoff = OEW + pax_weight + cargo_weight + fuel_weight # Run aerodynamic analysis counter = compute_aircraft_trim_data_point(vehicle,cruise_segment_tag,RES,counter,total_sims,neutral_point_0,w_i,c_g_i) return RES
[docs] def compute_aircraft_load_data_point(vehicle,cruise_segment_tag,RES,counter, total_sims,neutral_point,percent_pax, percent_cargo, percent_fuel, f_o,p_i,c_i,f_i): # run geometry and mass properties analyes center_of_gravity, mass, moment, _ = compute_vehicle_center_of_gravity(vehicle, centre_of_gravity_df = pd.DataFrame(columns=[ "Component", "Mass (kg)", "CG x (m)", "CG y (m)","CG z (m)"]), overwrite_center_of_gravity = True, segment=None, verbose=False) # store results RES.loading_results.CG_location[f_o,p_i,c_i,f_i] = center_of_gravity[0][0] RES.loading_results.mass[f_o,p_i,c_i,f_i] = mass[0] RES.loading_results.LEMAC_location[f_o,p_i,c_i,f_i] = vehicle.LEMAC RES.loading_results.static_margin[f_o,p_i,c_i,f_i] = (neutral_point - center_of_gravity[0][0]) /vehicle.reference_chord RES.loading_results.CG_percent_of_LEMAC_location[f_o,p_i,c_i,f_i] = (RES.loading_results.CG_location[f_o,p_i,c_i,f_i] - vehicle.LEMAC) / vehicle.reference_chord print('***************************************') print('Loading Diagram Data Point: ' + str(counter+1) + ' of ' + str(total_sims)) print('Mass : ', RES.loading_results.mass[f_o,p_i,c_i,f_i]) print('OEW : ', vehicle.mass_properties.operating_empty) print('Percent Fuel : ', percent_fuel*100 ) print('Percent Cargo : ', percent_cargo*100 ) print('Percent Pax : ', percent_pax*100 ) print('Ref. Chord : ', vehicle.reference_chord) print('LEMAC : ', vehicle.LEMAC ) print('Center of Gravity Location: ', RES.loading_results.CG_location[f_o,p_i,c_i,f_i] ) print('Neutral Point Location : ', neutral_point ) print('LEMAC : ', RES.loading_results.LEMAC_location[f_o,p_i,c_i,f_i] ) print('CG as % LEMAC : ', RES.loading_results.CG_percent_of_LEMAC_location[f_o,p_i,c_i,f_i] ) print('***************************************') counter += 1 return counter
[docs] def compute_aircraft_trim_data_point(vehicle,cruise_segment_tag,RES, counter,total_sims,neutral_point,w_i,c_g_i): # store results RES.trim_results.neutral_point[w_i,c_g_i] = neutral_point RES.trim_results.static_margin[w_i,c_g_i] = (RES.trim_results.neutral_point[w_i,c_g_i] - vehicle.mass_properties.center_of_gravity[0][0]) /vehicle.reference_chord RES.trim_results.mass[w_i,c_g_i] = vehicle.mass_properties.takeoff RES.trim_results.LEMAC_location[w_i,c_g_i] = vehicle.LEMAC RES.trim_results.CG_percent_of_LEMAC_location[w_i,c_g_i] = (vehicle.mass_properties.center_of_gravity[0][0] - vehicle.LEMAC) / vehicle.reference_chord print('***************************************') print('Trim Diagram Data Point : ' + str(counter+1) + ' of ' + str(total_sims)) print('Center of Gravity : ',vehicle.mass_properties.center_of_gravity[0][0]) print('OEW : ', vehicle.mass_properties.operating_empty) print('% LEMAC Location : ', RES.trim_results.LEMAC_location[w_i,c_g_i] ) print('Neutral Point : ',RES.trim_results.neutral_point[w_i,c_g_i]) print('Static Margin : ',RES.trim_results.static_margin[w_i,c_g_i]) print('Mass : ',RES.trim_results.mass[w_i,c_g_i] ) print('CG as % LEMAC : ', RES.trim_results.CG_percent_of_LEMAC_location[w_i,c_g_i] ) print('***************************************') counter += 1 return counter