Beispiel #1
0
class Propulsion(object):
    def __init__(self):
        self.numberOfEngines = 1
        self.engine = TurbofanEngine()
        self.CGx = None
        self.CGy = None
        self.CGz = None
        self.numberOfTanks = 1
        self.sfcModel = None

    def load(self, name, buildTable=False):
        self.engine.load(name)
        self._process_data(buildTable)

    def _process_data(self, buildTable=False):
        self.totalThrust = self.engine.thrustMC * self.numberOfEngines
        self._calc_cg()
        if buildTable:
            path = MyPaths()
            thrustTablePath = "%s//ThrustTable_%s.txt" % (path.db.wdir, self.engine.name)
            if os.path.isfile(thrustTablePath):
                # print 'loading thrust table'
                self._load_thrust_table(thrustTablePath)
            else:
                # print 'building thrust table'
                self._build_thrust_table()

    def _calc_cg(self):
        if hasattr(self.CGx, "__iter__"):
            mass = ones(self.numberOfEngines) * self.engine.mass
            self.totalMass, self.CG = get_total_cg(mass, self.CGx, self.CGy, self.CGz)

    def get_sfc_direct(self, Mach, altitude, thrustReq):
        Mach = float(Mach)
        altitude = float(altitude)
        thrustReq = float(thrustReq) / self.numberOfEngines  # thrust per engine

        sfcPerEngine = self.engine.get_sfc(Mach, altitude, thrustReq)
        return sfcPerEngine * self.numberOfEngines / 3600.0  # kg/(N*sec)

    def get_sfc2(self, Mach, altitude, thrustReq):
        Mach = self._normMach.normalize(Mach)
        alt = self._normAlt.normalize(altitude)
        Treq = self._normTreq.normalize(thrustReq)
        return self.sfcModel(Mach, alt, Treq)

    def get_sfc(self, Mach, altitude, thrustReq):
        if self.sfcModel == None:
            return self.get_sfc_direct(Mach, altitude, thrustReq)
        else:
            return self.get_sfc2(Mach, altitude, thrustReq)

    def _load_thrust_table(self, path):
        self._get_thrust_table_limits()
        data = read_tabulated_data_with_header(path)
        self._normMach = Normalization(data["Mach"].min(), data["Mach"].max(), 0, 1)
        self._normAlt = Normalization(data["altitude"].min(), data["altitude"].max(), 0, 1)
        self._normTreq = Normalization(data["ThrustRequired"].min(), data["ThrustRequired"].max(), 0, 1)
        normMach = self._normMach.normalize(data["Mach"])
        normAlt = self._normAlt.normalize(data["altitude"])
        normTreq = self._normTreq.normalize(data["ThrustRequired"])
        self.sfcModel = Rbf(normMach, normAlt, normTreq, data["sfc"])

    def _get_thrust_table_limits(self):
        n = 10
        MachMin = 0.05
        MachMax = 1.0
        altMin = 0
        altMax = 2e4
        Tmc = self.totalThrust
        Tmin = 0.05 * Tmc
        Tmax = Tmc
        self._normMach = Normalization(MachMin, MachMax, 0, 1)
        self._normAlt = Normalization(altMin, altMax, 0, 1)
        self._normTreq = Normalization(Tmin, Tmax, 0, 1)
        return n, MachMin, MachMax, altMin, altMax, Tmin, Tmax

    def _build_thrust_table(self):
        n, MachMin, MachMax, altMin, altMax, Tmin, Tmax = self._get_thrust_table_limits()
        MachList = linspace(MachMin, MachMax, n)
        altList = linspace(altMin, altMax, n)
        TreqList = linspace(Tmin, Tmax, n)
        nsfc = n * n * n
        sfc = ones(nsfc)
        Mach = ones(nsfc)
        altitude = ones(nsfc)
        thrustReq = ones(nsfc)

        i = 0
        for M in MachList:
            for alt in altList:
                for Treq in TreqList:
                    _sfc = self.get_sfc_direct(M, alt, Treq)
                    if not isnan(_sfc):
                        sfc[i] = _sfc
                        Mach[i] = M  # self._normMach.normalize(M)
                        altitude[i] = alt  # self._normAlt.normalize(alt)
                        thrustReq[i] = Treq  # self._normTreq.normalize(Treq)
                        i += 1
                        # print '%d\t%.4f\t%.0f\t%.0f\t%.6f'%(i, M, alt, Treq, _sfc)

        path = MyPaths()
        thrustTablePath = "%s//ThrustTable_%s.txt" % (path.db.wdir, self.engine.name)
        fid = open(thrustTablePath, "wt")
        fid.write("Mach\taltitude\tThrustRequired\tsfc\n")
        for M, alt, Treq, _sfc in zip(Mach[:i], altitude[:i], thrustReq[:i], sfc[:i]):
            fid.write("%.32f\t%.32e\t%.32e\t%.32e\n" % (M, alt, Treq, _sfc))
        fid.close()

        normMach = self._normMach.normalize(Mach[:i])
        normAlt = self._normAlt.normalize(altitude[:i])
        normTreq = self._normTreq.normalize(thrustReq[:i])
        self.sfcModel = Rbf(normMach, normAlt, normTreq, sfc[:i])

    def __call__(self, Mach, altitude, powerSetting):
        """ NOTE: powerSetting is linear function - will be replaced """
        thrustReq = self.engine.thrustMC * float(powerSetting) / 100.0
        return self.get_sfc(Mach, altitude, thrustReq)

    def get_thrust_available(self, altitude):
        atm = ISAtmosphere(altitude)
        rho0 = 1.2255
        return atm.density / rho0 * self.totalThrust

    def test(self):
        plt.figure()
        plt.hold(True)
        legend = list()
        alt = 1e4
        Mach = linspace(0.1, 1, 200)
        Tmc = self.totalThrust
        Pset = linspace(0.05 * Tmc, Tmc, 5)
        sfc = ones([len(Pset), len(Mach)])
        sfc2 = ones([len(Pset), len(Mach)])

        for i, p in enumerate(Pset):
            for j, M in enumerate(Mach):
                sfc[i, j] = self.get_sfc_direct(M, alt, p)
                sfc2[i, j] = self.get_sfc2(M, alt, p)

            plt.plot(Mach, sfc[i], marker="*")
            plt.plot(Mach, sfc2[i])

            legend.append("%.0f" % p)
            legend.append("%.0frbf" % p)
        plt.legend(legend)
        plt.show()
