# RCAIDE/Library/Missions/Common/Pre_Process/geometry.py
#
#
# Created: Apr 2023, M. Clarke
# ----------------------------------------------------------------------------------------------------------------------
# IMPORT
# ----------------------------------------------------------------------------------------------------------------------
import RCAIDE
from RCAIDE.Framework.Core import Units
from RCAIDE.Library.Methods.Geometry.LOPA import compute_layout_of_passenger_accommodations
from RCAIDE.Library.Methods.Geometry.Planform import fuselage_planform, wing_planform , compute_fuel_volume
# python imports
import numpy as np
import os, sys
import pandas as pd
from copy import deepcopy
# ----------------------------------------------------------------------------------------------------------------------
# geometry
# ----------------------------------------------------------------------------------------------------------------------
[docs]
def geometry(mission):
"""
Initializes and processes geometry for mission segments
Parameters
----------
mission : Mission
The mission containing segments to be analyzed
- analyses.geometry : Analysis
Stability analysis module
- vehicle : Vehicle
Aircraft geometry definition
- wings : list
Wing geometry definitions
- process.compute.lift.inviscid_wings : Process
Lift computation process
- surrogates : Data
Stability surrogate models
Returns
-------
None
Updates mission segment analyses directly
Notes
-----
This function prepares the geometry for each mission segment
See Also
--------
RCAIDE.Library.Methods.Geometry.Planform
RCAIDE.Framework.Mission.Segments
"""
for i , segment in enumerate(mission.segments):
# --------------------------------------------------------------------------------------------------------------------
# check if geometry analysis is defined
# --------------------------------------------------------------------------------------------------------------------
if segment.analyses.geometry is None:
raise AssertionError('Geometry Analyses not defined')
if i == 0 or segment.analyses.geometry.settings.unique_geometry:
geometry_preprocess_routine(segment.analyses)
else:
# Reuses previous segment pre processed data to save computational time.
# Ensures that changes in configuration are not overwritten.
vehicle_0 = deepcopy(segment.analyses.vehicle)
segment.analyses.vehicle = deepcopy(mission.segments[i-1].analyses.vehicle)
for wing in segment.analyses.vehicle.wings:
for control_surface in wing.control_surfaces:
control_surface.deflection = vehicle_0.wings[wing.tag].control_surfaces[control_surface.tag].deflection
for landing_gear in segment.analyses.vehicle.landing_gears:
landing_gear.gear_extended = vehicle_0.landing_gears[landing_gear.tag].gear_extended
for network in segment.analyses.vehicle.networks:
network.reverse_thrust = vehicle_0.networks[network.tag].reverse_thrust
for bus in network.busses:
bus.active = vehicle_0.networks[network.tag].busses[bus.tag].active
for propulsor in network.propulsors:
propulsor_0 = vehicle_0.networks[network.tag].propulsors[propulsor.tag]
propulsor.active = propulsor_0.active
if isinstance(propulsor, RCAIDE.Library.Components.Powertrain.Propulsors.Turbofan):
propulsor.fan.angular_velocity = propulsor_0.fan.angular_velocity
propulsor.fan_nozzle.exit_velocity = propulsor_0.fan_nozzle.exit_velocity
propulsor.core_nozzle.exit_velocity = propulsor_0.core_nozzle.exit_velocity
if isinstance(propulsor, RCAIDE.Library.Components.Powertrain.Propulsors.Electric_Rotor):
propulsor.rotor.orientation_euler_angles = propulsor_0.rotor.orientation_euler_angles
propulsor.rotor.blade_pitch_command = propulsor_0.rotor.blade_pitch_command
return
[docs]
def geometry_preprocess_routine(analyses):
settings = analyses.geometry.settings
vehicle = analyses.vehicle
# initalize variables
sectional_area = 0
defined_cabins = False
NPF = 0
NPB = 0
NPE = 0
# ================================================================================================================================================
# update fuselage properties
# ================================================================================================================================================
total_seats = 0
for fuselage in vehicle.fuselages:
compute_layout_of_passenger_accommodations(fuselage)
fuselage_planform(fuselage)
vehicle.length = np.maximum(vehicle.length, fuselage.lengths.total)
sectional_area = fuselage.areas.front_projected
for cabin in fuselage.cabins:
defined_cabins = True
for cabin_class in cabin.classes:
if type(cabin_class) == RCAIDE.Library.Components.Fuselages.Cabins.Classes.Economy:
NPE += cabin_class.number_of_seats
elif type(cabin_class) == RCAIDE.Library.Components.Fuselages.Cabins.Classes.Business:
NPB += cabin_class.number_of_seats
elif type(cabin_class) == RCAIDE.Library.Components.Fuselages.Cabins.Classes.First:
NPF += cabin_class.number_of_seats
total_seats += cabin.number_of_seats
for cabin in fuselage.cabins:
if cabin.number_of_passengers == 0: # if cabin class passengers are not defined, use ratio of cabin to aircraft
cabin.number_of_passengers = min(total_seats,int( np.round((cabin.number_of_seats / total_seats) * vehicle.number_of_passengers)))
# ================================================================================================================================================
# update landing gear properties
# ================================================================================================================================================
for landing_gear in vehicle.landing_gears:
symm = landing_gear.xz_plane_symmetric
landing_gear.wheels = landing_gear.number_of_gear_types_in_tandem * landing_gear.number_of_wheels_in_gear_type * (symm + 1)
# ================================================================================================================================================
# update wing properties
# ================================================================================================================================================
for wing in vehicle.wings:
# --------------------------------------------------------------------------------------------------------------------
# Blended Wing Body
# --------------------------------------------------------------------------------------------------------------------
if isinstance(wing, RCAIDE.Library.Components.Wings.Blended_Wing_Body):
# compute planform properties
wing_planform(wing)
compute_layout_of_passenger_accommodations(wing)
# update reference properties
if settings.overwrite_reference:
vehicle.reference_area = wing.areas.reference
vehicle.LEMAC = wing.LEMAC
for cabin in wing.cabins:
defined_cabins = True
for cabin_class in cabin.classes:
if type(cabin_class) == RCAIDE.Library.Components.Fuselages.Cabins.Classes.Economy:
NPE += cabin_class.number_of_seats
elif type(cabin_class) == RCAIDE.Library.Components.Fuselages.Cabins.Classes.Business:
NPB += cabin_class.number_of_seats
elif type(cabin_class) == RCAIDE.Library.Components.Fuselages.Cabins.Classes.First:
NPF += cabin_class.number_of_seats
total_seats += cabin.number_of_seats
if cabin.number_of_passengers == 0: # if cabin class passengers are not defined, use ratio of cabin to aircraft
cabin.number_of_passengers = min(total_seats,int( np.round((cabin.number_of_seats / total_seats) * vehicle.number_of_passengers)))
# --------------------------------------------------------------------------------------------------------------------
# All other wing surfaces
# --------------------------------------------------------------------------------------------------------------------
else:
wing_planform(wing)
if isinstance(wing, RCAIDE.Library.Components.Wings.Main_Wing) and settings.overwrite_reference:
vehicle.reference_area = wing.areas.reference
vehicle.LEMAC = wing.LEMAC
# reference chord
vehicle.reference_chord = np.maximum(vehicle.reference_chord , wing.chords.mean_aerodynamic)
# reference span
vehicle.reference_span = np.maximum(vehicle.reference_span , wing.spans.projected)
# total length
vehicle.length = np.maximum(vehicle.length, wing.chords.root)
# max cross sectional area
sectional_area += wing.areas.front_projected
for network in vehicle.networks:
for propulsor in network.propulsors:
sectional_area += (propulsor.diameter**2) * np.pi / 4.0
vehicle.maximum_cross_sectional_area = sectional_area
# --------------------------------------------------------------------------------------------------------------------
# Update passenger imformation
# --------------------------------------------------------------------------------------------------------------------
if vehicle.number_of_passengers == 0:
pass
else:
if defined_cabins:
vehicle.number_of_first_class_seats = NPF
vehicle.number_of_business_class_seats = NPB
vehicle.number_of_economy_class_seats = NPE
else:
vehicle.number_of_first_class_seats = vehicle.number_of_passengers / 20.
vehicle.number_of_business_class_seats = vehicle.number_of_passengers / 10.
vehicle.number_of_economy_class_seats = vehicle.number_of_passengers - NPF - NPB
# --------------------------------------------------------------------------------------------------------------------
# Compute fuel volume
# --------------------------------------------------------------------------------------------------------------------
compute_fuel_volume(vehicle, compute_fuel_volume=settings.compute_fuel_volume, update_max_fuel=settings.update_max_fuel)
if settings.write_geometry_properties:
write_geometry_to_excel(vehicle)
return
[docs]
def write_geometry_to_excel(vehicle):
"""
Export vehicle geometry and related fuel/propulsor data to an Excel workbook.
Parameters
----------
vehicle : RCAIDE.Vehicle
Vehicle object containing fuselages, wings, segments, networks, fuel tanks,
and propulsors to be serialized into tabular sheets.
Notes
-----
None
"""
excel_filename = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), os.path.splitext(os.path.basename(sys.argv[0]))[0] + "_geometry_description.xlsx")
fuselage_rows = []
wing_rows = []
segment_rows = []
fuel_rows = []
prop_rows = []
# Collect Fuselage Level Properties
for fuselage in vehicle.fuselages:
fuselage_rows.append({
"Fuselage Tag" : fuselage.tag,
"Fuselage Origin" : fuselage.origin[0],
"Total Length (m)" : fuselage.lengths.total,
"Nose Length (m)" : fuselage.lengths.nose,
"Tail Length (m)" : fuselage.lengths.tail,
"Maximum Height (m)" : fuselage.heights.maximum,
"Width (m)" : fuselage.width,
"Effective Diameter (m)" : fuselage.effective_diameter,
"Fineness Nose" : fuselage.fineness.nose,
"Fineness Tail" : fuselage.fineness.tail,
"Front Projected Area (m^2)" : fuselage.areas.front_projected,
"Side Projected Area (m^2)" : fuselage.areas.side_projected,
"Wetted Area (m^2)" : fuselage.areas.wetted,
"Passengers" : fuselage.number_of_passengers,
"Seats" : fuselage.number_of_seats,
})
# Collect wing-level properties
for wing in vehicle.wings:
wing_rows.append({
"Wing Tag" : wing.tag,
"Wing Origin" : wing.origin[0],
"Projected Span (m)" : wing.spans.projected,
"Root Chord (m)" : wing.chords.root,
"Mean Aerodynamic Chord (m)" : wing.chords.mean_aerodynamic,
"Gross Aspect Ratio" : wing.aspect_ratio,
"Trapezoid Aspect Ratio" : wing.spans.projected**2/wing.areas.reference,
"LEMAC (m)" : wing.LEMAC,
"Reference Area (m^2)" : wing.areas.reference,
"Wetted Area (m^2)" : wing.areas.wetted,
"XZ Symmetric" : wing.xz_plane_symmetric,
"XY Symmetric" : wing.xy_plane_symmetric,
})
# Collect segment-level properties for each wing
for segment in wing.segments:
segment_rows.append({
"Wing Tag" : wing.tag,
"Segment Tag" : segment.tag,
"Segment Origin" : f'[{str(segment.origin[0][0])}, {str(segment.origin[0][1])}, {str(segment.origin[0][2])}]',
"Spanwise Location (%)" : segment.percent_span_location * 100.0,
"Root Chord Fraction" : segment.root_chord_percent,
"Twist (deg)" : segment.twist / Units.degree,
"Outboard Dihedral (deg)" : segment.dihedral_outboard / Units.degree,
"Quarter-Chord Sweep (deg)" : segment.sweeps.quarter_chord / Units.degree,
"Leading-Edge Sweep (deg)" : segment.sweeps.leading_edge / Units.degree
})
# Collect fuel tank properties from fuel lines and busses
for network in vehicle.networks:
network_tag = getattr(network, "tag", None)
# Propulsors
for propulsor in network.propulsors:
prop_rows.append({
"Network Tag" : network_tag,
"Propulsor Origin" : propulsor.origin[0],
"Propulsor Tag" : getattr(propulsor, "tag", None),
"Type" : propulsor.__class__.__name__,
"Length (m)" : getattr(propulsor, "length", None),
"Diameter (m)" : getattr(propulsor, "diameter", None),
"Bypass Ratio" : getattr(propulsor, "bypass_ratio", None),
"Sealevel Static Thrust [lbf]" : getattr(propulsor, "sealevel_static_thrust", None)/Units.lbf,
"TSFC [lb/lbf-hr]" : getattr(propulsor, "TSFC", None)[0][0]
})
for fuel_line in network.fuel_lines:
container_tag = getattr(fuel_line, "tag", None)
for fuel_tank in fuel_line.fuel_tanks:
fuel_rows.append({
"Network Tag" : network_tag,
"Tank Type" : str(type(fuel_tank)[0]).split('.')[-1],
"Container Type" : "fuel_line",
"Container Tag" : container_tag,
"Fuel Tank Tag" : fuel_tank.tag,
"Wing Tag" : getattr(fuel_tank, "wing_tag", None),
"Fuselage Tag" : getattr(fuel_tank, "fuselage_tag", None),
"Percent Span Location" : getattr(fuel_tank, "percent_span_location", None),
"Segments Bounding Tank" : getattr(fuel_tank, "segments_bounding_tank", None),
"Segments % Chord Start" : getattr(fuel_tank, "segments_percent_chord_start", None),
"Segments % Chord End" : getattr(fuel_tank, "segments_percent_chord_end", None),
"BWB Aft Tank" : getattr(fuel_tank, "transverse_tank", None),
"XZ Plane Symmetric" : getattr(fuel_tank, "xz_plane_symmetric", None),
"Fuel Net Volume (m^3)" : getattr(getattr(fuel_tank.fuel, "volume_properties", None), "net_volume", None) if fuel_tank.fuel else None,
"Fuel Gross Volume (m^3)" : getattr(getattr(fuel_tank, "volume_properties", None), "gross_volume", None) if fuel_tank.fuel else None,
"Fuel Mass (kg)" : getattr(getattr(fuel_tank.fuel, "mass_properties", None), "mass", None) if fuel_tank.fuel else None,
})
# Write to Excel with separate sheets for wings and segments
with pd.ExcelWriter(excel_filename) as writer:
pd.DataFrame(fuselage_rows).to_excel(writer, sheet_name='Fuselage_Properties', index=False)
pd.DataFrame(wing_rows).to_excel(writer, sheet_name='Wing_Properties', index=False)
pd.DataFrame(segment_rows).to_excel(writer, sheet_name='Segment_Properties', index=False)
pd.DataFrame(fuel_rows).to_excel(writer, sheet_name='Fuel_Tanks', index=False)
pd.DataFrame(prop_rows).to_excel(writer, sheet_name='Propulsors', index=False)
print(f"Geometry Description written to Excel:\n {excel_filename}")
return