Example #1
0
class FlyingWing(object):
    def __init__(self):
        self.wing = Wing()  # main geometry
        self.designGoals = DesignGoals()
        self.landingGear = LandingGear()
        self.vlm = VLMparameters()
        self.mass = AircraftMass()  # total mass list
        self.drag = None  # total drag list
        self.propulsion = Propulsion()  # table lookup database
        self.aeroResults = None
        self._dragAero09 = True  # if True then aero09 is used, otherwise Mason+Korn equation

    def load_xls(self, name, xlsPath=None):
        """
        Loads aircraft data from *.xls datasheet and calculates necessary 
        parameters:
        - full geometry data set
        - weight and cg
        - parasite drag curve: CD0 vs Mach
        - engine thrust table if it does not exist for selected engine
        
        Parameters
        ----------
        
        name : string
            name of aircraft in database
        xlsPath : path, optional
            database path
        """
        self.name = str(name)
        if xlsPath == None:
            xlsPath = path.db.aircraftFW
        keyword = "SECTION: "
        db = ReadDatabase(xlsPath, name, keyword)
        idx = db.find_header(keyword + "DESIGN QUANTITIES")
        self.type = db.read_row(idx + 1, 1)
        self.designGoals.fuelMass = db.read_row(-1, 1)
        self.designGoals.avionicsMass = db.read_row(-1, 1)
        self.designGoals.grossMass = db.read_row(-1, 1)
        self.designGoals.cruiseMach = db.read_row(-1, 1)
        self.designGoals.cruiseAltitude = db.read_row(-1, 1)
        self.designGoals.loadFactor = db.read_row(-1, 1)
        self.designGoals.loadFactorLanding = db.read_row(-1, 1)
        self.designGoals.staticThrust = db.read_row(-1, 1)
        self.designGoals.numberOfOccupants = db.read_row(-1, 1)
        idx = db.find_header(keyword + "FUSELAGE")
        self.fusWidth = db.read_row(idx + 1, 1, False)
        idx = db.find_header(keyword + "MAIN WING")
        self.wing.segSpans = db.read_row(idx + 1, 1, True)
        self.wing.chords = db.read_row(-1, 1, True)
        self.wing.airfoilNames = db.read_row(-1, 1, True)
        self.wing.secOffset = db.read_row(-1, 1, True)
        self.wing.segDihedral = db.read_row(-1, 1, True)
        self.wing.secTwist = db.read_row(-1, 1, True)
        self.wing.incidence = db.read_row(-1, 1, False)
        elevonLocation = db.read_row(-1, 1, True)
        flapLocation = db.read_row(-1, 1, True)
        self.wing.set_elevon(elevonLocation)
        self.wing.set_flap(flapLocation)
        self.wing.flap.type = db.read_row(-1, 1, False)
        self.wing.material = db.read_row(-1, 1, False)
        self.wing.fuelTankCGratio = db.read_row(-1, 1, True)
        idx = db.find_header(keyword + "PROPULSION")
        engineName = db.read_row(idx + 1, 1, False)
        self.propulsion.load(engineName, True)
        self.propulsion.sfcModel = None
        self.propulsion.numberOfEngines = int(db.read_row(-1, 1, False))
        self.propulsion.CGx = db.read_row(-1, 1, True)
        self.propulsion.CGy = db.read_row(-1, 1, True)
        self.propulsion.CGz = db.read_row(-1, 1, True)
        self.propulsion.engine.deignAltitude = self.designGoals.cruiseAltitude
        self.propulsion.engine.designMach = self.designGoals.cruiseMach
        self.propulsion.engine.designThrust = self.designGoals.staticThrust / self.propulsion.numberOfEngines
        idx = db.find_header(keyword + "LANDING GEAR")
        self.landingGear.groundContactX = db.read_row(idx + 1, 1, True)
        self.landingGear.groundContactY = db.read_row(-1, 1, True)
        self.landingGear.groundContactZ = db.read_row(-1, 1, True)
        self.landingGear.tireWidth = db.read_row(-1, 1, True)
        self.landingGear.tireDiameter = db.read_row(-1, 1, True)
        self.landingGear.strutLength = db.read_row(-1, 1, True)
        # self.landingGear._set_right_mlg()
        idx = db.find_header(keyword + "VLM PARAM")
        self.vlm.panelsChordwise = db.read_row(idx + 1, 1, False)
        self.vlm.panelsSpanwise = db.read_row(-1, 1, False)
        self.vlm.distribution = db.read_row(-1, 1, False)
        sec = db.read_section("PAYLOAD", 0)
        for line in sec:
            name = str(line[0])
            mass = float(line[1])
            CG = np.array([float(val) for val in line[2:5]])
            MOI = np.array([float(val) for val in line[5:8]])
            self.mass.payload.add_item(name, mass, CG, MOI)
        self.mass.update_total()
        self._process_data(True)
        #        fuelCG = self.wing.locate_on_wing(self.wing.fuelTankCGratio[0],self.wing.fuelTankCGratio[1])
        #        fuelCG[1] = 0.0
        #        self.mass.set_fuel_mass(self.designGoals.fuelMass,fuelCG)
        self.designGoals._process_data()
        self._update_parasite_drag()
        self._update_mass()

    def _process_data(self, updateAirfoils=False):
        self.wing._process_data(updateAirfoils)
        self.designGoals.set_flight_conditions(self.wing.MAC)
        self.designGoals._process_data()
        self.propulsion._process_data()

    def save_xls(self):
        """
        TBD.
        Saves configuration to *.xls datasheet
        """
        pass

    def display(self, showAxes=False):
        """
        Displays current aircraft configuration using mayavi. 
        
        Notes
        -----
        
        Method may produce an error depending on PC configuration. To fix the error 
        follow these steps:
        
        1. In tools->Preferences->Console->External modules
        set value of ETS_TOOLKIT to "wx"
        
        2. 	In C:/Python27/Lib/site-packages/tvtk/pyface/light_manager.py, 
        CameraLight class, _color_changed method, change: 
        
        >>> self.source.color = val
        
        to
        
        >>> self.color = val
        """
        flying_wing_display(self, showAxes)

    def display_2d(self, linetype="k-"):
        """
        Displays 2D planform shape of the wing.

        Parameters
        ----------
        
        linetype : string, optional
            matplotlib linetype format
        """
        plt.figure()
        plt.plot(self.wing.x2d, self.wing.y2d, linetype)
        plt.axis("equal")
        plt.show()

    def _update_parasite_drag(self):
        alt = self.designGoals.cruiseAltitude
        if self._dragAero09:
            M, CD, Mdd, CDdd = get_parasite_drag_fw(self, alt)
            self._M = np.hstack([M[0] - 0.2, M[0] - 0.1, M]) + 0.1
            self._CD = np.hstack([CD[0], CD[0], CD])
            self.Mdd = Mdd
            self.CDdd = CDdd
        else:
            M, CD = get_transonic_drag_wing(self.wing, Ka=0.90)
            self._M = M
            self._CD = CD
        self._dragCurve = Akima1DInterpolator(self._M, self._CD)

    def plot_drag(self):
        m = np.linspace(self._M[0], self._M[-1], 100)
        cd = np.array([self.get_drag(_m) for _m in m])
        plt.figure()
        plt.hold(True)
        # plt.plot(self._M, self._CD,'bs-')
        plt.plot(m, cd, "r-")
        plt.xlabel("Mach")
        plt.ylabel("Drag coefficient")
        plt.grid(True)
        plt.show()

    def _update_mass(self):
        self.mass = get_flying_wing_mass(self)

    #
    #    def get_parasite_drag(self,velocity=None,altitude=None):
    #        """
    #        Returns friction and form drag coefficient at given velocity and altitude.
    #        If velocity and altitude are not specified then values from design targets
    #        are used
    #
    #        Parameters
    #        ----------
    #
    #        velocity : float, m/sec
    #        altitude : float, m
    #        """
    #        self._update_parasite_drag()
    #        return self.drag.get_total_drag()

    def update_aero_trim(
        self, velocity, altitude, CmTrim=0.0, loadFactor=1.0, mass=None, cg=None, inertia=None, CD0=None
    ):
        """
        Updates results of aerodynamic analysis at trim condition using AVL solver. Stores the result 
        of analysis in self.aeroResults.
        
        Parameters
        ----------
        
        velocity : float, m/sec
            true airspeed of the aircraft. If velocity<5 then it is treated as Mach number, otherwise 
            airspeed in m/sec.
        altitude : float, m
            density altitude at which analysis is performed
        CmTrim : float
            Required moment coefficient. By default CmTrim=0 - no pitching moment.
        loadFactor : float
            load factor can be used for gust analysis: analysis is performed with mass= mass*loadFactor
        mass : float, kg
            aircraft mass. If value is not defined then total aircraft mass will be calculated.
        cg : array, m
            center of gravity in format array([x,y,z]). If value is not specified then value will be 
            calculated
        inertia : array, kg*m2
            aircraft moment of inertia in format array([Ixx, Iyy, Izz]). Required for dynamic 
            stability calculation.
        CD0 : float
            parasite drag coefficient. If value is not specified then value will be calculated.
        """
        aero = Aerodynamics(self)
        fc = FlightConditionsAVL(self, velocity, altitude, CmTrim, loadFactor, mass, cg, inertia, CD0)
        self.aeroResults = aero.run_trim(fc)

    def get_aero_trim(self, velocity, altitude, CmTrim=0.0, loadFactor=1.0, mass=None, cg=None, inertia=None, CD0=None):
        """
        Same as update_aero_trim, but returns results of analysis.
        """
        self.update_aero_trim(
            velocity, altitude, CmTrim=0.0, loadFactor=1.0, mass=None, cg=None, inertia=None, CD0=None
        )
        return self.aeroResults

    def get_aero_single_point(
        self, velocity, altitude, alpha=0.0, beta=0.0, elevator=0.0, mass=None, cg=None, inertia=None, CD0=None
    ):
        """
        Performs aerodynamic analysis using AVL at given aircraft configuration and flight condtions.
        
        
        Parameters
        ----------
        
        velocity : float, m/sec
            true airspeed of the aircraft. If velocity<5 then it is treated as Mach number, otherwise 
            airspeed in m/sec.
        altitude : float, m
            density altitude at which analysis is performed
        alpha : float, deg
            aircraft angle of attack
        beta : float, deg
            aircraft sideslip angle
        elevator : float, deg
            elevator deflection. positive direction is down.
        mass : float, kg
            aircraft mass. If value is not defined then total aircraft mass will be calculated.
        cg : array, m
            center of gravity in format array([x,y,z]). If value is not specified then value will be 
            calculated
        inertia : array, kg*m2
            aircraft moment of inertia in format array([Ixx, Iyy, Izz]). Required for dynamic 
            stability calculation.
        CD0 : float
            parasite drag coefficient. If value is not specified then value will be calculated.
        """
        aero = Aerodynamics(self)
        fc = FlightConditionsAVL(self, velocity, altitude, 0, 1, mass, cg, inertia, CD0)
        alpha = float(alpha)
        beta = float(beta)
        elevator = float(elevator)
        self.aeroResults = aero.run_single_point(fc, alpha, beta, elevator)
        return self.aeroResults

    def get_cg(self, update=True):
        """
        Returns center of gravity coordinates in format array([x,y,z]).
        
        Parameters
        ----------
        
        update : bool
            runs weight and balance analysis first. Use True if configuration has been changed since 
            last function call.
        """
        if update:
            self._update_mass()
        return self.mass.total.CG

    def get_mass(self, update=True):
        """
        Returns total mass (empty+payload) of current aircraft configuration.
        
        Parameters
        ----------
        
        update : bool
            runs weight and balance analysis first. Use True if configuration has been changed since 
            last function call.
        """
        if update:
            self._update_mass()
        return self.mass.total.totalMass

    def get_mass_empty(self, update=True):
        """
        Returns empty mass (airframe+propulsion) of current aircraft configuration.
        
        Parameters
        ----------
        
        update : bool
            runs weight and balance analysis first. Use True if configuration has been changed since 
            last function call.
        """
        if update:
            self._update_mass()
        return self.mass.empty.totalMass

    def set_drag_method(self, option=1):
        """
        Switches drag analysis methods.
        
        Parameters
        ----------
        
        option : int
            if 1 - Aero09, else Friction+Korn
        """
        if option == 1:
            self._dragAero09 = True
        else:
            self._dragAero09 = False
        self._update_parasite_drag()

    def get_drag(self, velocity=None, altitude=None):
        """
        Returns parasite drag value of current aircraft configuration. Calculation is performed using 
        in-house Aero09 code.
        
        Parameters
        ----------
        
        velocity : float, m/sec
            true airspeed of the aircraft. If velocity<5 then it is treated as Mach number, otherwise 
            airspeed in m/sec.
        altitude : float, m
            density altitude at which analysis is performed
        """
        if velocity == None:
            velocity = self.designGoals.cruiseSpeed
        if altitude == None:
            altitude = self.designGoals.cruiseAltitude
        fc = FlightConditions(velocity, altitude)
        cd = self._dragCurve(fc.Mach)
        return cd

    def get_inertia(self):
        """
        Returns moment of inertia. Now this analysis is not available, so function returns [1;1;1].
        """
        return np.zeros(3)  # FIXME: should be replaced by real calculation

    def get_sfc(self, velocity, altitude, thrustRequired):
        """
        Returns thrust specific fuel consumption (TSFC).
        
        Parameters
        ----------
        
        velocity : float, m/sec
            true airspeed of the aircraft. If velocity<5 then it is treated as Mach number, otherwise 
            airspeed in m/sec.
        altitude : float, m
            density altitude at which analysis is performed
        thrustRequired : float, N
            required thrust
        """
        fc = FlightConditions(velocity, altitude, 0.0, self.wing.MAC)
        sfc = self.propulsion.get_sfc(fc.Mach, altitude, thrustRequired)
        return sfc