Beispiel #2
0
class DesignFormulation(design.Design):
    def setup(self):
        self.neval = 0
        self.lb = np.array([40, 40, 6.00, 3.00, 0.5, 1.00, 3.000, -4, -4])
        #self.lb = np.array([40, 40, 6.00, 3.00, 0.5, 1.00, 3.000, -4, -4])
        self.ub = np.array([60, 60, 7.50, 5.25, 1.8, 1.80, 3.200,  0,  0])
        #self.ub = np.array([60, 60, 7.50, 5.25, 1.8, 1.80, 3.200,  0,  0])
        self.x0 = np.array([55, 55, 6.91, 4.15, 1.1, 1.44, 3.115,  0, -3])
        self.norm = Normalization(self.lb, self.ub,-1.,1.)
        self.xCurrent = np.zeros(len(self.x0))
        self.x0norm = self.norm.normalize(self.x0)
        #self.bnds = np.array([[l,u] for l,u in zip(self.lb,self.ub)])
        # --- constraints ---
        self.WemptyMax      = 3500.0
        self.CnbMin         = 0.003
        self.ClbMax         = -0.075 #-6.0e-4
        self.SMmin          = 0.05
        self.SMmax          = 0.15
        self.combatRadiusMin= 750.0
        self.RCmin          = 125.0
        self.VmaxMin        = 0.90 # Mach
        self.minSectionLen  = 1.4*self.propulsion.engine.length
        self.CmdeReq        = -0.01 # baseline -0.00866
        self.alphaTrimMax   = 8.0+1e-4 # deg
        self.elevTrimMax    = 20.0
        self.elevTrimMin    = -20.0
        self.gvfmAero = True
        self.Vtrim = 65.0 # 135kts: assumed value
        self._load_rbf_models()
        # --- payload ---
        self.SDB = MassComponent('drop payload', 1132.0, np.array([4.5, 0.0, 0.12]))
        # --- misc ---
        self.analysisData = np.zeros(10)
