Tutorial 7 - Lift + Cruise eVTOL Aircraft Simulation#

Welcome to this tutorial on simulating a lift + cruise eVTOL aircraft using 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.


Header and Imports#

The Imports section is divided into two parts: simulation-specific libraries and general-purpose Python libraries.

The RCAIDE Imports section includes the core modules needed for the simulation. These libraries provide specialized classes and tools for building, analyzing, and running aircraft models.

[1]:
#----------------------------------------------------------------------
#   Imports
# ---------------------------------------------------------------------
import RCAIDE
from RCAIDE.Framework.Core import Units
from RCAIDE.Library.Methods.Powertrain.Propulsors.Electric_Rotor import design_electric_rotor
from RCAIDE.Library.Plots                                        import *
from RCAIDE.Input_Output import load as load_propulsor
from RCAIDE.Input_Output import save as save_propulsor

import os
import numpy as np
from copy import deepcopy
import matplotlib.pyplot as plt
import pickle
import sys
sys.path.insert(0,(os.path.dirname(os.getcwd())))

Vehicle Setup#

The ``vehicle_setup`` function defines the baseline configuration of the aircraft. This section builds the vehicle step-by-step by specifying its components, geometric properties, and high-level parameters.


1. Creating the Vehicle Instance#

The setup begins by creating a vehicle instance and assigning it a tag. The tag is a unique string identifier used to reference the vehicle during analysis or in post-processing steps.


2. Defining High-Level Vehicle Parameters#

The high-level parameters describe the aircraft’s key operational characteristics, such as:

  • Maximum Takeoff Weight: The heaviest allowable weight of the aircraft for safe flight.

  • Operating Empty Weight: The aircraft weight without fuel, passengers, or payload.

  • Payload: The weight of cargo and passengers.

  • Max Zero Fuel Weight: The maximum weight of the aircraft excluding fuel.

Units for these parameters can be converted automatically using the Units module to ensure consistency and reduce errors.


3. Defining the Landing Gear#

Landing gear parameters, such as the number of main and nose wheels, are set for the aircraft. While not used in this tutorial, these values can be applied in advanced analyses, such as ground loads or noise prediction.


4. Main Wing Setup#

The main wing is added using the ``Main_Wing`` class. This designation ensures that the primary lifting surface is recognized correctly by the analysis tools. Key properties of the wing include:

  • Area: The total wing surface area.

  • Span: The length of the wing from tip to tip.

  • Aspect Ratio: A ratio of span to average chord, determining wing efficiency.

  • Segments: Divisions of the wing geometry (e.g., root and tip sections).

  • Control Surfaces: High-lift devices like flaps and ailerons, defined by span fractions and deflections.


5. Horizontal and Vertical Stabilizers#

The stabilizers provide stability and control for the aircraft:

  • Horizontal Stabilizer: Defined using the Horizontal_Tail class. It follows a similar setup to the main wing but acts as a stabilizing surface.

  • Vertical Stabilizer: Defined using the Vertical_Tail class, with an additional option to designate the tail as a T-tail for weight calculations.


6. Fuselage Definition#

The fuselage is modeled by specifying its geometric parameters, such as:

  • Length: The overall length of the aircraft body.

  • Width: The widest part of the fuselage cross-section.

  • Height: The height of the fuselage.

These values influence drag calculations and overall structural weight.


7. Energy Network#

The energy network models the propulsion system. The energy network determines the engine’s thrust, bypass ratio, and fuel type. These parameters are essential for performance and fuel efficiency analyses.


