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()
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])