#        self._upd_approximation()
        # --- initial data ---
        self.Wf  = self.mass.fuel.mass
        self.CGf = self.mass.fuel.coords
        self.engineOffset = 0.75*self.propulsion.engine.length
        self.payloadOffset = 0.75*self.propulsion.engine.length
        self.fileFeasible = 'ucav_feasible.txt'
        self.set_x(self.x0norm)        
    
    def get_bounds(self,x0,c1=0.3):
        n = len(self.x0norm)
        #c1 = 0.25
        treshold = 0.005
        xUold = np.ones(n)
        xLold = -np.ones(n)
        xUnew = np.zeros(n)
        xLnew = np.zeros(n)
        u = xUold-x0
        l = x0 - xLold
        delta = np.zeros(len(self.x0))
        i=0
        for _l,_u in zip(l,u):
            delta[i] = min([_l,_u])*c1
            if delta[i]<treshold:
                d = c1*(xUold[i]-xLold[i])
                if _u<treshold:
                    xLnew[i] = x0[i] - d
                    xUnew[i] = x0[i]
                else:
                    xLnew[i] = x0[i]
                    xUnew[i] = x0[i] + d
            else:
                xUnew[i] = x0[i] + delta[i]
                xLnew[i] = x0[i] - delta[i]
            print '%.4f\t%.4f\t%.4f'%(_l,_u,delta[i])
            i += 1
        bounds = np.transpose(np.vstack([xLnew,xUnew]))
        return bounds
        
#        
#    def _upd_approximation(self):
#        pathIn = 'design_out4.txt'
#        pathInSamples = 'DOE_LHS250_FFD2_3_3.txt'
#        learnData = read_tabulated_data_without_header(pathIn)
#        xNorm = read_tabulated_data_without_header(pathInSamples)
#        learnData = np.transpose(learnData)
#        _Cnb     = RbfMod(xNorm, (learnData[2])*1e3)
#        self.Cnb = lambda x: _Cnb(x)/1e3
#        self.Clb = RbfMod(xNorm, learnData[3])
#        self.LD  = RbfMod(xNorm, learnData[0])

    def set_x(self,xnorm,fullUpdate=True):
        """
        To minimize function evaluation, this function re-calculates analysis if 
        new x is given, otherwise precalculated results are used.
        """
        self.xcurNorm = xnorm
        x = self.norm.denormalize(xnorm)
        try:
            self._upd_configuration(x)
            self._upd_analysis(xnorm,fullUpdate)
        except:
            print x
            print xnorm
            self.display_2d()
            self._upd_configuration(x)
            print 'Error occured'
            self._upd_analysis(xnorm,fullUpdate)