Example #2
0
class FlyingWing(object):
    def __init__(self):
        self.wing = Wing() #main geometry
        self.designGoals = DesignGoals()
        self.landingGear = LandingGear()
        self.vlm = VLMparameters()
        self.mass = AircraftMass() #total mass list
        self.drag = None #total drag list
        self.propulsion = Propulsion() #table lookup database
        self.aeroResults = None
        self._dragAero09 = False # if True then aero09 is used, otherwise Mason+Korn equation

    def load_xls(self, name, xlsPath=None):
        """
        Loads aircraft data from *.xls datasheet and calculates necessary 
        parameters:
        - full geometry data set
        - weight and cg
        - parasite drag curve: CD0 vs Mach
        - engine thrust table if it does not exist for selected engine
        
        Parameters
        ----------
        
        name : string
            name of aircraft in database
        xlsPath : path, optional
            database path
        """
        self.name = str(name)
        if xlsPath==None:
            xlsPath = path.db.aircraftFW
        keyword = 'SECTION: '
        db = ReadDatabase(xlsPath, name, keyword)
        idx = db.find_header(keyword+'DESIGN QUANTITIES')
        self.type                          = db.read_row(idx+1,1)
        self.designGoals.fuelMass          = db.read_row(-1,1)
        self.designGoals.avionicsMass      = db.read_row(-1,1)
        self.designGoals.grossMass         = db.read_row(-1,1)
        self.designGoals.cruiseMach        = db.read_row(-1,1)
        self.designGoals.cruiseAltitude    = db.read_row(-1,1)
        self.designGoals.loadFactor        = db.read_row(-1,1)
        self.designGoals.loadFactorLanding = db.read_row(-1,1)
        #self.designGoals.staticThrust      = db.read_row(-1,1)
        #self.designGoals.numberOfOccupants = db.read_row(-1,1)
        missionProfile                     = db.read_row(-1,1)
        self.mission = mission_profile.load(missionProfile)
        idx = db.find_header(keyword+'FUSELAGE')
        self.fusWidth = db.read_row(idx+1,1,False)
        idx = db.find_header(keyword+'MAIN WING')
        self.wing.segSpans           = db.read_row(idx+1,1,True)
        self.wing.chords             = db.read_row(-1,1,True)
        self.wing.airfoilNames       = db.read_row(-1,1,True)
        self.wing.secOffset          = db.read_row(-1,1,True)
        self.wing.segDihedral        = db.read_row(-1,1,True)
        self.wing.secTwist           = db.read_row(-1,1,True)
        self.wing.incidence          = db.read_row(-1,1,False)
        elevonLocation               = db.read_row(-1,1,True)
        flapLocation                 = db.read_row(-1,1,True)
        self.wing.set_elevon(elevonLocation)
        self.wing.set_flap(flapLocation)
        self.wing.flap.type          = db.read_row(-1,1,False)
        #FIXME: CLmax3D is temporal, later calcluation of CLmax should be added
        self.CLmax3D                 = db.read_row(-1,1,True)
        self.wing.material           = db.read_row(-1,1,False)
        self.wing.fuelTankCGratio    = db.read_row(-1,1,True)
        idx = db.find_header(keyword+'PROPULSION')
        engineName                      = db.read_row(idx+1,1,False) #FIXME: table/direct analysis should be controlled explicitly
        self.propulsion.load(engineName,False)
        self.propulsion.sfcModel = None
        self.propulsion.numberOfEngines = int(db.read_row(-1,1,False))
        self.propulsion.CGx             = db.read_row(-1,1,True)
        self.propulsion.CGy             = db.read_row(-1,1,True)
        self.propulsion.CGz             = db.read_row(-1,1,True)
        self.propulsion.engine.deignAltitude = self.designGoals.cruiseAltitude
        self.propulsion.engine.designMach    = self.designGoals.cruiseMach
        self.propulsion.engine.designThrust  = self.designGoals.staticThrust / self.propulsion.numberOfEngines
        idx = db.find_header(keyword+'LANDING GEAR')
        self.landingGear.groundContactX = db.read_row(idx+1,1,True)
        self.landingGear.groundContactY = db.read_row(-1,1,True)
        self.landingGear.groundContactZ = db.read_row(-1,1,True)
        self.landingGear.tireWidth      = db.read_row(-1,1,True)
        self.landingGear.tireDiameter   = db.read_row(-1,1,True)
        self.landingGear.strutLength    = db.read_row(-1,1,True)
        #self.landingGear._set_right_mlg()
        idx = db.find_header(keyword+'VLM PARAM')
        self.vlm.panelsChordwise = db.read_row(idx+1,1,False)
        self.vlm.panelsSpanwise  = db.read_row(-1,1,False)
        self.vlm.distribution    = db.read_row(-1,1,False)
        sec = db.read_section('PAYLOAD',0)
        for line in sec:
            name = str(line[0])
            mass = float(line[1])
            CG   = np.array([float(val) for val in line[2:5]])
            MOI  = np.array([float(val) for val in line[5:8]])
            self.mass.payload.add_item(name,mass,CG,MOI)
        self.mass.update_total()
        self._process_data(True)