[2]:
# ----------------------------------------------------------------------
#   Build the Vehicle
# ----------------------------------------------------------------------
def vehicle_setup(new_regression=True) :

    ospath                                = os.path.abspath(os.path.join('Notebook'))
    separator                             = os.path.sep
    rel_path                              = os.path.dirname(ospath) + separator + '..' + separator + '..' + separator + 'VnV' + separator + 'Vehicles' + separator
    airfoil_path                          = rel_path

    local_path       = os.path.dirname(ospath) + separator


    #------------------------------------------------------------------------------------------------------------------------------------
    # ################################################# Vehicle-level Properties ########################################################
    #------------------------------------------------------------------------------------------------------------------------------------

    vehicle               = RCAIDE.Vehicle()
    vehicle.tag           = 'Lift_Cruise'
    vehicle.configuration = 'eVTOL'

    # mass properties
    vehicle.mass_properties.max_takeoff       = 2700
    vehicle.mass_properties.takeoff           = vehicle.mass_properties.max_takeoff
    vehicle.mass_properties.operating_empty   = vehicle.mass_properties.max_takeoff
    vehicle.flight_envelope.ultimate_load     = 5.7
    vehicle.flight_envelope.positive_limit_load        = 3.
    vehicle.passengers                        = 5

    #------------------------------------------------------------------------------------------------------------------------------------
    # ######################################################## Wings ####################################################################
    #------------------------------------------------------------------------------------------------------------------------------------
    # ------------------------------------------------------------------
    #   Main Wing
    # ------------------------------------------------------------------
    wing                          = RCAIDE.Library.Components.Wings.Main_Wing()
    wing.tag                      = 'main_wing'
    wing.aspect_ratio             = 8.95198  # will  be overwritten
    wing.sweeps.quarter_chord     = 0.0
    wing.thickness_to_chord       = 0.14
    wing.taper                    = 0.292
    wing.spans.projected          = 11.82855
    wing.chords.root              = 1.75
    wing.total_length             = 1.75
    wing.chords.tip               = 1.0
    wing.chords.mean_aerodynamic  = 1.5
    wing.dihedral                 = 0.0
    wing.areas.reference          = 15.629
    wing.twists.root              = 4. * Units.degrees
    wing.twists.tip               = 0.
    wing.origin                   = [[1.5, 0., 0.991]]
    wing.aerodynamic_center       = [ 1.567, 0., 0.991]
    wing.winglet_fraction         = 0.0
    wing.symmetric                = True
    wing.vertical                 = False

    # Segment
    segment                       = RCAIDE.Library.Components.Wings.Segments.Segment()
    segment.tag                   = 'Section_1'
    segment.percent_span_location = 0.0
    segment.twist                 = 4. * Units.degrees
    segment.root_chord_percent    = 1.
    segment.dihedral_outboard     = 8 * Units.degrees
    segment.sweeps.quarter_chord  = 0.9  * Units.degrees
    segment.thickness_to_chord    = 0.16
    wing.append_segment(segment)

    # Segment
    segment                       = RCAIDE.Library.Components.Wings.Segments.Segment()
    segment.tag                   = 'Section_2'
    segment.percent_span_location = 3.5/wing.spans.projected
    segment.twist                 = 3. * Units.degrees
    segment.root_chord_percent    = 1.4000/1.7500
    segment.dihedral_outboard     = 0.0 * Units.degrees
    segment.sweeps.quarter_chord  = 1.27273 * Units.degrees
    segment.thickness_to_chord    = 0.16
    wing.append_segment(segment)

    # Segment
    segment                       = RCAIDE.Library.Components.Wings.Segments.Segment()
    segment.tag                   = 'Section_3'
    segment.percent_span_location = 11.3/wing.spans.projected
    segment.twist                 = 2.0 * Units.degrees
    segment.root_chord_percent    = 1.000/1.7500
    segment.dihedral_outboard     = 35.000* Units.degrees
    segment.sweeps.quarter_chord  = 45.000* Units.degrees
    segment.thickness_to_chord    = 0.16
    wing.append_segment(segment)

    # Segment
    segment                       = RCAIDE.Library.Components.Wings.Segments.Segment()
    segment.tag                   = 'Section_4'
    segment.percent_span_location = 11.6/wing.spans.projected
    segment.twist                 = 0.0 * Units.degrees
    segment.root_chord_percent    = 0.9/1.7500
    segment.dihedral_outboard     = 60. * Units.degrees
    segment.sweeps.quarter_chord  = 70.0 * Units.degrees
    segment.thickness_to_chord    = 0.16
    wing.append_segment(segment)

    # Segment
    segment                       = RCAIDE.Library.Components.Wings.Segments.Segment()
    segment.tag                   = 'Section_5'
    segment.percent_span_location = 1.0
    segment.twist                 = 0.0 * Units.degrees
    segment.root_chord_percent    = 0.35/1.7500
    segment.dihedral_outboard     = 0  * Units.degrees
    segment.sweeps.quarter_chord  = 0  * Units.degrees
    segment.thickness_to_chord    = 0.16
    wing.append_segment(segment)

    vehicle.reference_area        = wing.areas.reference
    wing.areas.wetted             = wing.areas.reference  * 2
    wing.areas.exposed            = wing.areas.reference  * 2


    # control surfaces -------------------------------------------
    flap                          = RCAIDE.Library.Components.Wings.Control_Surfaces.Flap()
    flap.tag                      = 'flap'
    flap.span_fraction_start      = 0.2
    flap.span_fraction_end        = 0.5
    flap.deflection               = 0.0 * Units.degrees
    flap.chord_fraction           = 0.20
    wing.append_control_surface(flap)


    aileron                       = RCAIDE.Library.Components.Wings.Control_Surfaces.Aileron()
    aileron.tag                   = 'aileron'
    aileron.span_fraction_start   = 0.7
    aileron.span_fraction_end     = 0.9
    aileron.deflection            = 0.0 * Units.degrees
    aileron.chord_fraction        = 0.2
    wing.append_control_surface(aileron)


    # add to vehicle
    vehicle.append_component(wing)


    #------------------------------------------------------------------------------------------------------------------------------------
    #   Horizontal Tail
    #------------------------------------------------------------------------------------------------------------------------------------
    wing                          = RCAIDE.Library.Components.Wings.Horizontal_Tail()
    wing.aspect_ratio             = 3.04444
    wing.sweeps.quarter_chord     = 17. * Units.degrees
    wing.thickness_to_chord       = 0.12
    wing.spans.projected          = 2.71805
    wing.chords.root              = 0.94940
    wing.total_length             = 0.94940
    wing.chords.tip               = 0.62731
    wing.chords.mean_aerodynamic  = 0.809
    wing.dihedral                 = 20 *Units.degrees
    wing.taper                    = wing.chords.tip / wing.chords.root
    wing.areas.reference          = 2.14279
    wing.areas.wetted             = 2.14279   * 2
    wing.areas.exposed            = 2.14279   * 2
    wing.twists.root              = 0.0
    wing.twists.tip               = 0.0
    wing.origin                   = [[  5.374 ,0.0 ,  0.596]]
    wing.aerodynamic_center       = [   5.374, 0.0,   0.596]
    wing.winglet_fraction         = 0.0
    wing.symmetric                = True


    elevator                              = RCAIDE.Library.Components.Wings.Control_Surfaces.Elevator()
    elevator.tag                          = 'elevator'
    elevator.span_fraction_start          = 0.6
    elevator.span_fraction_end            = 0.9
    elevator.deflection                   = 0.0  * Units.deg
    elevator.chord_fraction               = 0.4
    wing.append_control_surface(elevator)


    rudder                                = RCAIDE.Library.Components.Wings.Control_Surfaces.Rudder()
    rudder.tag                            = 'rudder'
    rudder.span_fraction_start            = 0.1
    rudder.span_fraction_end              = 0.5
    rudder.deflection                     = 0.0  * Units.deg
    rudder.chord_fraction                 = 0.4
    wing.append_control_surface(rudder)


    # add to vehicle
    vehicle.append_component(wing)

    #------------------------------------------------------------------------------------------------------------------------------------
    # ##########################################################   Fuselage  ############################################################
    #------------------------------------------------------------------------------------------------------------------------------------
    fuselage                                    = RCAIDE.Library.Components.Fuselages.Fuselage()
    fuselage.tag                                = 'fuselage'
    fuselage.seats_abreast                      = 2.
    fuselage.seat_pitch                         = 3.
    fuselage.fineness.nose                      = 0.88
    fuselage.fineness.tail                      = 1.13
    fuselage.lengths.nose                       = 0.5
    fuselage.lengths.tail                       = 1.5
    fuselage.lengths.cabin                      = 4.46
    fuselage.lengths.total                      = 6.46
    fuselage.width                              = 5.85 * Units.feet      # change
    fuselage.heights.maximum                    = 4.65 * Units.feet      # change
    fuselage.heights.at_quarter_length          = 3.75 * Units.feet      # change
    fuselage.heights.at_wing_root_quarter_chord = 4.65 * Units.feet      # change
    fuselage.heights.at_three_quarters_length   = 4.26 * Units.feet      # change
    fuselage.areas.wetted                       = 236. * Units.feet**2   # change
    fuselage.areas.front_projected              = 0.14 * Units.feet**2   # change
    fuselage.effective_diameter                 = 1.276     # change
    fuselage.differential_pressure              = 0.

    # Segment
    segment                                     = RCAIDE.Library.Components.Fuselages.Segments.Segment()
    segment.tag                                 = 'segment_0'
    segment.percent_x_location                  = 0.0
    segment.percent_z_location                  = 0.     # change
    segment.height                              = 0.049
    segment.width                               = 0.032
    fuselage.append_segment(segment)

    # Segment
    segment                                     = RCAIDE.Library.Components.Fuselages.Segments.Segment()
    segment.tag                                 = 'segment_1'
    segment.percent_x_location                  = 0.10912/fuselage.lengths.total
    segment.percent_z_location                  = 0.00849
    segment.height                              = 0.481
    segment.width                               = 0.553
    fuselage.append_segment(segment)

    # Segment
    segment                                     = RCAIDE.Library.Components.Fuselages.Segments.Segment()
    segment.tag                                 = 'segment_2'
    segment.percent_x_location                  = 0.47804/fuselage.lengths.total
    segment.percent_z_location                  = 0.02874
    segment.height                              = 1.00
    segment.width                               = 0.912
    fuselage.append_segment(segment)

    # Segment
    segment                                     = RCAIDE.Library.Components.Fuselages.Segments.Segment()
    segment.tag                                 = 'segment_3'
    segment.percent_x_location                  = 0.161
    segment.percent_z_location                  = 0.04348
    segment.height                              = 1.41
    segment.width                               = 1.174
    fuselage.append_segment(segment)

    # Segment
    segment                                     = RCAIDE.Library.Components.Fuselages.Segments.Segment()
    segment.tag                                 = 'segment_4'
    segment.percent_x_location                  = 0.284
    segment.percent_z_location                  = 0.05435
    segment.height                              = 1.62
    segment.width                               = 1.276
    fuselage.append_segment(segment)

    # Segment
    segment                                     = RCAIDE.Library.Components.Fuselages.Segments.Segment()
    segment.tag                                 = 'segment_5'
    segment.percent_x_location                  = 3.43026/fuselage.lengths.total
    segment.percent_z_location                  = 0.31483/fuselage.lengths.total
    segment.height                              = 1.409
    segment.width                               = 1.121
    fuselage.append_segment(segment)

    # Segment
    segment                                     = RCAIDE.Library.Components.Fuselages.Segments.Segment()
    segment.tag                                 = 'segment_6'
    segment.percent_x_location                  = 4.20546/fuselage.lengths.total
    segment.percent_z_location                  = 0.32216/fuselage.lengths.total
    segment.height                              = 1.11
    segment.width                               = 0.833
    fuselage.append_segment(segment)

    # Segment
    segment                                     = RCAIDE.Library.Components.Fuselages.Segments.Segment()
    segment.tag                                 = 'segment_7'
    segment.percent_x_location                  = 4.99358/fuselage.lengths.total
    segment.percent_z_location                  = 0.37815/fuselage.lengths.total
    segment.height                              = 0.78
    segment.width                               = 0.512
    fuselage.append_segment(segment)

    # Segment
    segment                                     = RCAIDE.Library.Components.Fuselages.Segments.Segment()
    segment.tag                                 = 'segment_8'
    segment.percent_x_location                  = 1.
    segment.percent_z_location                  = 0.55/fuselage.lengths.total
    segment.height                              = 0.195
    segment.width                               = 0.130
    fuselage.append_segment(segment)

    vehicle.append_component(fuselage)

    #------------------------------------------------------------------------------------------------------------------------------------
    # ##########################################################  Booms  ################################################################
    #------------------------------------------------------------------------------------------------------------------------------------
    boom                                    = RCAIDE.Library.Components.Booms.Boom()
    boom.tag                                = 'boom_1r'
    boom.configuration                      = 'boom'
    boom.origin                             = [[   0.036, 1.950,  1]]
    boom.seats_abreast                      = 0.
    boom.seat_pitch                         = 0.0
    boom.fineness.nose                      = 0.950
    boom.fineness.tail                      = 1.029
    boom.lengths.nose                       = 0.2
    boom.lengths.tail                       = 0.2
    boom.lengths.cabin                      = 4.15
    boom.lengths.total                      = 4.2
    boom.width                              = 0.15
    boom.heights.maximum                    = 0.15
    boom.heights.at_quarter_length          = 0.15
    boom.heights.at_three_quarters_length   = 0.15
    boom.heights.at_wing_root_quarter_chord = 0.15
    boom.areas.wetted                       = 0.018
    boom.areas.front_projected              = 0.018
    boom.effective_diameter                 = 0.15
    boom.differential_pressure              = 0.
    boom.symmetric                          = True
    boom.index                              = 1

    # Segment
    segment                           = RCAIDE.Library.Components.Booms.Segments.Segment()
    segment.tag                       = 'segment_1'
    segment.percent_x_location        = 0.
    segment.percent_z_location        = 0.0
    segment.height                    = 0.05
    segment.width                     = 0.05
    boom.append_segment(segment)

    # Segment
    segment                           = RCAIDE.Library.Components.Booms.Segments.Segment()
    segment.tag                       = 'segment_2'
    segment.percent_x_location        = 0.03
    segment.percent_z_location        = 0.
    segment.height                    = 0.15
    segment.width                     = 0.15
    boom.append_segment(segment)

    # Segment
    segment                           = RCAIDE.Library.Components.Booms.Segments.Segment()
    segment.tag                       = 'segment_3'
    segment.percent_x_location        = 0.97
    segment.percent_z_location        = 0.
    segment.height                    = 0.15
    segment.width                     = 0.15
    boom.append_segment(segment)

    # Segment
    segment                           = RCAIDE.Library.Components.Booms.Segments.Segment()
    segment.tag                       = 'segment_4'
    segment.percent_x_location        = 1.
    segment.percent_z_location        = 0.
    segment.height                    = 0.05
    segment.width                     = 0.05
    boom.append_segment(segment)

    # add to vehicle
    vehicle.append_component(boom)

    # add left long boom
    boom              = deepcopy(vehicle.booms.boom_1r)
    boom.origin[0][1] = -boom.origin[0][1]
    boom.tag          = 'boom_1l'
    vehicle.append_component(boom)

    # add left long boom
    boom              = deepcopy(vehicle.booms.boom_1r)
    boom.origin       = [[     0.110,    4.891,   1.050]]
    boom.tag          = 'boom_2r'
    boom.lengths.total                      = 4.16
    vehicle.append_component(boom)

    # add inner left boom
    boom              = deepcopy(vehicle.booms.boom_1r)
    boom.origin       = [[     0.110, -  4.891,    1.050 ]]
    boom.lengths.total                      = 4.16
    boom.tag          = 'boom_2l'
    vehicle.append_component(boom)

    #------------------------------------------------------------------------------------------------------------------------------------
    # ########################################################  Energy Network  #########################################################
    #------------------------------------------------------------------------------------------------------------------------------------
    network                                                = RCAIDE.Framework.Networks.Electric()

    #====================================================================================================================================
    # Forward Bus
    #====================================================================================================================================
    cruise_bus                           = RCAIDE.Library.Components.Powertrain.Distributors.Electrical_Bus()
    cruise_bus.tag                       = 'cruise_bus'
    cruise_bus.number_of_battery_modules =  1

    #------------------------------------------------------------------------------------------------------------------------------------
    # Bus Battery
    #------------------------------------------------------------------------------------------------------------------------------------
    battery_module                                                    = RCAIDE.Library.Components.Powertrain.Sources.Battery_Modules.Lithium_Ion_NMC()
    battery_module.tag                                                = 'cruise_bus_battery'
    battery_module.electrical_configuration.series                     = 140
    battery_module.electrical_configuration.parallel                   = 60
    battery_module.geometric_configuration.normal_count               = 140
    battery_module.geometric_configuration.parallel_count             = 60
    for _ in range( cruise_bus.number_of_battery_modules):
        cruise_bus.battery_modules.append(deepcopy(battery_module))
    cruise_bus.initialize_bus_properties()

    #------------------------------------------------------------------------------------------------------------------------------------
    # Forward Bus Propulsors
    #------------------------------------------------------------------------------------------------------------------------------------
    # Define Forward Propulsor Container
    cruise_propulsor_1                                     = RCAIDE.Library.Components.Powertrain.Propulsors.Electric_Rotor()
    cruise_propulsor_1.tag                                 = 'cruise_propulsor_1'
    cruise_propulsor_1.wing_mounted                        = False

    # Electronic Speed Controller
    propeller_esc                                          = RCAIDE.Library.Components.Powertrain.Modulators.Electronic_Speed_Controller()
    propeller_esc.efficiency                               = 0.95
    propeller_esc.bus_voltage                              = cruise_bus.voltage
    propeller_esc.origin                                   = [[6.583, 1.300,  1.092 ]]
    propeller_esc.tag                                      = 'propeller_esc_1'
    cruise_propulsor_1.electronic_speed_controller         = propeller_esc

    # Propeller
    g                                                      = 9.81                                   # gravitational acceleration
    speed_of_sound                                         = 340                                    # speed of sound
    Hover_Load                                             = vehicle.mass_properties.takeoff*g *1.1 # hover load

    propeller                                              = RCAIDE.Library.Components.Powertrain.Converters.Propeller()
    propeller.number_of_blades                             = 3
    propeller.tag                                          = 'propeller_1'
    propeller.origin                                       = [[6.583, 1.300,  1.092 ]]
    propeller.tip_radius                                   = 1.15
    propeller.hub_radius                                   = 0.1 * propeller.tip_radius
    propeller.cruise.design_freestream_velocity            = 130.* Units['mph']
    propeller.cruise.design_tip_mach                       = 0.65
    propeller.cruise.design_angular_velocity               = propeller.cruise.design_tip_mach *speed_of_sound/propeller.tip_radius
    propeller.cruise.design_lift_coefficient               = 0.7
    propeller.cruise.design_altitude                       = 1500 * Units.feet
    propeller.cruise.design_thrust                         = 3150
    propeller.rotation                                     = 1
    propeller.variable_pitch                               = True
    airfoil                                                = RCAIDE.Library.Components.Airfoils.Airfoil()
    airfoil.coordinate_file                                = airfoil_path + 'Airfoils' + separator + 'NACA_4412.txt'
    airfoil.polar_files                                    = [airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_50000.txt' ,
                                                             airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_100000.txt' ,
                                                             airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_200000.txt' ,
                                                             airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_500000.txt' ,
                                                             airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_1000000.txt',
                                                             airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_3500000.txt',
                                                             airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_5000000.txt',
                                                             airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_7500000.txt' ]
    propeller.append_airfoil(airfoil)
    propeller.airfoil_polar_stations                       = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    cruise_propulsor_1.rotor                               = propeller

    # Propeller Motor
    propeller_motor                                        = RCAIDE.Library.Components.Powertrain.Converters.DC_Motor()
    propeller_motor.efficiency                             = 0.95
    propeller_motor.tag                                    = 'propeller_motor_1'
    propeller_motor.origin                                 = [[6.583, 1.300,  1.092 ]]
    propeller_motor.nominal_voltage                        = cruise_bus.voltage
    propeller_motor.no_load_current                        = 0.001
    propeller_motor.wing_tag                               = 'horizontal_tail'
    cruise_propulsor_1.motor                               = propeller_motor

    # rear propeller nacelle
    propeller_nacelle                = RCAIDE.Library.Components.Nacelles.Stack_Nacelle()
    propeller_nacelle.tag            = 'propeller_nacelle'
    propeller_nacelle.length         = 1.24
    propeller_nacelle.diameter       = 0.4
    propeller_nacelle.origin        = [[5.583,  1.300 ,    1.092]]
    propeller_nacelle.flow_through   = False

    nac_segment                    = RCAIDE.Library.Components.Nacelles.Segments.Segment()
    nac_segment.tag                = 'segment_1'
    nac_segment.percent_x_location = 0.0
    nac_segment.height             = 0.0
    nac_segment.width              = 0.0
    propeller_nacelle.append_segment(nac_segment)

    nac_segment                    = RCAIDE.Library.Components.Nacelles.Segments.Segment()
    nac_segment.tag                = 'segment_2'
    nac_segment.percent_x_location = 0.10/propeller_nacelle.length
    nac_segment.height             = 0.2
    nac_segment.width              = 0.2
    propeller_nacelle.append_segment(nac_segment)

    nac_segment                    = RCAIDE.Library.Components.Nacelles.Segments.Segment()
    nac_segment.tag                = 'segment_2'
    nac_segment.percent_x_location = 0.15 /propeller_nacelle.length
    nac_segment.height             = 0.25
    nac_segment.width              = 0.25
    propeller_nacelle.append_segment(nac_segment)


    nac_segment                    = RCAIDE.Library.Components.Nacelles.Segments.Segment()
    nac_segment.tag                = 'segment_4'
    nac_segment.percent_x_location = 0.2/propeller_nacelle.length
    nac_segment.height             = 0.3
    nac_segment.width              = 0.3
    propeller_nacelle.append_segment(nac_segment)


    nac_segment                    = RCAIDE.Library.Components.Nacelles.Segments.Segment()
    nac_segment.tag                = 'segment_5'
    nac_segment.percent_x_location = 0.25/propeller_nacelle.length
    nac_segment.height             = 0.35
    nac_segment.width              = 0.35
    propeller_nacelle.append_segment(nac_segment)

    nac_segment                    = RCAIDE.Library.Components.Nacelles.Segments.Segment()
    nac_segment.tag                = 'segment_6'
    nac_segment.percent_x_location = 0.5/propeller_nacelle.length
    nac_segment.height             = 0.4
    nac_segment.width              = 0.4
    propeller_nacelle.append_segment(nac_segment)

    nac_segment                    = RCAIDE.Library.Components.Nacelles.Segments.Segment()
    nac_segment.tag                = 'segment_7'
    nac_segment.percent_x_location = 0.75/propeller_nacelle.length
    nac_segment.height             = 0.35
    nac_segment.width              = 0.35
    propeller_nacelle.append_segment(nac_segment)

    nac_segment                    = RCAIDE.Library.Components.Nacelles.Segments.Segment()
    nac_segment.tag                = 'segment_8'
    nac_segment.percent_x_location = 0.98/propeller_nacelle.length
    nac_segment.height             = 0.3
    nac_segment.width              = 0.3
    propeller_nacelle.append_segment(nac_segment)

    nac_segment                    = RCAIDE.Library.Components.Nacelles.Segments.Segment()
    nac_segment.tag                = 'segment_9'
    nac_segment.percent_x_location = 1.0
    nac_segment.height             = 0.0
    nac_segment.width              = 0.0
    propeller_nacelle.append_segment(nac_segment)
    cruise_propulsor_1.nacelle = propeller_nacelle

    design_electric_rotor(cruise_propulsor_1)


    network.propulsors.append(cruise_propulsor_1)

    # make and append copy of forward propulsor (efficient coding)
    cruise_propulsor_2                             = deepcopy(cruise_propulsor_1)
    cruise_propulsor_2.tag                         = 'cruise_propulsor_2'
    cruise_propulsor_2.rotor.origin                = [[6.583, -1.300,  1.092 ]]
    propeller_nacelle_2                            = deepcopy(propeller_nacelle)
    propeller_nacelle_2.tag                        = 'propeller_nacelle_2'
    propeller_nacelle_2.origin                     = [[5.583, - 1.300,     1.092]]
    cruise_propulsor_2.nacelle                     = propeller_nacelle_2
    network.propulsors.append(cruise_propulsor_2)
    cruise_bus.assigned_propulsors = [['cruise_propulsor_1','cruise_propulsor_2' ]]

    #------------------------------------------------------------------------------------------------------------------------------------
    # Additional Bus Loads
    #------------------------------------------------------------------------------------------------------------------------------------

    # Avionics
    avionics                       = RCAIDE.Library.Components.Powertrain.Systems.Avionics()
    avionics.power_draw            = 10. # Watts
    avionics.mass_properties.mass  = 1.0 * Units.kg
    cruise_bus.avionics            = avionics

    # append forward bus
    network.busses.append(cruise_bus)


    #====================================================================================================================================
    # Lift Bus
    #====================================================================================================================================
    lift_bus                                               = RCAIDE.Library.Components.Powertrain.Distributors.Electrical_Bus()
    lift_bus.tag                                           = 'lift_bus'
    lift_bus.number_of_battery_modules =  1

    #------------------------------------------------------------------------------------------------------------------------------------
    # Bus Battery
    #------------------------------------------------------------------------------------------------------------------------------------
    battery_module                                                    = RCAIDE.Library.Components.Powertrain.Sources.Battery_Modules.Lithium_Ion_NMC()
    battery_module.tag                                                = 'lift_bus_battery'
    battery_module.electrical_configuration.series                    = 140
    battery_module.origin                                             = [[4.2, 0.0, 0.0]]
    battery_module.electrical_configuration.parallel                  = 20
    battery_module.geometric_configuration.normal_count              = 140
    battery_module.geometric_configuration.parallel_count            = 20
    for _ in range( lift_bus.number_of_battery_modules):
        lift_bus.battery_modules.append(deepcopy(battery_module))
    lift_bus.initialize_bus_properties()

    #------------------------------------------------------------------------------------------------------------------------------------
    # Lift Propulsors
    #------------------------------------------------------------------------------------------------------------------------------------

    # Define Lift Propulsor Container
    lift_propulsor_1                                       = RCAIDE.Library.Components.Powertrain.Propulsors.Electric_Rotor()
    lift_propulsor_1.wing_mounted                          = True

    # Electronic Speed Controller
    lift_rotor_esc                                         = RCAIDE.Library.Components.Powertrain.Modulators.Electronic_Speed_Controller()
    lift_rotor_esc.efficiency                              = 0.95
    lift_rotor_esc.origin                                  = [[-0.073 ,  1.950 , 1.2]]
    lift_rotor_esc.bus_voltage                             = lift_bus.voltage
    lift_propulsor_1.electronic_speed_controller           = lift_rotor_esc

    # Lift Rotor Design
    lift_rotor                                             = RCAIDE.Library.Components.Powertrain.Converters.Lift_Rotor()
    lift_rotor.origin                                      = [[-0.073 ,  1.950 , 1.2]]
    lift_rotor.active                                      = True
    lift_rotor.tip_radius                                  = 2.8/2
    lift_rotor.hub_radius                                  = 0.1
    lift_rotor.number_of_blades                            = 3
    lift_rotor.hover.design_altitude                       = 40 * Units.feet
    lift_rotor.hover.design_thrust                         = Hover_Load/8
    lift_rotor.hover.design_freestream_velocity            = np.sqrt(lift_rotor.hover.design_thrust/(2*1.2*np.pi*(lift_rotor.tip_radius**2)))
    lift_rotor.oei.design_altitude                         = 40 * Units.feet
    lift_rotor.oei.design_thrust                           = Hover_Load/7
    lift_rotor.oei.design_freestream_velocity              = np.sqrt(lift_rotor.oei.design_thrust/(2*1.2*np.pi*(lift_rotor.tip_radius**2)))
    airfoil                                                = RCAIDE.Library.Components.Airfoils.Airfoil()
    airfoil.coordinate_file                       =  airfoil_path + 'Airfoils' + separator + 'NACA_4412.txt'
    airfoil.polar_files                           = [airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_50000.txt' ,
                                                     airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_100000.txt' ,
                                                     airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_200000.txt' ,
                                                     airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_500000.txt' ,
                                                     airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_1000000.txt',
                                                     airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_3500000.txt',
                                                     airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_5000000.txt',
                                                     airfoil_path + 'Airfoils' + separator + 'Polars' + separator + 'NACA_4412_polar_Re_7500000.txt' ]
    lift_rotor.append_airfoil(airfoil)
    lift_rotor.airfoil_polar_stations                      = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    lift_propulsor_1.rotor =  lift_rotor

    #------------------------------------------------------------------------------------------------------------------------------------
    # Lift Rotor Motor
    #------------------------------------------------------------------------------------------------------------------------------------
    lift_rotor_motor                                       = RCAIDE.Library.Components.Powertrain.Converters.DC_Motor()
    lift_rotor_motor.efficiency                            = 0.9
    lift_rotor_motor.nominal_voltage                       = lift_bus.voltage*3/4
    lift_rotor_motor.propeller_radius                      = lift_rotor.tip_radius
    lift_rotor_motor.no_load_current                       = 0.01
    lift_propulsor_1.motor                                 = lift_rotor_motor



    #------------------------------------------------------------------------------------------------------------------------------------
    # Lift Rotor Nacelle
    #------------------------------------------------------------------------------------------------------------------------------------
    nacelle                           = RCAIDE.Library.Components.Nacelles.Nacelle()
    nacelle.length                    = 0.45
    nacelle.diameter                  = 0.3
    nacelle.orientation_euler_angles  = [0,-90*Units.degrees,0.]
    nacelle.flow_through              = False
    nacelle.origin                    = [[  -0.073,  1.950, 1.2]]
    lift_propulsor_1.nacelle          =  nacelle

    design_electric_rotor(lift_propulsor_1)

    network.propulsors.append(lift_propulsor_1)


    # Front Rotors Locations
    origins = [[  -0.073,  1.950, 1.2], [-0.073  , -1.950  , 1.2],[ 4.440 ,  1.950 , 1.2], [ 4.440  , -1.950  , 1.2],
               [ 0.219 ,  4.891 , 1.2], [ 0.219  , - 4.891 , 1.2], [ 4.196 ,  4.891 , 1.2], [ 4.196  , - 4.891 , 1.2]]
    orientation_euler_angles = [[10.0*Units.degrees,np.pi/2.,0.],[-10.0* Units.degrees,np.pi/2.,0.], [10.0* Units.degrees,np.pi/2.,0.], [-10.0* Units.degrees,np.pi/2.,0.],
                                [10.0* Units.degrees,np.pi/2.,0.], [-10.0* Units.degrees,np.pi/2.,0.], [10.0* Units.degrees,np.pi/2.,0.], [-10.0* Units.degrees,np.pi/2.,0.]] # vector of angles defining default orientation of rotor


    assigned_propulsor_list = []
    for i in range(len(origins)):
        propulsor_i                                       = deepcopy(lift_propulsor_1)
        propulsor_i.tag                                   = 'lift_propulsor_' + str(i + 1)
        propulsor_i.rotor.tag                             = 'lift_rotor_' + str(i + 1)
        propulsor_i.rotor.origin                          = [origins[i]]
        propulsor_i.rotor.orientation_euler_angle         = orientation_euler_angles[i]
        propulsor_i.motor.tag                             = 'lift_rotor_motor_' + str(i + 1)
        propulsor_i.motor.origin                          = [origins[i]]
        propulsor_i.electronic_speed_controller.tag       = 'lift_rotor_esc_' + str(i + 1)
        propulsor_i.electronic_speed_controller.origin    = [origins[i]]
        propulsor_i.nacelle.tag                           = 'lift_rotor_nacelle_' + str(i + 1)
        propulsor_i.nacelle.origin                        = [origins[i]]
        network.propulsors.append(propulsor_i)
        assigned_propulsor_list.append(propulsor_i.tag)
    lift_bus.assigned_propulsors = [assigned_propulsor_list]

    #------------------------------------------------------------------------------------------------------------------------------------
    # Additional Bus Loads
    #------------------------------------------------------------------------------------------------------------------------------------                                                = payload

    # Avionics
    avionics                                                = RCAIDE.Library.Components.Powertrain.Systems.Avionics()
    avionics.power_draw                                     = 10. # Watts
    avionics.mass_properties.mass                           = 1.0 * Units.kg
    lift_bus.avionics                                       = avionics


    network.busses.append(lift_bus)

    # append energy network
    vehicle.append_energy_network(network)
    return vehicle

Configurations Setup#

The ``configs_setup`` function defines the different vehicle configurations (referred to as configs) used during the simulation. Configurations allow for modifications to the baseline vehicle, such as altering control surface settings, without redefining the entire vehicle.


1. Base Configuration#

The base configuration serves as the foundation for all other configurations. It is defined to match the baseline vehicle created in the vehicle_setup function. Configurations in RCAIDE are created as containers using RCAIDE Data classes. These classes provide additional functionality, such as the ability to append new configurations or modifications.


2. Remaining Configurations#

The remaining configurations, such as forward, transition, and vertical, follow a similar pattern:

  • Forward

  • Transition

  • Vertical

Each configuration is built upon the previous one or the base configuration, ensuring modularity and easy customization.


[3]:
def configs_setup(vehicle):

    configs = RCAIDE.Library.Components.Configs.Config.Container()

    base_config                                                       = RCAIDE.Library.Components.Configs.Config(vehicle)
    base_config.tag                                                   = 'base'
    configs.append(base_config)

    forward_config                                                    = RCAIDE.Library.Components.Configs.Config(vehicle)
    forward_config.tag                                                = 'forward_flight'
    forward_config.networks.electric.busses['lift_bus'].active        = False
    configs.append(forward_config)

    transition_config                                                 = RCAIDE.Library.Components.Configs.Config(vehicle)
    transition_config.tag                                             = 'transition_flight'
    configs.append(transition_config)


    vertical_config                                                   = RCAIDE.Library.Components.Configs.Config(vehicle)
    vertical_config.tag                                               = 'vertical_flight'
    vertical_config.networks.electric.busses['cruise_bus'].active = False
    configs.append(vertical_config)

    return configs

Base Analysis#

The ``base_analysis`` function defines the analyses required for evaluating the aircraft. Each analysis addresses a specific aspect of the vehicle’s performance or characteristics. Below are the key analyses, their purpose, and considerations for their use.


1. Weights Analysis#

The weights analysis calculates the distribution of the aircraft’s weight across various components. This method is based on empirical correlations designed for tube-and-wing transport aircraft configurations.

  • Provides a breakdown of component weights (e.g., wings, fuselage, engines).

  • While informative, the results of this analysis are not directly used in the performance evaluation.


2. Aerodynamics Analysis#

The aerodynamics analysis evaluates the aerodynamic performance of the aircraft. It uses RCAIDE’s fidelity zero method:

  • Fidelity Zero: This is RCAIDE’s baseline aerodynamic analysis method, suitable for subsonic transport aircraft.

  • Similar to aerodynamic methods found in conceptual design texts.

  • Provides estimates for lift, drag, and other aerodynamic coefficients.

Note: Higher-fidelity aerodynamic methods are available for more detailed analyses if needed.


3. Stability Analysis#

The stability analysis calculates stability derivatives for the aircraft. While it is not used in the current mission setup, it can be run post-mission for checks or additional analysis.

  • Like the aerodynamic method, it uses fidelity zero for baseline stability analysis.

  • Applicable for basic stability checks of subsonic transport aircraft.


4. Energy Analysis#

The energy analysis runs the energy network attached to the vehicle. For this turboprop-powered aircraft:

  • The analysis evaluates the turboprop energy network.

  • Ensures the propulsion system behavior, such as thrust and fuel consumption, is accounted for.


5. Planet Analysis#

The planet analysis defines the planetary environment the vehicle operates in. This setup allows for the attachment of an atmospheric model.


6. Atmosphere Analysis#

The atmosphere analysis sets the atmospheric conditions for the simulation. A common choice is the US 1976 Standard Atmosphere, which provides:

  • Standard temperature, pressure, and density profiles with altitude.

  • Consistent atmospheric conditions for performance evaluations.


[4]:

def base_analysis(vehicle): # ------------------------------------------------------------------ # Initialize the Analyses # ------------------------------------------------------------------ analyses = RCAIDE.Framework.Analyses.Vehicle() analyses.vehicle = vehicle # Geometry geometry = RCAIDE.Framework.Analyses.Geometry.Geometry() geometry.settings.overwrite_reference = True geometry.settings.update_wing_properties = True analyses.append(geometry) # ------------------------------------------------------------------ # Weights weights = RCAIDE.Framework.Analyses.Weights.Electric_VTOL() weights.aircraft_type = "VTOL" analyses.append(weights) # ------------------------------------------------------------------ # Aerodynamics Analysis aerodynamics = RCAIDE.Framework.Analyses.Aerodynamics.Vortex_Lattice_Method() aerodynamics.training.Mach = np.array([0.1 ,0.3, 0.5, 0.65 , 0.85, 1.3]) analyses.append(aerodynamics) # ------------------------------------------------------------------ # Stability Analysis stability = RCAIDE.Framework.Analyses.Stability.Vortex_Lattice_Method() analyses.append(stability) # ------------------------------------------------------------------ # Energy energy = RCAIDE.Framework.Analyses.Energy.Energy() analyses.append(energy) # ------------------------------------------------------------------ # Planet Analysis planet = RCAIDE.Framework.Analyses.Planets.Earth() analyses.append(planet) # ------------------------------------------------------------------ # Atmosphere Analysis atmosphere = RCAIDE.Framework.Analyses.Atmospheric.US_Standard_1976() analyses.append(atmosphere) # done! return analyses

Analyses Setup#

The ``analyses_setup`` function assigns a set of analyses to each vehicle configuration. Analyses are used to evaluate the aircraft’s performance, aerodynamics, energy systems, and other characteristics for a given configuration.


1. Overview of Analyses Assignment#

In this tutorial, all configurations share the same set of analyses. However, this function provides the flexibility to assign a unique set of analyses to any specific configuration.


2. Purpose of Analyses Assignment#

The analyses ensure that the defined vehicle configurations (e.g., cruise, takeoff, landing) are evaluated correctly during the simulation. Each configuration can have:

  • Common Analyses: Shared across multiple configurations for simplicity.

  • Custom Analyses: Tailored to a specific phase of flight or performance evaluation.


3. Typical Analyses Included#

The following analyses are typically assigned to each configuration:

  • Weights Analysis: Computes weight distribution across components.

  • Aerodynamics Analysis: Estimates lift, drag, and aerodynamic coefficients.

  • Stability Analysis: Evaluates stability derivatives for flight control assessments.

  • Energy Analysis: Runs the energy network (e.g., turboprop engine) for thrust and fuel performance.

  • Atmosphere Analysis: Sets atmospheric conditions using standard atmospheric models.

By assigning these analyses, the vehicle’s behavior under different configurations (e.g., cruise, takeoff, landing) can be comprehensively evaluated.


4. Customizing Analyses#

To assign a custom analysis set for a specific configuration:

  1. Define a new analysis function tailored to the desired evaluation.

  2. Replace the default analyses for the target configuration by calling the custom function.

For example, the takeoff configuration might use a modified aerodynamic analysis to account for flap and slat deployment.


[5]:
def analyses_setup(configs):

    analyses = RCAIDE.Framework.Analyses.Analysis.Container()

    # build a base analysis for each config
    for tag,config in configs.items():
        analysis = base_analysis(config)
        analyses[tag] = analysis

    return analyses

Mission Setup#

The ``mission_setup`` function defines the mission profile used to compute the aircraft’s performance. A mission profile consists of sequential segments that represent different phases of flight, such as climb, cruise, and descent.


1. Mission Profile Overview#

A mission profile is made up of individual flight segments. Each segment specifies the aircraft’s flight conditions, such as:

  • Altitude

  • Speed

  • Range

  • Time

These segments are simulated sequentially, allowing for a detailed performance analysis of the vehicle across all phases of flight.


2. Segments in the Mission Profile#

Common segments in a mission profile include:

  • Taxi: Ground movement of the aircraft before takeoff and after landing.

  • Takeoff: Acceleration and lift-off phase with high-lift devices deployed.

  • Climb: Gradual ascent to cruise altitude, often with reduced flap/slat deployment.

  • Cruise: Level flight at a constant altitude and speed for fuel-efficient operation.

  • Descent: Controlled reduction in altitude as the aircraft prepares for landing.

  • Landing: Final phase of flight with maximum flap and slat deployment for touchdown.

Each segment defines specific performance conditions and parameters, such as speed, altitude, and duration.

For more information on the mission solver and its implementation, refer to the relevant RCAIDE documentation.


[6]:

# ------------------------------------------------------------------ # Baseline Mission Setup # ------------------------------------------------------------------ def mission_setup(analyses): # ------------------------------------------------------------------ # Initialize the Mission # ------------------------------------------------------------------ mission = RCAIDE.Framework.Mission.Sequential_Segments() mission.tag = 'baseline_mission' # unpack Segments module Segments = RCAIDE.Framework.Mission.Segments # base segment base_segment = Segments.Segment() Vstall = 80 * Units.kts #------------------------------------------------------------------------------------------------------------------------------------ # Vertical Climb #------------------------------------------------------------------------------------------------------------------------------------ segment = Segments.Vertical_Flight.Climb(base_segment) segment.tag = "Vertical_Climb" segment.analyses.extend( analyses.vertical_flight ) segment.altitude_start = 0.0 * Units.ft segment.altitude_end = 50. * Units.ft segment.initial_battery_state_of_charge = 1.0 segment.climb_rate = 500. * Units['ft/min'] # define flight dynamics to model segment.flight_dynamics.force_z = True # define flight controls segment.assigned_control_variables.throttle.active = True segment.assigned_control_variables.throttle.assigned_propulsors = [['lift_propulsor_1','lift_propulsor_2','lift_propulsor_3','lift_propulsor_4', 'lift_propulsor_5','lift_propulsor_6','lift_propulsor_7','lift_propulsor_8']] mission.append_segment(segment) #------------------------------------------------------------------------------------------------------------------------------------ # High-Speed Climbing Transition #------------------------------------------------------------------------------------------------------------------------------------ segment = Segments.Climb.Constant_Acceleration_Constant_Pitchrate_Constant_Angle(base_segment) segment.tag = "High_Speed_Climbing_Transition" segment.analyses.extend( analyses.transition_flight) segment.true_course = 0 * Units.degree segment.altitude_start = 50.0 * Units.ft segment.altitude_end = 500.0 * Units.ft segment.climb_angle = 3 * Units.degrees segment.acceleration = 0.25 * Units['m/s/s'] segment.pitch_initial = 2. * Units.degrees segment.pitch_final = 7. * Units.degrees # define flight dynamics to model segment.flight_dynamics.force_x = True segment.flight_dynamics.force_z = True # define flight controls segment.assigned_control_variables.throttle.active = True segment.assigned_control_variables.throttle.assigned_propulsors = [['cruise_propulsor_1','cruise_propulsor_2'], ['lift_propulsor_1','lift_propulsor_2','lift_propulsor_3','lift_propulsor_4', 'lift_propulsor_5','lift_propulsor_6','lift_propulsor_7','lift_propulsor_8']] mission.append_segment(segment) #------------------------------------------------------------------------------------------------------------------------------------ # Second Climb #------------------------------------------------------------------------------------------------------------------------------------ segment = Segments.Climb.Linear_Speed_Constant_Rate(base_segment) segment.tag = "Climb" segment.analyses.extend( analyses.forward_flight) segment.altitude_start = 500.0 * Units.ft segment.altitude_end = 1000. * Units.ft segment.climb_rate = 300. * Units['ft/min'] segment.air_speed_end = 110 * Units['mph'] # define flight dynamics to model segment.flight_dynamics.force_x = True segment.flight_dynamics.force_z = True # define flight controls segment.assigned_control_variables.throttle.active = True segment.assigned_control_variables.throttle.assigned_propulsors = [['cruise_propulsor_1','cruise_propulsor_2']] segment.assigned_control_variables.pitch_angle.active = True mission.append_segment(segment) #------------------------------------------------------------------------------------------------------------------------------------ # Cruise #------------------------------------------------------------------------------------------------------------------------------------ segment = Segments.Cruise.Constant_Speed_Constant_Altitude(base_segment) segment.tag = "Cruise" segment.analyses.extend( analyses.forward_flight ) segment.altitude = 1000.0 * Units.ft segment.air_speed = 110. * Units['mph'] segment.distance = 40 *Units.nmi # define flight dynamics to model segment.flight_dynamics.force_x = True segment.flight_dynamics.force_z = True # define flight controls segment.assigned_control_variables.throttle.active = True segment.assigned_control_variables.throttle.assigned_propulsors = [['cruise_propulsor_1','cruise_propulsor_2']] segment.assigned_control_variables.pitch_angle.active = True mission.append_segment(segment) #------------------------------------------------------------------------------------------------------------------------------------ # Descent #------------------------------------------------------------------------------------------------------------------------------------ segment = Segments.Climb.Linear_Speed_Constant_Rate(base_segment) segment.tag = "Descent" segment.analyses.extend(analyses.forward_flight) segment.altitude_start = 1000.0 * Units.ft segment.altitude_end = 500. * Units.ft segment.climb_rate = -200. * Units['ft/min'] segment.air_speed_start = 90 * Units.kts segment.air_speed_end = Vstall # define flight dynamics to model segment.flight_dynamics.force_x = True segment.flight_dynamics.force_z = True # define flight controls segment.assigned_control_variables.throttle.active = True segment.assigned_control_variables.throttle.assigned_propulsors = [['cruise_propulsor_1','cruise_propulsor_2']] segment.assigned_control_variables.pitch_angle.active = True mission.append_segment(segment) #------------------------------------------------------------------------------------------------------------------------------------ # High-Speed Descending Transition #------------------------------------------------------------------------------------------------------------------------------------ segment = Segments.Climb.Constant_Acceleration_Constant_Pitchrate_Constant_Angle(base_segment) segment.tag = "Descending_Transition" segment.analyses.extend( analyses.transition_flight ) segment.altitude_start = 500.0 * Units.ft segment.altitude_end = 50.0 * Units.ft segment.climb_angle = 3. * Units.degrees segment.acceleration = -0.5 segment.pitch_initial = 2 * Units.degrees segment.pitch_final = 5. * Units.degrees # define flight dynamics to model segment.flight_dynamics.force_x = True segment.flight_dynamics.force_z = True # define flight controls segment.assigned_control_variables.throttle.active = True segment.assigned_control_variables.throttle.assigned_propulsors = [['cruise_propulsor_1','cruise_propulsor_2'], ['lift_propulsor_1','lift_propulsor_2','lift_propulsor_3','lift_propulsor_4', 'lift_propulsor_5','lift_propulsor_6','lift_propulsor_7','lift_propulsor_8'] ] mission.append_segment(segment) #------------------------------------------------------------------------------------------------------------------------------------ # Low Speed Approach Transition #------------------------------------------------------------------------------------------------------------------------------------ segment = Segments.Cruise.Constant_Acceleration_Constant_Pitchrate_Constant_Altitude(base_segment) segment.tag = "Low_Speed_Approach_Transition" segment.analyses.extend( analyses.transition_flight ) segment.altitude = 50. * Units.ft segment.air_speed_start = 36.5 * Units['mph'] segment.air_speed_end = 50. * Units['ft/min'] segment.acceleration = -0.25 * Units['m/s/s'] segment.pitch_initial = 5. * Units.degrees segment.pitch_final = 2. * Units.degrees # define flight dynamics to model segment.flight_dynamics.force_x = True segment.flight_dynamics.force_z = True # define flight controls segment.assigned_control_variables.throttle.active = True segment.assigned_control_variables.throttle.assigned_propulsors = [['cruise_propulsor_1','cruise_propulsor_2'], ['lift_propulsor_1','lift_propulsor_2','lift_propulsor_3','lift_propulsor_4', 'lift_propulsor_5','lift_propulsor_6','lift_propulsor_7','lift_propulsor_8']] mission.append_segment(segment) #------------------------------------------------------------------------------------------------------------------------------------ # Vertical Descent #------------------------------------------------------------------------------------------------------------------------------------ segment = Segments.Vertical_Flight.Descent(base_segment) segment.tag = "Vertical_Descent" segment.analyses.extend( analyses.vertical_flight) segment.altitude_start = 50.0 * Units.ft segment.altitude_end = 0. * Units.ft segment.descent_rate = 300. * Units['ft/min'] # define flight dynamics to model segment.flight_dynamics.force_z = True # define flight controls segment.assigned_control_variables.throttle.active = True segment.assigned_control_variables.throttle.assigned_propulsors = [['lift_propulsor_1','lift_propulsor_2','lift_propulsor_3','lift_propulsor_4', 'lift_propulsor_5','lift_propulsor_6','lift_propulsor_7','lift_propulsor_8']] mission.append_segment(segment) return mission

Missions Setup#

The missions_setup function is responsible for setting up a list of missions. This allows multiple missions to be incorporated if desired, but only one is used here.

  1. Initialize Missions Object: It creates an empty Missions object from the RCAIDE.Framework.Mission module.

  2. Tag the Mission: It assigns the tag 'base_mission' to the provided mission object. This tag is used to identify the mission.

  3. Add Mission to List: It adds the tagged mission to the Missions object.

  4. Return Missions Object: Finally, it returns the Missions object, which now contains the tagged mission.


[7]:

def missions_setup(mission): missions = RCAIDE.Framework.Mission.Missions() # base mission mission.tag = 'base_mission' missions.append(mission) return missions

Plot Mission#

The last function in this file is used to plot the performance results from the mission evaluation. The results shown are not an exhaustive list of RCAIDE outputs, and custom plotting routines can be created.

[8]:
def plot_results(results):

    # Plots fligh conditions
    plot_flight_conditions(results)

    # Plot arcraft trajectory
    plot_flight_trajectory(results)

    # Plot Aerodynamic Coefficients
    plot_aerodynamic_coefficients(results)

    # Plot Aircraft Stability
    plot_longitudinal_stability(results)

    # Plot Aircraft Electronics
    plot_battery_temperature(results)
    plot_battery_cell_conditions(results)
    plot_battery_degradation(results)
    plot_electric_propulsor_efficiencies(results)

    # Plot Propeller Conditions
    plot_rotor_conditions(results)

    # Plot Battery Degradation
    plot_battery_degradation(results)

    return

Save Aircraft Geometry#

``save_aircraft_geometry`` is a function that saves an aircraft geometry to a pickle (.pkl) file. This is useful for saving the geometry to a file for later use.

[9]:
def save_aircraft_geometry(geometry,filename):
    pickle_file  = filename + '.pkl'
    with open(pickle_file, 'wb') as file:
        pickle.dump(geometry, file)
    return

Load Aircraft Geometry#

``load_aircraft_geometry`` is a function that loads an aircraft geometry from a pickle (.pkl)file. This geometry may have been created earlier or in a separate file.

[10]:

def load_aircraft_geometry(filename): load_file = filename + '.pkl' with open(load_file, 'rb') as file: results = pickle.load(file) return results

Load Rotor#

``load_rotor`` is a function that loads a rotor from a file. This rotor may have been created earlier or in a separate file. Documentation for the load function can be found in the RCAIDE documentation.

[11]:
def load_rotor(filename):
    rotor =  load(filename)
    return rotor

Save Rotor#

``save_rotor`` is a function that saves a rotor to a file. This is useful for saving the rotor to a file for later use.


[12]:

def save_rotor(rotor, filename): save(rotor, filename) return

Main Script#

The main script is used to call each of the functions defined above to execute the mission. A main script is used to run the functions for increased readability and maintainability.

[13]:
# vehicle data
vehicle  = vehicle_setup()

# Set up configs
configs  = configs_setup(vehicle)

# vehicle analyses
analyses = analyses_setup(configs)

# mission analyses
mission  = mission_setup(analyses)
missions = missions_setup(mission)

results = missions.base_mission.evaluate()

# plot the results
plot_results(results)

# plot vehicle
plot_3d_vehicle(vehicle,
                show_figure                 = False
                )
Solving Vertical_Climb:  |          | 0/8 segs [00:00<?, ?seg/s]
Lift-rotor Optimization Successful
Simulation Time: 0.18 mins

 Optimum motor design failed. Using slack constraints

 Warning: Max Fuel or Max Zero Fuel not defined. Iterating to find these values.

Performing Weights Analysis
--------------------------------------------------------
Propulsion Architecture: Electric
Aircraft Type          : VTOL
Method                 : Physics_Based

=== WEIGHT BREAKDOWN REPORT ===

Structural Components:
Component                    Weight (kg)
----------------------------------------
Wings                             142.26
Fuselage                          142.31
Empennage                           0.00
Landing Gear                       45.86
Nacelle                             0.00
Booms                             155.19
Paint                               0.00
----------------------------------------
Total                             485.62

Propulsion Components:
Component                    Weight (kg)
----------------------------------------
Engines                            69.40
Thrust Reversers                    0.00
Miscellaneous                      35.20
Fuel System                         0.00
Fuel Tanks                          0.00
Electrical Cabling                 45.44
Thermal Management                  0.00
Battery                           839.73
Motors                            592.05
----------------------------------------
Total                            1581.82

Systems:
Component                    Weight (kg)
----------------------------------------
Control Systems                     0.00
Apu                                 0.00
Electrical                          0.00
Avionics                           16.50
Hydraulics                          0.00
Furnishings                         0.00
Air Conditioner                     0.00
Instruments                         0.00
----------------------------------------
Total                              16.50

Payload Breakdown:
Component                    Weight (kg)
----------------------------------------
Passengers                          0.00
Baggage                             0.00
Cargo                               0.00
----------------------------------------
Total                               0.00

Operational Items Breakdown:
Component                    Weight (kg)
----------------------------------------
Misc                                0.00
Flight Crew                         0.00
Flight Attendants                   0.00
Passenger Service                   0.00
----------------------------------------
Total                               0.00

Overall Summary:
Metric                       Weight (kg)
----------------------------------------
Operating Empty Weight           2083.94
Payload Weight                      0.00
Fuel Weight                         0.00
Takeoff Weight                   2083.94
Zero Fuel Weight                 2083.94
Max Takeoff Weight               2700.00

===============================


 Creating aerodynamic surrogate ...

 Computing neutral point ...

          +----------------------------------------------------+
          |              MISSION SOLVER INITIATED              |
          +----------------------------------------------------+

Solving Vertical_Descent:  |██████████| 8/8 segs [06:32<00:00, 49.05s/seg]
2026-07-17 23:57:04.335 ( 452.370s) [    7F2EE89CEB80]vtkXOpenGLRenderWindow.:1460  WARN| bad X server connection. DISPLAY=
2026-07-17 23:57:04.335 ( 452.370s) [    7F2EE89CEB80]vtkOpenGLRenderWindow.c:645   WARN| Failed to load EGL! Please install the EGL library from your distribution's package manager.
2026-07-17 23:57:04.336 ( 452.371s) [    7F2EE89CEB80]vtkOSOpenGLRenderWindow:149   WARN| libOSMesa not found. It appears that OSMesa is not installed in your system. Please install the OSMesa library from your distribution's package manager.
2026-07-17 23:57:04.337 ( 452.372s) [    7F2EE89CEB80]vtkOpenGLRenderWindow.c:645   WARN| Failed to load EGL! Please install the EGL library from your distribution's package manager.
2026-07-17 23:57:04.337 ( 452.372s) [    7F2EE89CEB80]vtkOSOpenGLRenderWindow:149   WARN| libOSMesa not found. It appears that OSMesa is not installed in your system. Please install the OSMesa library from your distribution's package manager.
[13]:
<pyvista.plotting.plotter.Plotter at 0x7f2e7d212850>
findfont: Failed to find font weight heavy, now using 700.
Segment did not converge. Segment Tag: Descending_Transition
Error Message:
Singular matrix C in LSQ subproblem
Segment did not converge. Segment Tag: Low_Speed_Approach_Transition
Error Message:
Singular matrix C in LSQ subproblem
Segment did not converge. Segment Tag: Vertical_Descent
Error Message:
Singular matrix C in LSQ subproblem
../../_images/tutorials_Missions_Tutorial_07_lift_cruise_evtol_aircraft_26_6.png
../../_images/tutorials_Missions_Tutorial_07_lift_cruise_evtol_aircraft_26_7.png
../../_images/tutorials_Missions_Tutorial_07_lift_cruise_evtol_aircraft_26_8.png
../../_images/tutorials_Missions_Tutorial_07_lift_cruise_evtol_aircraft_26_9.png
../../_images/tutorials_Missions_Tutorial_07_lift_cruise_evtol_aircraft_26_10.png
../../_images/tutorials_Missions_Tutorial_07_lift_cruise_evtol_aircraft_26_11.png
../../_images/tutorials_Missions_Tutorial_07_lift_cruise_evtol_aircraft_26_12.png
../../_images/tutorials_Missions_Tutorial_07_lift_cruise_evtol_aircraft_26_13.png
../../_images/tutorials_Missions_Tutorial_07_lift_cruise_evtol_aircraft_26_14.png
../../_images/tutorials_Missions_Tutorial_07_lift_cruise_evtol_aircraft_26_15.png
../../_images/tutorials_Missions_Tutorial_07_lift_cruise_evtol_aircraft_26_16.png