#            self.analysisData[0] = 0.0
#            self.analysisData[1:] = -np.ones(len(self.analysisData)-1)*100.0
#            raise ValueError
    
    def _upd_configuration(self,x):
        sweep1 = x[0]
        sweep2 = x[1]
        chord1 = x[2]
        chord2 = x[3]
        chord3 = x[4]
        span1  = x[5]
        span2  = x[6]
        twist1 = x[7]
        twist2 = x[8]

        self.set_spans([span1,span2])
        self.set_sweep_angles([sweep1, sweep2])
        self.set_chords([chord1,chord2,chord3])
        self.set_twist_by_index(twist1,0)
        self.set_twist_by_index(twist2,1)
        self._set_engine_location()
        self._update_mass()
    
    def _set_engine_location(self):
        engineDiameter = self.propulsion.engine.diameter
        x,l = self.wing.get_max_segment_length(engineDiameter)
        self.maxSectionLength = l
        cgNew = x+self.engineOffset
        cgNew2 = x+self.payloadOffset
        #self.mass.empty.update_item_cg('engine',cgNew,0,0)
        #self.set_engine_cg(cgNew,0,0)
        self.SDB = MassComponent('drop payload', 1132.0, np.array([cgNew2, 0.0, 0.12]))
        #self.mass.payload.update_item_cg('drop payload',cgNew,.0,.0)
        
    
    def _upd_analysis(self,xnorm,fullUpdate):
        #self._update_mass()
        self._upd_drag()
        alt = self.designGoals.cruiseAltitude
        aero = self.get_aero_single_point(0.7,1e4,2.0)
        self.analysisData[0] = aero.LD

        if fullUpdate:
        # mission
            self.mass.set_fuel_mass(self.Wf, self.CGf)
            if not self.mass.payload.item_exists(self.SDB.name):
                self.mass.payload.add_component(self.SDB)
            self.mass.set_fuel_mass(self.Wf,self.CGf)
            self.combatRadius = run_mission_B15(self) /1e3
            # performance
            self.mass.set_fuel_mass(self.Wf,self.CGf)
            if not self.mass.payload.item_exists(self.SDB.name):
                self.mass.payload.add_component(self.SDB)

            slf = SteadyLevelFlight(self)
            self.analysisData[1] = self.mass.empty()
            self.analysisData[2] = aero.derivs.Cnb
            self.analysisData[3] = aero.derivs.Clb  #self.aero.derivs.Clb
            self.analysisData[4] = aero.SM
            self.analysisData[5] = self.combatRadius
            self.analysisData[7] = slf.run_max_TAS(alt).Mach
            aeroTrim = self.get_aero_trim(self.Vtrim, 0.0)
            self.analysisData[8] = aeroTrim.elevator
            self.analysisData[9] = aeroTrim.alpha

            self.mass.set_fuel_mass(self.Wf,self.CGf)
            if not self.mass.payload.item_exists(self.SDB.name):
                self.mass.payload.add_component(self.SDB)
            clm = ClimbDescent(self)
            self.analysisData[6] = clm.run_max_climb_rate(0).climbRate
        print self.analysisData[0]
    

    def f(self,x):
        self.neval += 1
        self.set_x(x,False)
        return -self.analysisData[0]

    def g(self,x):
        self.set_x(x,True)
        _x = self.norm.denormalize(x)
        g = np.zeros(12)
        g[0] = self.WemptyMax - self.analysisData[1]
        g[1] = self.analysisData[2] - self.CnbMin
        g[2] = self.ClbMax - self.analysisData[3]
        g[3] = self.SMmax - self.analysisData[4]
        g[4] = self.analysisData[4] - self.SMmin
        g[5] = self.analysisData[5] - self.combatRadiusMin
        g[6] = self.analysisData[6] - self.RCmin
        g[7] = self.analysisData[7] - self.VmaxMin
        g[8] = self.maxSectionLength - self.minSectionLen
        g[9] = self.analysisData[8] - self.elevTrimMin
        g[10] = _x[0] - _x[1]
        g[11] = self.alphaTrimMax - self.analysisData[9]
        print g>0
        out = ''
        for v in g:
            out += '%.0e '%v
        print out
        print '%.4f\t%.4f\t%.4f'%(self.analysisData[1],self.analysisData[5],self.analysisData[8])

        g[0] *= 1e-2
        g[1] *= 1e4
        g[2] *= 1e3
        g[3] *= 1e3
        g[4] *= 1e3
        g[5] *= 1e-1
        g[6] *= 1e-1
        g[7] *= 1e2
        g[9] *= 10.
        g[11] *= 10
        return g*1000.
    
    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):
        aero = super(DesignFormulation,self).get_aero_single_point(velocity,altitude,alpha,beta,elevator,mass,cg,inertia,CD0)
        if self.gvfmAero:
            sys.stdout.write('GVFM add> ')
            xcurrent = self.xcurNorm
            LDold = aero.coef.CL/(aero.coef.CD-self.CD0rbf(xcurrent))
            aero.coef.CL += self.CLrbf(xcurrent)
            aero.coef.CD += self.CDrbf(xcurrent)-self.CD0rbf(xcurrent)
            aero.LD      = LDold + self.LDrbf(xcurrent)
            aero.SM      += self.SMrbf(xcurrent)
            aero.CD0     += self.CD0rbf(xcurrent)
            aero.k       += self.krbf(xcurrent)
        return aero
    
    def update_aero_trim(self,velocity,altitude,CmTrim=0.0,loadFactor=1.0,
                      mass=None,cg=None,inertia=None,CD0=None):
        super(DesignFormulation,self).update_aero_trim(velocity,altitude,CmTrim,loadFactor,mass,cg,inertia,CD0)
        if self.gvfmAero:
            xcurrent = self.xcurNorm
            #self.aeroResults.CD0     += self.CD0rbf(xcurrent)
            self.aeroResults.k       += self.krbf(xcurrent)

    def get_drag(self,velocity=None,altitude=None):
        cd0 = super(DesignFormulation,self).get_drag(velocity,altitude)
        if self.gvfmAero:
            return cd0 + self.CD0rbf(self.xcurNorm)
        else:
            return cd0

    def _load_rbf_models(self,DOEpath=None,rsltPath=None):

        if DOEpath==None:
            DOEpath = r'D:\1. Current work\2014 - UAV performance code\Results\DOE\LHS_dvar9_sample24.txt'
        if rsltPath==None:
            rsltPath = r'D:\1. Current work\2014 - UAV performance code\Results\DOE\CFDresults.txt'
        dCD0 = 0.0005
        xDOE = read_tabulated_data_without_header(DOEpath)
        fDOE = read_tabulated_data_without_header(rsltPath,1)
        self.CLrbf = RbfMod(xDOE, fDOE[:,4]-fDOE[:,13])
        self.CDrbf = RbfMod(xDOE, fDOE[:,5]-fDOE[:,14])
        self.CD0rbf= RbfMod(xDOE, fDOE[:,11]-fDOE[:,19]+dCD0)
        self.krbf  = RbfMod(xDOE, fDOE[:,12]-fDOE[:,20])
        self.SMrbf = RbfMod(xDOE, fDOE[:,10]-fDOE[:,18],0.5)
        self.LDrbf = RbfMod(xDOE, fDOE[:,7] -fDOE[:,15])
        self._xdoe = xDOE
    
    def show_results(self):
        print '{0:10}={1:10.4f}'.format('LD',self.analysisData[0])
        print '{0:10}={1:10.4f}'.format('SM',self.analysisData[4])
        print '{0:10}={1:10.4f}'.format('Cnb',self.analysisData[2])
        print '{0:10}={1:10.4f}'.format('Clb',self.analysisData[3])
        print '{0:10}={1:10.4f}'.format('elev',self.analysisData[8])
        print '{0:10}={1:10.4f}'.format('alpha',self.analysisData[9])
        print '{0:10}={1:10.4f}'.format('We',self.analysisData[1])
        print '{0:10}={1:10.4f}'.format('Rcombat',self.analysisData[5])
        print '{0:10}={1:10.4f}'.format('ROC',self.analysisData[6])
        print '{0:10}={1:10.4f}'.format('Mmax',self.analysisData[7])