#        fuelCG = self.wing.locate_on_wing(self.wing.fuelTankCGratio[0],self.wing.fuelTankCGratio[1])
#        fuelCG[1] = 0.0
#        self.mass.set_fuel_mass(self.designGoals.fuelMass,fuelCG)
        #self.designGoals._process_data()
        self._update_parasite_drag()
        self._update_mass()

    def _process_data(self,updateAirfoils=False):
        self.wing._process_data(updateAirfoils)
        self.designGoals.set_flight_conditions(self.wing.MAC)
        self.designGoals._process_data()
        self.propulsion._process_data()

    def save_xls(self):
        """
        TBD.
        Saves configuration to *.xls datasheet
        """
        pass

    def display(self,showAxes=False):
        """
        Displays current aircraft configuration using mayavi. 
        
        Notes
        -----
        
        Method may produce an error depending on PC configuration. To fix the error 
        follow these steps:
        
        1. In tools->Preferences->Console->External modules
        set value of ETS_TOOLKIT to "wx"
        
        2. 	In C:/Python27/Lib/site-packages/tvtk/pyface/light_manager.py, 
        CameraLight class, _color_changed method, change: 
        
        >>> self.source.color = val
        
        to
        
        >>> self.color = val
        """
        flying_wing_display(self,showAxes)
    
    def display_2d(self,linetype='k-'):
        """
        Displays 2D planform shape of the wing.

        Parameters
        ----------
        
        linetype : string, optional
            matplotlib linetype format
        """
        plt.figure()
        plt.plot(self.wing.x2d, self.wing.y2d,linetype)
        plt.axis('equal')
        plt.show()
    
    def _update_parasite_drag(self):
        alt = self.designGoals.cruiseAltitude
        if self._dragAero09:
            M, CD, Mdd, CDdd = get_parasite_drag_fw(self,alt)
            self._M = np.hstack([M[0]-.2,M[0]-.1,M])+.1
            self._CD = np.hstack([CD[0],CD[0],CD])
            self.Mdd = Mdd
            self.CDdd = CDdd
        else:
            M, CD = get_transonic_drag_wing(self.wing,Ka=0.90)
            self._M = M
            self._CD = CD
        self._dragCurve = Akima1DInterpolator(self._M,self._CD)
    
    def plot_drag(self):
        m = np.linspace(self._M[0],self._M[-1],100)
        cd = np.array([self.get_drag(_m) for _m in m])
        plt.figure()
        plt.hold(True)
        #plt.plot(self._M, self._CD,'bs-')
        plt.plot(m,cd,'r-')
        plt.xlabel('Mach')
        plt.ylabel('Drag coefficient')
        plt.grid(True)
        plt.show()

    def _update_mass(self):
        self.mass = get_flying_wing_mass(self)
    
    def update_aero_trim(self,velocity,altitude,CmTrim=0.0,loadFactor=1.0,
                      mass=None,cg=None,inertia=None,CD0=None):
        """
        Updates results of aerodynamic analysis at trim condition using AVL solver. Stores the result 
        of analysis in self.aeroResults.
        
        Parameters
        ----------
        
        velocity : float, m/sec
            true airspeed of the aircraft. If velocity<5 then it is treated as Mach number, otherwise 
            airspeed in m/sec.
        altitude : float, m
            density altitude at which analysis is performed
        CmTrim : float
            Required moment coefficient. By default CmTrim=0 - no pitching moment.
        loadFactor : float
            load factor can be used for gust analysis: analysis is performed with mass= mass*loadFactor
        mass : float, kg
            aircraft mass. If value is not defined then total aircraft mass will be calculated.
        cg : array, m
            center of gravity in format array([x,y,z]). If value is not specified then value will be 
            calculated
        inertia : array, kg*m2
            aircraft moment of inertia in format array([Ixx, Iyy, Izz]). Required for dynamic 
            stability calculation.
        CD0 : float
            parasite drag coefficient. If value is not specified then value will be calculated.
        """
        aero = Aerodynamics(self)
        fc = FlightConditionsAVL(self,velocity,altitude,CmTrim,loadFactor,mass,
                                 cg,inertia,CD0)
        self.aeroResults = aero.run_trim(fc)
    
    def get_aero_trim(self,velocity,altitude,CmTrim=0.0,loadFactor=1.0,
                      mass=None,cg=None,inertia=None,CD0=None):
        """
        Same as update_aero_trim, but returns results of analysis.
        """
        self.update_aero_trim(velocity,altitude,CmTrim=0.0,loadFactor=1.0,
                              mass=None,cg=None,inertia=None,CD0=None)
        return self.aeroResults
    
    def get_aero_single_point(self,velocity=None,altitude=None,alpha=0.0,beta=0.0,
                              elevator=0.0,mass=None,cg=None,inertia=None,CD0=None):
        """
        Performs aerodynamic analysis using AVL at given aircraft configuration and flight condtions.
        
        
        Parameters
        ----------
        
        velocity : float, m/sec
            true airspeed of the aircraft. If velocity<5 then it is treated as Mach number, otherwise 
            airspeed in m/sec.
        altitude : float, m
            density altitude at which analysis is performed
        alpha : float, deg
            aircraft angle of attack
        beta : float, deg
            aircraft sideslip angle
        elevator : float, deg
            elevator deflection. positive direction is down.
        mass : float, kg
            aircraft mass. If value is not defined then total aircraft mass will be calculated.
        cg : array, m
            center of gravity in format array([x,y,z]). If value is not specified then value will be 
            calculated
        inertia : array, kg*m2
            aircraft moment of inertia in format array([Ixx, Iyy, Izz]). Required for dynamic 
            stability calculation.
        CD0 : float
            parasite drag coefficient. If value is not specified then value will be calculated.
        """
        if velocity==None:
            velocity = self.designGoals.cruiseSpeed
        if altitude==None:
            altitude = self.designGoals.cruiseAltitude
        aero = Aerodynamics(self)
        fc = FlightConditionsAVL(self,velocity,altitude,0,1,mass,cg,inertia,CD0)
        alpha = float(alpha)
        beta = float(beta)
        elevator = float(elevator)
        self.aeroResults = aero.run_single_point(fc,alpha,beta,elevator)
        return self.aeroResults

    def get_cg(self,update=True):
        """
        Returns center of gravity coordinates in format array([x,y,z]).
        
        Parameters
        ----------
        
        update : bool
            runs weight and balance analysis first. Use True if configuration has been changed since 
            last function call.
        """
        if update:
            self._update_mass()
        return self.mass.total.CG

    def get_mass(self,update=True):
        """
        Returns total mass (empty+payload) of current aircraft configuration.
        
        Parameters
        ----------
        
        update : bool
            runs weight and balance analysis first. Use True if configuration has been changed since 
            last function call.
        """
        if update:
            self._update_mass()
        return self.mass.total.totalMass

    def get_mass_empty(self,update=True):
        """
        Returns empty mass (airframe+propulsion) of current aircraft configuration.
        
        Parameters
        ----------
        
        update : bool
            runs weight and balance analysis first. Use True if configuration has been changed since 
            last function call.
        """
        if update:
            self._update_mass()
        return self.mass.empty.totalMass

    def get_mass_fuel(self,update=True):
        """
        Returns empty mass (airframe+propulsion) of current aircraft configuration.
        
        Parameters
        ----------
        
        update : bool
            runs weight and balance analysis first. Use True if configuration has been changed since 
            last function call.
        """
        if update:
            self._update_mass()
        return self.mass.fuel.mass   
      
#    def get_mass_fuel(self,update=True):
#        """
#        Returns fuel mass at given state.
#        
#        Parameters
#        ----------
#        
#        update : bool
#            runs weight and balance analysis first. Use True if configuration has been changed since 
#            last function call.
#        """
#        if update:
#            self._update_mass()
#        return self.mass.fuel.mass

    def set_drag_method(self,option=1):
        """
        Switches drag analysis methods.
        
        Parameters
        ----------
        
        option : int
            if 1 - Aero09, else Friction+Korn
        """
        if option==1:
            self._dragAero09 = True
        else:
            self._dragAero09 = False
        self._update_parasite_drag()

    def get_drag(self,velocity=None,altitude=None):
        """
        Returns parasite drag value of current aircraft configuration. Calculation is performed using 
        in-house Aero09 code.
        
        Parameters
        ----------
        
        velocity : float, m/sec
            true airspeed of the aircraft. If velocity<5 then it is treated as Mach number, otherwise 
            airspeed in m/sec.
        altitude : float, m
            density altitude at which analysis is performed
        """
        if velocity==None:
            velocity = self.designGoals.cruiseSpeed
        if altitude==None:
            altitude = self.designGoals.cruiseAltitude
        fc = FlightConditions(velocity,altitude)
        cd = self._dragCurve(fc.Mach)
        return cd*1.10
    
    def get_inertia(self):
        """
        Returns moment of inertia. Now this analysis is not available, so function returns [1;1;1].
        """
        return np.zeros(3) #FIXME: should be replaced by real calculation
    
    def get_sfc(self,velocity,altitude,thrustRequired=None):
        """
        Returns thrust specific fuel consumption (TSFC).
        
        Parameters
        ----------
        
        velocity : float, m/sec
            true airspeed of the aircraft. If velocity<5 then it is treated as Mach number, otherwise 
            airspeed in m/sec.
        altitude : float, m
            density altitude at which analysis is performed
        thrustRequired : float, N
            required thrust
        """
        if thrustRequired==None:
            thrustRequired = self.propulsion.get_thrust_available(altitude)
        fc = FlightConditions(velocity,altitude,0.0,self.wing.MAC)
        sfc = self.propulsion.get_sfc(fc.Mach,altitude,thrustRequired)
        return sfc
    
    def get_fuel_flow(self,velocity,altitude,thrustRequired=None):
        atm = FlightConditions(velocity,altitude)
        if thrustRequired==None:
            thrustRequired = self.propulsion.get_thrust_available(altitude)
        ff = self.propulsion.get_fuel_flow(atm.Mach,altitude,thrustRequired)
        return ff

    def get_mission_fuel(self,mission=None,iterMax=20,tol=1.0,display=False):
        if not self.mission.calculated:
            wfuel,mis = mission2.get_mission_fuel(self,mission, iterMax, tol, display)
            self.designGoals.fuelMass = wfuel
            self._update_mass()
            self.mission = mis
            self.mission.calculated = True
            return wfuel
        else:
            return self.designGoals.fuelMass
    
    def set_fuel_fraction(self,fuelMassFraction=1.0):
        fuelMassFraction = min([1.0,fuelMassFraction])
        fuelMassFraction = max([0.0,fuelMassFraction])
        self.mass.fuel.set_fuel_fraction(fuelMassFraction)
        self.mass.update_total()

    def get_CLmax3D(self,deflection, altitude=0.0, velocity=0.1):
        """
        Calculation of 3D CLmax coefficient based on Gudmunsson's GA design book p441.
        2D section analysis is performed here using Xfoil and javafoil for 
        plain flap only. The result need to be validated.
        
        Parameters
        ----------
        
        deflection : float, deg
            plain flap deflection angles
        
        altitude : float, m
            field altitude where analysis is performed. Typically should be 
            SL
        
        velocity : float, m/sec or Mach
            airspeed close to takeoff/landing speed
        """
        fc = FlightConditions(velocity,altitude)
        h = altitude
        V = velocity
        isFlapped = self.wing.isFlapped
        CLmaxFl = list()
        alphaFl = list()
        CL = np.zeros(len(isFlapped))
        for i,val in enumerate(isFlapped):
            if val==1:
                Cf = 1-self.wing.flap.location[i]
                _c, _a = self.wing.airfoils[i].get_clmax(V,h,Cf,deflection)
                CLmaxFl.append(_c)
                alphaFl.append(_a)
                CL[i] = _c
        CLmaxFl = np.mean(CLmaxFl)
        alphaFl = np.mean(alphaFl)
        #---
        for i,val in enumerate(isFlapped):
            if val==0:
                pol = self.wing.airfoils[i].get_xfoil_polar(fc.Mach,fc.Re,[-20,20,0.5])
                CL[i] = pol._alphaCl(alphaFl)
        #---
        Sref = self.wing.area
        Si   = self.wing.segAreas
        sum1 = np.sum( isFlapped*CL*Si )
        sum2 = np.sum( (1.0-isFlapped)*CL*Si )
        CLmax = 0.9/Sref* (sum1 + sum2)
        return 2.0*CLmax