def setup(self, **kwargs): self.wns = WingNoStruct() self.wb = WingBox(self.wns, "wing") Wwing = Variable('W_{wing}', 'N', 'Wing System Weight') Cwing = Variable('C_{wing}', 1, '-', 'Wing Weight Margin and Sensitivity Factor') # w.r.t. the quarter chord of the root of the wing. dxACwing = Variable('\\Delta x_{AC_{wing}}','m','Wing Aerodynamic Center Shift') #wing induced drag reduction due to wing tip devices TipReduct = Variable('TipReduct', '-', 'Induced Drag Reduction Factor from Wing Tip Devices') constraints = [] with SignomialsEnabled(): constraints.extend([ self.wns['\\lambda'] == self.wb['taper'], TCS([Wwing >= Cwing * self.wb['W_{struct}'] + self.wb['W_{struct}']*(self.wns['f_{flap}'] + \ self.wns['f_{slat}'] + self.wns['f_{aileron}'] + self.wns['f_{lete}'] + self.wns['f_{ribs}'] + \ self.wns['f_{spoiler}'] + self.wns['f_{watt}'])]), TCS([dxACwing <= 1./24.*(self.wns['c_{root}'] + 5.*self.wns['c_{tip}'])/self.wns['S'] \ *self.wns['b']**2*self.wns['\\tan(\\Lambda)']]), ]) return self.wns, self.wb, constraints
def setup(self, aircraft, state, **kwargs): self.aircraft = aircraft self.aircraftP = aircraft.dynamic(state) self.wingP = self.aircraftP.wingP self.fuseP = self.aircraftP.fuseP self.engineP = self.aircraftP.engineP #variable definitions z_bre = Variable('z_{bre}', '-', 'Breguet Parameter') Rng = Variable('Rng', 'nautical_miles', 'Cruise Segment Range') constraints = [] constraints.extend([ #steady level flight constraint on D self.aircraftP['D'] == aircraft['numeng'] * self.engineP['F'], #taylor series expansion to get the weight term TCS([self.aircraftP['W_{burn}']/self.aircraftP['W_{end}'] >= te_exp_minus1(z_bre, nterm=3)]), #breguet range eqn TCS([z_bre >= (self.engineP['TSFC'] * self.aircraftP['thr']* self.aircraftP['D']) / self.aircraftP['W_{avg}']]), #time self.aircraftP['thr'] * state['V'] == Rng, ]) return constraints, self.aircraftP
def setup(self, static, state, N=5): exec parse_variables(BladeElementProp.__doc__) with Vectorize(N): blade = BladeElementPerf(static, state) constraints = [ blade.dr == static.R / (N), blade.omega == omega, blade.r[0] == static.R / (2. * N) ] for n in range(1, N): constraints += [ TCS([blade.r[n] >= blade.r[n - 1] + static.R / N]), blade.eta_i[n] == blade.eta_i[n - 1], ] constraints += [ TCS([Q >= sum(blade.dQ)]), eta == state.V * T / (omega * Q), blade.M[-1] <= Mtip, static.T_m >= T, omega <= omega_max ] with SignomialsEnabled(): constraints += [TCS([T <= sum(blade.dT)])] return constraints, blade
def setup(self, wing, state, **kwargs): #new variables CL= Variable('C_{L}', '-', 'Lift Coefficient') Cdw = Variable('C_{d_w}', '-', 'Cd for a NC130 Airfoil at Re=2e7') Dwing = Variable('D_{wing}', 'N', 'Total Wing Drag') Lwing = Variable('L_{wing}', 'N', 'Wing Lift') CLaw = Variable('C_{L_{\\alpha,w}}', '-', 'Lift curve slope, wing') #constraints constraints = [] constraints.extend([ #airfoil drag constraint Lwing == (.5*wing['S']*state.atm['\\rho']*state['V']**2)*CL, TCS([Cdw**6.5 >= (1.02458748e10 * CL**15.587947404823325 * state['M']**156.86410659495155 + 2.85612227e-13 * CL**1.2774976672501526 * state['M']**6.2534328002723703 + 2.08095341e-14 * CL**0.8825277088649582 * state['M']**0.0273667615730107 + 1.94411925e+06 * CL**5.6547413360261691 * state['M']**146.51920742858428)]), TCS([Dwing >= (.5*wing['S']*state.atm['\\rho']*state['V']**2)*(Cdw + wing['K']*CL**2)]), CLaw == 5, ]) return constraints
def setup(self, aircraft, state, **kwargs): self.aircraft = aircraft self.aircraftP = AircraftP(aircraft, state) self.wingP = self.aircraftP.wingP self.fuseP = self.aircraftP.fuseP self.engineP = self.aircraftP.engineP #variable definitions z_bre = Variable('z_{bre}', '-', 'Breguet Parameter') Rng = Variable('Rng', 'nautical_miles', 'Cruise Segment Range') TotRng = Variable('TotRng', 'nautical_miles', 'Total Cruise Range') #new varibales for the TASOPT flight profile gammaCruise = Variable('\\gamma_{cruise}', '-', 'Cruise Climb Angle') RCCruise = Variable('RC_{cruise}', 'ft/min', 'Cruise Climb Rate') excessP = Variable('P_{excess}', 'W', 'Excess Power During Cruise') constraints = [] ## constraints.extend([ #taylor series expansion to get the weight term ## TCS([self.aircraftP['W_{burn}']/self.aircraftP['W_{end}'] >= ## te_exp_minus1(z_bre, nterm=3)]), ## ## #breguet range eqn ## TCS([z_bre >= (self.engineP['TSFC'] * self.aircraftP['thr']* ## self.aircraftP['D']) / self.aircraftP['W_{avg}']]), ## #time ## self.aircraftP['thr'] * state['V'] == Rng, ## ]) #new constarints for the TASOPT flight profile with SignomialsEnabled(): constraints.extend([ #compute the cruise climb angle state['\\rho']*self.aircraftP['V']**2 == gammaCruise * state["P_{atm}"] * self.aircraftP['M']**2, #compute the cruise climb rate self.aircraftP['V'] * gammaCruise == RCCruise, #constraint on drag and thrust self.aircraft['numeng']*self.engineP['thrust'] >= self.aircraftP['D'] + self.aircraftP['W_{avg}'] * gammaCruise, RCCruise == excessP/self.aircraftP['W_{avg}'], #climb rate constraints TCS([excessP + state['V'] * self.aircraftP['D'] <= state['V'] * aircraft['numeng'] * self.engineP['thrust']]), #time TCS([self.aircraftP['thr'] * state['V'] >= Rng + (.5*gammaCruise**2)*self.aircraftP['thr'] * state['V']]), ]) return constraints, self.aircraftP
def setup(self, aircraft): fs = FlightState() A = Variable("A", "m/s**2", "log fit equation helper 1") B = Variable("B", "1/m", "log fit equation helper 2") g = Variable("g", 9.81, "m/s**2", "gravitational constant") mu = Variable("\\mu", 0.5, "-", "Braking coefficient of friction") T_rev = Variable("T", "N", "Reverse thrust") cda = Variable("CDA", 0.024, "-", "parasite drag coefficient") CLg = Variable("C_{L_g}", "-", "ground lift coefficient") CDg = Variable("C_{D_g}", "-", "grag ground coefficient") Vstall = Variable("V_{stall}", "knots", "stall velocity") Sgr = Variable("S_{gr}", "ft", "landing ground roll") Slnd = Variable("S_{land}", "ft", "landing distance") etaprop = Variable("\\eta_{prop}", 0.05, "-", "propellor efficiency") msafety = Variable("m_{fac}", 1.4, "-", "Landing safety margin") CLland = Variable("C_{L_{land}}", 3.5, "-", "landing CL") cdp = Variable("c_{d_{p_{stall}}}", 0.025, "-", "profile drag at Vstallx1.2") V_ref = Variable("V_{ref}", "kts", "Approach reference speed") f_ref = Variable("f_{ref}", 1.3, "-", "Approach reference speed margin above stall") path = os.path.dirname(os.path.abspath(__file__)) df = pd.read_csv(path + os.sep + "logfit.csv") fd = df.to_dict(orient="records")[0] constraints = [ T_rev == aircraft["P_{shaft-max}"] * etaprop / fs["V"], Vstall == (2. * aircraft.topvar("W") / fs["\\rho"] / aircraft["S"] / CLland)**0.5, fs["V"] >= f_ref * Vstall, V_ref == fs["V"], Slnd >= msafety * Sgr, FitCS(fd, Sgr * 2 * B, [A / g, B * fs["V"]**2 / g]), ] with SignomialsEnabled(): constraints.extend([ TCS([(B * aircraft.topvar("W") / g + 0.5 * fs["\\rho"] * aircraft["S"] * CDg >= 0.5 * fs["\\rho"] * aircraft["S"] * mu * CLland)]), TCS([A / g <= T_rev / aircraft.topvar("W") + mu]), CDg <= cda + cdp + CLland**2 / pi / aircraft["AR"] / aircraft["e"], ]) return constraints, fs
def setup(self, aircraft, state, **kwargs): self.aircraft = aircraft self.aircraftP = AircraftP(aircraft, state) self.wingP = self.aircraftP.wingP self.fuseP = self.aircraftP.fuseP #variable definitions z_bre = Variable('z_{bre}', '-', 'Breguet Parameter') Rng = Variable('Rng', 'nautical_miles', 'Cruise Segment Range') constraints = [] constraints.extend([ #taylor series expansion to get the weight term TCS([ self.aircraftP['W_{burn}'] / self.aircraftP['W_{end}'] >= te_exp_minus1(z_bre, nterm=3) ]), #time self.aircraftP['thr'] * state['V'] == Rng, self.aircraftP['W_{burn}'] == self.aircraft.engine['TSFC'][2:] * self.aircraft.engine['F'][:2:] * self.aircraftP['thr'] ]) return constraints, self.aircraftP
def setup(self, aircraft, state, **kwargs): #make submodels self.aircraft = aircraft self.wingP = aircraft.wing.dynamic(state) self.fuseP = aircraft.fuse.dynamic(state) self.engineP = aircraft.engine.dynamic(state) self.Pmodels = [self.wingP, self.fuseP, self.engineP] #variable definitions Vstall = Variable('V_{stall}', 'knots', 'Aircraft Stall Speed') D = Variable('D', 'N', 'Total Aircraft Drag') W_avg = Variable('W_{avg}', 'N', 'Geometric Average of Segment Start and End Weight') W_start = Variable('W_{start}', 'N', 'Segment Start Weight') W_end = Variable('W_{end}', 'N', 'Segment End Weight') W_burn = Variable('W_{burn}', 'N', 'Segment Fuel Burn Weight') WLoadmax = Variable('W_{Load_{max}}', 'N/m^2', 'Max Wing Loading') WLoad = Variable('W_{Load}', 'N/m^2', 'Wing Loading') t = Variable('tmin', 'min', 'Segment Flight Time in Minutes') thours = Variable('thr', 'hour', 'Segment Flight Time in Hours') constraints = [] constraints.extend([ #speed must be greater than stall speed state['V'] >= Vstall, #Figure out how to delete Vstall == 120 * units('kts'), WLoadmax == 6664 * units('N/m^2'), #compute the drag TCS([D >= self.wingP['D_{wing}'] + self.fuseP['D_{fuse}']]), #constraint CL and compute the wing loading W_avg == .5 * self.wingP['C_{L}'] * self.aircraft['S'] * state.atm['\\rho'] * state['V']**2, WLoad == .5 * self.wingP['C_{L}'] * self.aircraft['S'] * state.atm['\\rho'] * state['V']**2 / self.aircraft.wing['S'], #set average weight equal to the geometric avg of start and end weight W_avg == (W_start * W_end)**.5, #constrain the max wing loading WLoad <= WLoadmax, #compute fuel burn from TSFC W_burn == aircraft['numeng'] * self.engineP['TSFC'] * thours * self.engineP['thrust'], #time unit conversion t == thours, #make lift equal weight --> small angle approx in climb self.wingP['L_{wing}'] == W_avg, ]) return constraints, self.wingP, self.engineP, self.fuseP
def setup(self, aircraft, state, **kwargs): #submodels self.aircraft = aircraft self.aircraftP = AircraftP(aircraft, state) self.wingP = self.aircraftP.wingP self.fuseP = self.aircraftP.fuseP self.engineP = self.aircraftP.engineP #variable definitions theta = Variable('\\theta', '-', 'Aircraft Climb Angle') excessP = Variable('P_{excess}', 'W', 'Excess Power During Climb') RC = Variable('RC', 'feet/min', 'Rate of Climb/Decent') dhft = Variable('dhft', 'feet', 'Change in Altitude Per Climb Segment [feet]') RngClimb = Variable('R_{climb}', 'nautical_miles', 'Down Range Distance Covered in Each Climb Segment') TotRngClimb = Variable('TotRngClimb', 'nautical_miles', 'Down Range Distance Covered in Climb') #constraints constraints = [] constraints.extend([ #constraint on drag and thrust self.aircraft['numeng']*self.engineP['thrust'] >= self.aircraftP['D'] + self.aircraftP['W_{avg}'] * theta, #climb rate constraints TCS([excessP + state['V'] * self.aircraftP['D'] <= state['V'] * aircraft['numeng'] * self.engineP['thrust']]), RC == excessP/self.aircraftP['W_{avg}'], RC >= 500*units('ft/min'), #make the small angle approximation and compute theta theta * state['V'] == RC, dhft == self.aircraftP['tmin'] * RC, #makes a small angle assumption during climb ## RngClimb == self.aircraftP['thr']*state['V'], ]) with SignomialsEnabled(): constraints.extend([ #time TCS([self.aircraftP['thr'] * state['V'] >= RngClimb + (.5*theta**2)*self.aircraftP['thr'] * state['V']]), ]) return constraints, self.aircraftP
def setup(self, aircraft, state, **kwargs): self.aircraft = aircraft self.aircraftP = AircraftP(aircraft, state) self.wingP = self.aircraftP.wingP self.fuseP = self.aircraftP.fuseP self.engineP = self.aircraftP.engineP #variable definitions z_bre = Variable('z_{bre}', '-', 'Breguet Parameter') Rng = Variable('Rng', 'nautical_miles', 'Cruise Segment Range') constraints = [] constraints.extend([ #steady level flight constraint on D self.aircraftP['D'] == aircraft['numeng'] * self.engineP['thrust'], #taylor series expansion to get the weight term TCS([ self.aircraftP['W_{burn}'] / self.aircraftP['W_{end}'] >= te_exp_minus1(z_bre, nterm=3) ]), #breguet range eqn # old version -- possibly unneeded numeng # TCS([z_bre >= (self.aircraft['numeng'] * self.engineP['TSFC'] * self.aircraftP['thr']* # self.aircraftP['D']) / self.aircraftP['W_{avg}']]), # new version -- needs to be thought through carefully # seems correct to me - I switched T to D below (steady level flight) but fogot #about the Negn term TCS([ z_bre >= (self.engineP['TSFC'] * self.aircraftP['thr'] * self.aircraftP['D']) / self.aircraftP['W_{avg}'] ]), #time self.aircraftP['thr'] * state['V'] == Rng, ]) return constraints, self.aircraftP
def setup(self): Wing.fillModel = None self.wing = Wing() W = Variable("W", "lbf", "aircraft weight") WS = Variable("W/S", "lbf/ft^2", "Aircraft wing loading") PW = Variable("P/W", "hp/lbf", "Aircraft shaft hp/weight ratio") Wpay = Variable("W_{pay}", 2 * 195, "lbf", "payload weight") hbatt = Variable("h_{batt}", 210, "W*hr/kg", "battery specific energy") etae = Variable("\\eta_{e}", 0.9, "-", "total electrical efficiency") Wbatt = Variable("W_{batt}", "lbf", "battery weight") Wwing = Variable("W_{wing}", "lbf", "wing weight") Pshaftmax = Variable("P_{shaft-max}", "W", "max shaft power") sp_motor = Variable("sp_{motor}", 7. / 9.81, "kW/N", 'Motor specific power') Wmotor = Variable("W_{motor}", "lbf", "motor weight") Wcent = Variable("W_{cent}", "lbf", "aircraft center weight") fstruct = Variable("f_{struct}", 0.2, "-", "structural weight fraction") Wstruct = Variable("W_{struct}", "lbf", "structural weight") e = Variable("e", 0.8, "-", "span efficiency factor") CL_max_clean = Variable("CL_{max_{clean}}", 1.6, "-", "Clean CL max") CL_max_to = Variable("CL_{max_{to}}", 2.0, "-", "Clean CL max") CL_max_aprch = Variable("CL_{max_{aprch}}", 2.4, "-", "Clean CL max") fbattmax = Variable("f_{batt,max}", 1.0, "-", "max battery fraction") loading = self.wing.spar.loading(self.wing) loading.substitutions.update({ loading.kappa: 0.05, self.wing.spar.material.sigma: 1.5e9, loading.Nmax: 6, self.wing.skin.material.tmin: 0.012 * 4, self.wing.mfac: 1.4, self.wing.spar.mfac: 0.8 }) constraints = [ Wcent == loading.W, WS == W / self.wing.planform.S, PW == Pshaftmax / W, TCS([W >= Wbatt + Wpay + self.wing.W + Wmotor + Wstruct]), Wcent >= Wbatt + Wpay + Wmotor + Wstruct, Wstruct >= fstruct * W, Wmotor >= Pshaftmax / sp_motor, Wbatt / W <= fbattmax, ] return constraints, self.wing, loading
def setup(self, Wstart, Wend, perf): z_bre = Variable("z_{bre}", "-", "Breguet coefficient") Wfuel = Variable("W_{fuel}", "lbf", "segment-fuel weight") g = Variable("g", 9.81, "m/s^2", "gravitational acceleration") R = Variable("R", "nautical_miles", "range") rhofuel = Variable("\\rho_{JetA}", 6.75, "lb/gallon", "Jet A fuel density") constraints = [ TCS([ z_bre >= (R * perf["\\dot{m}"] * rhofuel * g / perf["V"] / (Wend * Wstart)**0.5) ]), Wfuel / Wend >= te_exp_minus1(z_bre, 3), Wstart >= Wend + Wfuel ] return constraints
def setup(self, perf): z_bre = Variable("z_{bre}", "-", "Breguet coefficient") t = Variable("t", "days", "Time per flight segment") f_fueloil = Variable("f_{(fuel/oil)}", 0.98, "-", "Fuel-oil fraction") Wfuel = Variable("W_{fuel}", "lbf", "Segment-fuel weight") g = Variable("g", 9.81, "m/s^2", "gravitational acceleration") constraints = [ TCS([ z_bre >= (perf["P_{total}"] * t * perf["BSFC"] * g / (perf["W_{end}"] * perf["W_{start}"])**0.5) ]), f_fueloil * Wfuel / perf["W_{end}"] >= te_exp_minus1(z_bre, 3), perf["W_{start}"] >= perf["W_{end}"] + Wfuel ] return constraints
def setup(self, static, state): exec parse_variables(BladeElementPerf.__doc__) V = state.V rho = state.rho R = static.R mu = state.mu path = os.path.dirname(__file__) fd = pd.read_csv(path + os.sep + "dae51_fitdata.csv").to_dict(orient="records")[0] c = static.c constraints = [ TCS([Wa >= V + va]), TCS([Wt + vt <= omega * r]), TCS([G == (1. / 2.) * Wr * c * cl]), F == (2. / pi) * (1.01116 * f**.0379556) **(10), #This is the GP fit of arccos(exp(-f)) M == Wr / a, lam_w == (r / R) * (Wa / Wt), va == vt * (Wt / Wa), eps == cd / cl, TCS([dQ >= rho * B * G * (Wa + eps * Wt) * r * dr]), AR_b == R / c, AR_b <= AR_b_max, Re == Wr * c * rho / mu, eta_i == (V / (omega * r)) * (Wt / Wa), TCS([f + (r / R) * B / (2 * lam_w) <= (B / 2.) * (1. / lam_w)]), XfoilFit(fd, cd, [cl, Re], name="polar"), cl <= cl_max ] with SignomialsEnabled(): constraints += [ SignomialEquality(Wr**2, (Wa**2 + Wt**2)), TCS([dT <= rho * B * G * (Wt - eps * Wa) * dr]), TCS([ vt**2 * F**2 * (1. + (4. * lam_w * R / (pi * B * r))**2) >= (B * G / (4. * pi * r))**2 ]), ] return constraints, state
def setup(self, Nmissions, **kwargs): g = Variable('g', 9.81, 'm*s^-2', 'Acceleration due to gravity') dPover = Variable('\\Delta P_{over}', 'psi', 'Cabin overpressure') with Vectorize(Nmissions): npass = Variable('n_{pass}', '-', 'Number of passengers') Nland = Variable('N_{land}', 6.0, '-', 'Emergency landing load factor') # [TAS] Nlift = Variable('N_{lift}', '-', 'Wing maximum load factor') SPR = Variable('SPR', '-', 'Number of seats per row') nrows = Variable('n_{rows}', '-', 'Number of rows') nseat = Variable('n_{seat}', '-', 'Number of seats') pitch = Variable('p_s', 'cm', 'Seat pitch') # Cross-sectional variables Adb = Variable('A_{db}', 'm^2', 'Web cross sectional area') Afloor = Variable('A_{floor}', 'm^2', 'Floor beam x-sectional area') Afuse = Variable('A_{fuse}', 'm^2', 'Fuselage x-sectional area') Askin = Variable('A_{skin}', 'm^2', 'Skin cross sectional area') hdb = Variable('h_{db}', 'm', 'Web half-height') hfloor = Variable('h_{floor}', 'm', 'Floor beam height') hfuse = Variable('h_{fuse}', 'm', 'Fuselage height') dRfuse = Variable('\\Delta R_{fuse}', 'm', 'Fuselage extension height') Rfuse = Variable('R_{fuse}', 'm', 'Fuselage radius') tdb = Variable('t_{db}', 'm', 'Web thickness') thetadb = Variable('\\theta_{db}', '-', 'DB fuselage joining angle') tshell = Variable('t_{shell}', 'm', 'Shell thickness') tskin = Variable('t_{skin}', 'm', 'Skin thickness') waisle = Variable('w_{aisle}', 'm', 'Aisle width') wdb = Variable('w_{db}', 'm', 'DB added half-width') wfloor = Variable('w_{floor}', 'm', 'Floor half-width') wfuse = Variable('w_{fuse}', 'm', 'Fuselage half-width') wseat = Variable('w_{seat}', 'm', 'Seat width') wsys = Variable('w_{sys}', 'm', 'Width between cabin and skin for systems') # Tail cone variables lamcone = Variable('\\lambda_{cone}', '-', 'Tailcone radius taper ratio') lcone = Variable('l_{cone}', 'm', 'Cone length') plamv = Variable('p_{\\lambda_{vt}}', 1.6, '-', '1 + 2*Tail taper ratio') # tcone = Variable('t_{cone}', 'm', 'Cone thickness') # perhaps to be added later # Lengths c0 = Variable('c_0', 'm', 'Root chord of the wing') lfuse = Variable('l_{fuse}', 'm', 'Fuselage length') lnose = Variable('l_{nose}', 'm', 'Nose length') lshell = Variable('l_{shell}', 'm', 'Shell length') lfloor = Variable('l_{floor}', 'm', 'Floor length') # Surface areas Sbulk = Variable('S_{bulk}', 'm^2', 'Bulkhead surface area') Snose = Variable('S_{nose}', 'm^2', 'Nose surface area') # Volumes Vbulk = Variable('V_{bulk}', 'm^3', 'Bulkhead skin volume') Vcabin = Variable('V_{cabin}', 'm^3', 'Cabin volume') Vcone = Variable('V_{cone}', 'm^3', 'Cone skin volume') Vcyl = Variable('V_{cyl}', 'm^3', 'Cylinder skin volume') Vdb = Variable('V_{db}', 'm^3', 'Web volume') Vfloor = Variable('V_{floor}', 'm^3', 'Floor volume') Vnose = Variable('V_{nose}', 'm^3', 'Nose skin volume') # Loads sigskin = Variable('\\sigma_{skin}', 'N/m^2', 'Max allowable skin stress') sigth = Variable('\\sigma_{\\theta}', 'N/m^2', 'Skin hoop stress') sigx = Variable('\\sigma_x', 'N/m^2', 'Axial stress in skin') # Floor loads Mfloor = Variable('M_{floor}', 'N*m', 'Max bending moment in floor beams') Pfloor = Variable('P_{floor}', 'N', 'Distributed floor load') Sfloor = Variable('S_{floor}', 'N', 'Maximum shear in floor beams') sigfloor = Variable('\\sigma_{floor}', 'N/m^2', 'Max allowable floor stress') taucone = Variable('\\tau_{cone}', 'N/m^2', 'Shear stress in cone') taufloor = Variable('\\tau_{floor}', 'N/m^2', 'Max allowable shear web stress') # Bending inertias (ported from TASOPT) # (shell inertia contribution) A0h = Variable('A_{0h}', 'm^2', 'Horizontal bending area constant A0h') # (tail impact + aero loading) A1hLand = Variable( 'A_{1h_{Land}}', 'm', 'Horizontal bending area constant A1h (landing case)') A1hMLF = Variable( 'A_{1h_{MLF}}', 'm', 'Horizontal bending area constant A1h (max aero load case)') # (fuselage impact) A2hLand = Variable( 'A_{2h_{Land}}', '-', 'Horizontal bending area constant A2h (landing case)') A2hMLF = Variable( 'A_{2h_{MLF}}', '-', 'Horizontal bending area constant A2h (max aero load case)') AhbendbLand = Variable( 'A_{hbendb_{Land}}', 'm^2', 'Horizontal bending area at rear wingbox (landing case)') AhbendbMLF = Variable( 'A_{hbendb_{MLF}}', 'm^2', 'Horizontal bending area at rear wingbox (max aero load case)') AhbendfLand = Variable( 'A_{hbendf_{Land}}', 'm^2', 'Horizontal bending area at front wingbox (landing case)') AhbendfMLF = Variable( 'A_{hbendf_{MLF}}', 'm^2', 'Horizontal bending area at front wingbox (max aero load case)') Avbendb = Variable('A_{vbend_{b}}', 'm^2', 'Vertical bending material area at rear wingbox') B0v = Variable( 'B_{0v}', 'm^2', 'Vertical bending area constant B0') #(shell inertia contribution) B1v = Variable('B_{1v}', 'm', 'Vertical bending area constant B1') # #(vertical tail bending load) Ihshell = Variable('I_{h_{shell}}', 'm^4', 'Shell horizontal bending inertia') Ivshell = Variable('I_{v_{shell}}', 'm^4', 'Shell vertical bending inertia') rMh = Variable('r_{M_h}', .4, '-', 'Horizontal inertial relief factor') # [TAS] rMv = Variable('r_{M_v}', .7, '-', 'Vertical inertial relief factor') # [TAS] sigbend = Variable('\\sigma_{bend}', 'N/m^2', 'Bending material stress') sigMh = Variable('\\sigma_{M_h}', 'N/m^2', 'Horizontal bending material stress') sigMv = Variable('\\sigma_{M_v}', 'N/m^2', 'Vertical bending material stress') Vhbend = Variable('V_{hbend}', 'm^3', 'Horizontal bending material volume') Vhbendb = Variable( 'V_{hbend_{b}}', 'm^3', 'Horizontal bending material volume b') # back fuselage Vhbendc = Variable( 'V_{hbend_{c}}', 'm^3', 'Horizontal bending material volume c') # center fuselage Vhbendf = Variable( 'V_{hbend_{f}}', 'm^3', 'Horizontal bending material volume f') # front fuselage Vvbend = Variable('V_{vbend}', 'm^3', 'Vertical bending material volume') Vvbendb = Variable( 'V_{vbend_{b}}', 'm^3', 'Vertical bending material volume b') #back fuselage Vvbendc = Variable( 'V_{vbend_{c}}', 'm^3', 'Vertical bending material volume c') #center fuselage Whbend = Variable('W_{hbend}', 'lbf', 'Horizontal bending material weight') Wvbend = Variable('W_{vbend}', 'lbf', 'Vertical bending material weight') xhbendLand = Variable( 'x_{hbend_{Land}}', 'ft', 'Horizontal zero bending location (landing case)') xhbendMLF = Variable( 'x_{hbend_{MLF}}', 'ft', 'Horizontal zero bending location (maximum aero load case)') xvbend = Variable('x_{vbend}', 'ft', 'Vertical zero bending location') # Material properties rE = Variable( 'r_E', 1., '-', 'Ratio of stringer/skin moduli') # [TAS] # [b757 freight doc] rhocargo = Variable('\\rho_{cargo}', 150, 'kg/m^3', 'Cargo density') rhocone = Variable('\\rho_{cone}', 'kg/m^3', 'Cone material density') # [TAS] rhobend = Variable('\\rho_{bend}', 'kg/m^3', 'Stringer density') # [TAS] rhofloor = Variable('\\rho_{floor}', 'kg/m^3', 'Floor material density') # [TAS] rholugg = Variable('\\rho_{lugg}', 100, 'kg/m^3', 'Luggage density') # [Philippe] rhoskin = Variable('\\rho_{skin}', 'kg/m^3', 'Skin density') # [TAS] Wppfloor = Variable('W\'\'_{floor}', 'N/m^2', 'Floor weight/area density') # [TAS] Wppinsul = Variable( 'W\'\'_{insul}', 'N/m^2', 'Weight/area density of insulation material') # [TAS] Wpseat = Variable('W\'_{seat}', 'N', 'Weight per seat') # [TAS] Wpwindow = Variable('W\'_{window}', 'N/m', 'Weight/length density of windows') # [TAS] # Weight fractions fapu = Variable('f_{apu}', 0.035, '-', 'APU weight as fraction of payload weight') # [TAS] ffadd = Variable( 'f_{fadd}', '-', 'Fractional added weight of local reinforcements') # [TAS] fframe = Variable('f_{frame}', '-', 'Fractional frame weight') # [Philippe] flugg1 = Variable( 'f_{lugg,1}', '-', 'Proportion of passengers with one suitcase') # [Philippe] flugg2 = Variable( 'f_{lugg,2}', '-', 'Proportion of passengers with two suitcases') # [Philippe] fpadd = Variable('f_{padd}', 0.35, '-', 'Other misc weight as fraction of payload weight') fseat = Variable('f_{seat}', '-', 'Fractional seat weight') fstring = Variable('f_{string}', '-', 'Fractional stringer weight') # [Philippe] # Weights Wapu = Variable('W_{apu}', 'lbf', 'APU weight') Wavgpass = Variable('W_{avg. pass}', 'lbf', 'Average passenger weight') # [Philippe] Wavgpasstot = Variable( 'W_{avg. pass_{total}}', 215., 'lbf', 'Average passenger weight including payload') #[TAS] Wcargo = Variable('W_{cargo}', 'lbf', 'Cargo weight') # [Philippe] Wcarryon = Variable('W_{carry on}', 'lbf', 'Ave. carry-on weight') # [Philippe] Wchecked = Variable('W_{checked}', 'lbf', 'Ave. checked bag weight') # [Philippe] Wcone = Variable('W_{cone}', 'lbf', 'Cone weight') Wdb = Variable('W_{db}', 'lbf', 'Web weight') Wfix = Variable('W_{fix}', 'lbf', 'Fixed weights (pilots, cockpit seats, navcom)') Wfloor = Variable('W_{floor}', 'lbf', 'Floor weight') Wfuse = Variable('W_{fuse}', 'lbf', 'Fuselage weight') Winsul = Variable('W_{insul}', 'lbf', 'Insulation material weight') Wpadd = Variable('W_{padd}', 'lbf', 'Misc weights (galley, toilets, doors etc.)') Wseat = Variable('W_{seat}', 'lbf', 'Seating weight') Wshell = Variable('W_{shell}', 'lbf', 'Shell weight') Wskin = Variable('W_{skin}', 'lbf', 'Skin weight') Wtail = Variable('W_{tail}', 'lbf', 'Total tail weight') Wwindow = Variable('W_{window}', 'lbf', 'Window weight') with Vectorize(Nmissions): Wpay = Variable('W_{payload}', 'lbf', 'Payload weight') Wlugg = Variable('W_{lugg}', 'lbf', 'Passenger luggage weight') Wpass = Variable('W_{pass}', 'lbf', 'Passenger weight') Wpaymax = Variable('W_{payload_{max}}', 'lbf', 'Maximum payload weight') # x-location variables xshell1 = Variable('x_{shell1}', 'm', 'Start of cylinder section') xshell2 = Variable('x_{shell2}', 'm', 'End of cylinder section') xtail = Variable('x_{tail}', 'm', 'x-location of tail') xwing = Variable('x_{wing}', 'm', 'x-location of wing c/4') # Wingbox variables xf = Variable('x_f', 'm', 'x-location of front of wingbox') xb = Variable('x_b', 'm', 'x-location of back of wingbox') w = Variable('r_{w/c}', 0.5, '-', 'Wingbox width-to-chord ratio') #weight margin and sensitivity Cfuse = Variable('C_{fuse}', 1, '-', 'Fuselage Weight Margin and Sensitivity') #fuselage drag reference mach MfuseD = Variable('M_{fuseD}', '-', 'Fuselage Drag Reference Mach') # Moments # alphaMf0 = Variable('\\alpha_{Mf0}','-','AoA at which fuselage moment is zero') constraints = [] with SignomialsEnabled(): constraints.extend([ # Passenger constraints Wlugg >= flugg2 * npass * 2 * Wchecked + flugg1 * npass * Wchecked + Wcarryon, TCS([Wpass >= npass * Wavgpass]), Wpay >= Wpass + Wlugg + Wcargo, TCS([Wpay >= npass * Wavgpasstot]), Wpaymax >= Wpay, nseat >= npass, nrows == nseat / SPR, lshell == nrows * pitch, # Fuselage joint angle relations thetadb == wdb / Rfuse, # first order Taylor works... hdb >= Rfuse * (1.0 - .5 * thetadb**2), # [SP] # Cross-sectional constraints Adb >= (2 * hdb + dRfuse) * tdb, Afuse >= (pi + 2 * thetadb + 2 * thetadb * \ (1 - thetadb**2 / 2)) * Rfuse**2 + 2*dRfuse*Rfuse, # [SP] Askin >= (2 * pi + 4 * thetadb) * Rfuse * tskin + 2*dRfuse*tskin, wfloor == wfuse, wfuse <= (Rfuse + wdb), SignomialEquality(hfuse, Rfuse + 0.5*dRfuse), #[SP] #[SPEquality] TCS([tshell <= tskin * (1. + rE * fstring * rhoskin / rhobend)]), #[SP] # Fuselage surface area relations Snose >= (2 * pi + 4 * thetadb) * Rfuse**2 * \ (1 / 3 + 2 / 3 * (lnose / Rfuse)**(8 / 5))**(5 / 8), Sbulk >= (2 * pi + 4 * thetadb) * Rfuse**2, # Fuselage length relations SignomialEquality(lfuse, lnose + lshell + lcone), #[SP] #[SPEquality] # NOTE: it is not clear which direction the pressure is! lcone == Rfuse / lamcone, xshell1 == lnose, TCS([xshell2 >= lnose + lshell]), # STRESS RELATIONS # Pressure shell loading tskin == dPover * Rfuse / sigskin, tdb == 2 * dPover * wdb / sigskin, sigx == dPover * Rfuse / (2 * tshell), sigth == dPover * Rfuse / tskin, # Floor loading lfloor >= lshell + 2 * Rfuse, TCS([Pfloor >= Nland * (Wpaymax + Wseat)]), Afloor >= 2. * Mfloor / (sigfloor * hfloor) + 1.5 * Sfloor / taufloor, Vfloor == 2 * wfloor * Afloor, Wfloor >= rhofloor * g * Vfloor + 2 * wfloor * lfloor * Wppfloor, # hfloor <= 0.1 * Rfuse, # Tail cone sizing taucone == sigskin, Wcone >= rhocone * g * Vcone * (1 + fstring + fframe), xtail >= lnose + lshell + .5 * lcone, # BENDING MODEL # Maximum axial stress is the sum of bending and pressurization # stresses Ihshell <= ((pi + 4 * thetadb) * Rfuse**2 + 8.*(1-thetadb**2/2) * (dRfuse/2.)*Rfuse + \ (2*pi + 4 * thetadb)*(dRfuse/2)**2) * Rfuse * tshell + \ 2 / 3 * (hdb + dRfuse/2.)**3 * tdb, # [SP] Ivshell <= (pi*Rfuse**2 + 8*wdb*Rfuse + (2*pi+4*thetadb)*wdb**2)*Rfuse*tshell, #[SP] #Ivshell # approximation needs to be improved # Horizontal bending material model # Calculating xhbend, the location where additional bending # material is required xhbendLand >= xwing, xhbendLand <= lfuse, xhbendMLF >= xwing, xhbendMLF <= lfuse, SignomialEquality(A0h, A2hLand * (xshell2 - xhbendLand) ** 2 + A1hLand * (xtail - xhbendLand)), # [SP] #[SPEquality] SignomialEquality(A0h, A2hMLF * (xshell2 - xhbendMLF) ** 2 + A1hMLF * (xtail - xhbendMLF)), # [SP] #[SPEquality] A2hLand >= Nland * (Wpaymax + Wpadd + Wshell + Wwindow + Winsul + Wfloor + Wseat) / \ (2 * lshell * hfuse * sigMh), # Landing loads constant A2hLand A2hMLF >= Nlift * (Wpaymax + Wpadd + Wshell + Wwindow + Winsul + Wfloor + Wseat) / \ (2 * lshell * hfuse * sigMh), # Max wing aero loads constant A2hMLF # Shell inertia constant A0h A0h == (Ihshell / (rE * hfuse**2)), # [SP] # Bending area behind wingbox AhbendfLand >= A2hLand * (xshell2 - xf)**2 + A1hLand * (xtail - xf) - A0h, # [SP] AhbendfMLF >= A2hMLF * (xshell2 - xf)**2 + A1hMLF * (xtail - xf) - A0h, # [SP] # Bending area in front of wingbox AhbendbLand >= A2hLand * (xshell2 - xb)**2 + A1hLand * (xtail - xb) - A0h, # [SP] AhbendbMLF >= A2hMLF * (xshell2 - xb)**2 + A1hMLF * (xtail - xb) - A0h, # [SP] # Bending volume forward of wingbox Vhbendf >= A2hLand / 3 * ((xshell2 - xf)**3 - (xshell2 - xhbendLand)**3) \ + A1hLand / 2 * ((xtail - xf)**2 - (xtail - xhbendLand)**2) \ - A0h * (xhbendLand - xf), # [SP] Vhbendf >= A2hMLF / 3 * ((xshell2 - xf)**3 - (xshell2 - xhbendMLF)**3) \ + A1hMLF / 2 * ((xtail - xf)**2 - (xtail - xhbendMLF)**2) \ - A0h * (xhbendMLF - xf), # [SP] # Bending volume behind wingbox Vhbendb >= A2hLand / 3 * ((xshell2 - xb)**3 - (xshell2 - xhbendLand)**3) \ + A1hLand / 2 * ((xtail - xb)**2 - (xtail - xhbendLand)**2) \ - A0h * (xhbendLand - xb), # [SP] Vhbendb >= A2hMLF / 3 * ((xshell2 - xb)**3 - (xshell2 - xhbendMLF)**3) \ + A1hMLF / 2 * ((xtail - xb)**2 - (xtail - xhbendMLF)**2) \ - A0h * (xhbendMLF - xb), # [SP] # Bending volume over wingbox Vhbendc >= .5 * (AhbendfLand + AhbendbLand) * c0 * w, Vhbendc >= .5 * (AhbendfMLF + AhbendbMLF) * c0 * w, # Determining more constraining load case (landing vs. max aero horizontal bending) Vhbend >= Vhbendc + Vhbendf + Vhbendb, Whbend >= g * rhobend * Vhbend, # Vertical bending material model # Calculating xvbend, the location where additional bending material is required xvbend >= xwing, xvbend <= lfuse, SignomialEquality(B0v, B1v * (xtail - xvbend)), # [SP] #[SPEquality] #B1v definition in Aircraft() B0v == Ivshell/(rE*wfuse**2), Avbendb >= B1v * (xtail - xb) - B0v, Vvbendb >= 0.5*B1v * ((xtail-xb)**2 - (xtail - xvbend)**2) - B0v * (xvbend - xb), Vvbendc >= 0.5*Avbendb*c0*w, Vvbend >= Vvbendb + Vvbendc, Wvbend >= rhobend*g*Vvbend, # Wing variable substitutions SignomialEquality(xf,xwing + .5 * c0 * w), # [SP] [SPEquality] SignomialEquality(xb,xwing - .5 * c0 * w), # [SP] [SPEquality] sigMh <= sigbend - rE * dPover / 2 * Rfuse / tshell, sigMv <= sigbend - rE * dPover / 2 * Rfuse / tshell, # Volume relations Vcyl == Askin * lshell, Vnose == Snose * tskin, Vbulk == Sbulk * tskin, Vdb == Adb * lshell, Vcabin >= Afuse * (lshell + 0.67 * lnose + 0.67 * Rfuse), # Weight relations Wapu == Wpaymax * fapu, Wdb == rhoskin * g * Vdb, Winsul >= Wppinsul * ((1.1 * pi + 2 * thetadb) * Rfuse * lshell + 0.55 * (Snose + Sbulk)), Wwindow >= Wpwindow * lshell, Wpadd == Wpaymax * fpadd, # Two methods for determining seat weight (must be inequalities) Wseat >= Wpseat * nseat, Wseat >= fseat * Wpaymax, Wskin >= rhoskin * g * (Vcyl + Vnose + Vbulk), Wshell >= Wskin * (1 + fstring + ffadd + fframe) + Wdb, Wfuse >= Cfuse*(Wshell + Wfloor + Winsul + \ Wapu + Wfix + Wwindow + Wpadd + Wseat + Whbend + Wvbend + Wcone), ]) return constraints
def setup(self, Nclimb, Ncruise, Nfleet, substitutions=None, **kwargs): eng = 0 #two level vectorization to make a fleet with Vectorize(Nfleet): # vectorize with Vectorize(Nclimb + Ncruise): enginestate = FlightState() ac = Aircraft(Nclimb, Ncruise, enginestate, eng, Nfleet) #two level vectorization to make a fleet with Vectorize(Nfleet): #Vectorize with Vectorize(Nclimb): climb = ClimbSegment(ac) with Vectorize(Ncruise): cruise = CruiseSegment(ac) statelinking = StateLinking(climb.state, cruise.state, enginestate, Nclimb, Ncruise) with Vectorize(Nfleet): #declare new variables W_ftotal = Variable('W_{f_{total}}', 'N', 'Total Fuel Weight') W_fclimb = Variable('W_{f_{climb}}', 'N', 'Fuel Weight Burned in Climb') W_fcruise = Variable('W_{f_{cruise}}', 'N', 'Fuel Weight Burned in Cruise') W_total = Variable('W_{total}', 'N', 'Total Aircraft Weight') CruiseAlt = Variable('CruiseAlt', 'ft', 'Cruise Altitude [feet]') ReqRng = Variable('ReqRng', 'nautical_miles', 'Required Cruise Range') W_dry = Variable('W_{dry}', 'N', 'Aircraft Dry Weight') dhfthold = Variable('dhfthold', 'ft', 'Hold Variable') W_ffleet = Variable('W_{f_{fleet}}', 'N', 'Total Fleet Fuel Burn') h = climb['h'] hftClimb = climb['hft'] dhft = climb['dhft'] hftCruise = cruise['hft'] #make overall constraints constraints = [] constraints.extend([ #weight constraints TCS([ ac['W_{e}'] + ac['W_{payload}'] + ac['numeng'] * ac['W_{engine}'] + ac['W_{wing}'] <= W_dry ]), TCS([W_dry + W_ftotal <= W_total]), climb['W_{start}'][0] == W_total, climb['W_{end}'][-1] == cruise['W_{start}'][0], # similar constraint 1 TCS([climb['W_{start}'] >= climb['W_{end}'] + climb['W_{burn}']]), # similar constraint 2 TCS([ cruise['W_{start}'] >= cruise['W_{end}'] + cruise['W_{burn}'] ]), climb['W_{start}'][1:] == climb['W_{end}'][:-1], cruise['W_{start}'][1:] == cruise['W_{end}'][:-1], TCS([W_dry <= cruise['W_{end}'][-1]]), TCS([W_ftotal >= W_fclimb + W_fcruise]), TCS([W_fclimb >= sum(climb['W_{burn}'])]), TCS([W_fcruise >= sum(cruise['W_{burn}'])]), #altitude constraints hftCruise == CruiseAlt, TCS([ hftClimb[1:Nclimb] >= hftClimb[:Nclimb - 1] + dhft[:Nclimb - 1] ]), TCS([hftClimb[0] >= dhft[0]]), hftClimb[-1] <= hftCruise, #compute the dh dhfthold == hftCruise[0] / Nclimb, dhft == dhfthold, #set the range for each cruise segment, doesn't take credit for climb #down range disatnce covered cruise.cruiseP['Rng'] == ReqRng / (Ncruise), #compute fuel burn from TSFC cruise['W_{burn}'] == ac['numeng'] * ac.engine['TSFC'][Nclimb:] * cruise['thr'] * ac.engine['F'][Nclimb:], climb['W_{burn}'] == ac['numeng'] * ac.engine['TSFC'][:Nclimb] * climb['thr'] * ac.engine['F'][:Nclimb], CruiseAlt >= 30000 * units('ft'), #min climb rate constraint climb['RC'] >= 500 * units('ft/min'), ]) fleetfuel = [ #compute the fleet fuel burn W_ffleet >= .375 * W_ftotal[0] + .375 * W_ftotal[1] + .125 * W_ftotal[2] + .125 * W_ftotal[3], ] M2 = .8 M25 = .6 M4a = .1025 M0 = .8 engineclimb = [ ac.engine.engineP['M_2'][:Nclimb] == climb['M'], ac.engine.engineP['M_{2.5}'][:Nclimb] == M25, ac.engine.engineP['hold_{2}'] == 1 + .5 * (1.398 - 1) * M2**2, ac.engine.engineP['hold_{2.5}'] == 1 + .5 * (1.354 - 1) * M25**2, ac.engine.engineP['c1'] == 1 + .5 * (.401) * M0**2, #constraint on drag and thrust ac['numeng'] * ac.engine['F_{spec}'][:Nclimb] >= climb['D'] + climb['W_{avg}'] * climb['\\theta'], #climb rate constraints TCS([ climb['excessP'] + climb.state['V'] * climb['D'] <= climb.state['V'] * ac['numeng'] * ac.engine['F_{spec}'][:Nclimb] ]), ] M25 = .6 enginecruise = [ ac.engine.engineP['M_2'][Nclimb:] == cruise['M'], ac.engine.engineP['M_{2.5}'][Nclimb:] == M25, #steady level flight constraint on D cruise['D'] == ac['numeng'] * ac.engine['F_{spec}'][Nclimb:], #breguet range eqn TCS([ cruise['z_{bre}'] >= (ac.engine['TSFC'][Nclimb:] * cruise['thr'] * cruise['D']) / cruise['W_{avg}'] ]), ] ranges = [ ReqRng[0] == 500 * units('nautical_miles'), ReqRng[1] == 1000 * units('nautical_miles'), ReqRng[2] == 1500 * units('nautical_miles'), ReqRng[3] == 2000 * units('nautical_miles'), ] return constraints + ac + climb + cruise + enginecruise + engineclimb + enginestate + statelinking + ranges + fleetfuel
def setup(self): #variables p = Variable('p_{ht}', '-', 'Substituted variable = 1 + 2*taper') q = Variable('q_{ht}', '-', 'Substituted variable = 1 + taper') etaht = Variable('\\eta_{ht}', '-', 'Tail efficiency') tanLh = Variable('\\tan(\\Lambda_{ht})', '-', 'tangent of horizontal tail sweep') taper = Variable('\lambda_{ht}', '-', 'Horizontal tail taper ratio') tau = Variable('\\tau_{ht}', '-', 'Horizontal tail thickness/chord ratio') xcght = Variable('x_{CG_{ht}}', 'm', 'Horizontal tail CG location') ymac = Variable('y_{\\bar{c}_{ht}}', 'm', 'Spanwise location of mean aerodynamic chord') dxlead = Variable('\\Delta x_{lead_{ht}}', 'm', 'Distance from CG to horizontal tail leading edge') dxtrail = Variable('\\Delta x_{trail_{ht}}', 'm', 'Distance from CG to horizontal tail trailing edge') lht = Variable('l_{ht}', 'm', 'Horizontal tail moment arm') ARht = Variable('AR_{ht}', '-', 'Horizontal tail aspect ratio') amax = Variable('\\alpha_{ht,max}', '-', 'Max angle of attack, htail') e = Variable('e_{ht}', '-', 'Oswald efficiency factor') Sh = Variable('S_{ht}', 'm^2', 'Horizontal tail area') bht = Variable('b_{ht}', 'm', 'Horizontal tail span') chma = Variable('\\bar{c}_{ht}', 'm', 'Mean aerodynamic chord (ht)') croot = Variable('c_{root_{ht}}', 'm', 'Horizontal tail root chord') ctip = Variable('c_{tip_{ht}}', 'm', 'Horizontal tail tip chord') Lmax = Variable('L_{ht_{max}}', 'N', 'Maximum load') fl = Variable(r"f(\lambda_{ht})", '-', 'Empirical efficiency function of taper') CLhmax = Variable('C_{L_{ht,max}}', '-', 'Max lift coefficient') CLfCG = Variable('C_{L_{ht,fCG}}', '-', 'HT CL During Max Forward CG') #new variables Vh = Variable('V_{ht}', '-', 'Horizontal Tail Volume Coefficient') mrat = Variable('m_{ratio}', '-', 'Wing to Tail Lift Slope Ratio') #variable just for the D8 cattach = Variable('c_{attach}', 'm', 'HT Chord Where it is Mounted to the VT') #constraints constraints = [] with SignomialsEnabled(): constraints.extend([ # Moment arm and geometry -- same as for vtail TCS([dxlead + ymac * tanLh + 0.25 * chma >= lht], reltol=1e-2), # [SP] TCS([dxlead + croot <= dxtrail]), p >= 1 + 2*taper, 2*q >= 1 + p, ymac == (bht/3)*q/p, SignomialEquality((2./3)*(1 + taper + taper**2)*croot/q, chma), taper == ctip/croot, SignomialEquality(Sh, bht*(croot + ctip)/2), # Oswald efficiency # Nita, Scholz, # "Estimating the Oswald factor from basic # aircraft geometrical parameters" TCS([fl >= (0.0524*taper**4 - 0.15*taper**3 + 0.1659*taper**2 - 0.0706*taper + 0.0119)], reltol=0.2), # NOTE: slightly slack TCS([e*(1 + fl*ARht) <= 1]), ARht == bht**2/Sh, taper >= 0.2, # TODO: make less arbitrary taper <= 1, ]) return constraints
def setup(self, ac, substitutions=None, **kwargs): #define the number of each flight segment Nclimb = 2 Ncruise = 2 #Vectorize with Vectorize(Nclimb): climb = ClimbSegment(ac) with Vectorize(Ncruise): cruise = CruiseSegment(ac) #declare new variables W_ftotal = Variable('W_{f_{total}}', 'N', 'Total Fuel Weight') W_fclimb = Variable('W_{f_{climb}}', 'N', 'Fuel Weight Burned in Climb') W_fcruise = Variable('W_{f_{cruise}}', 'N', 'Fuel Weight Burned in Cruise') W_total = Variable('W_{total}', 'N', 'Total Aircraft Weight') CruiseAlt = Variable('CruiseAlt', 'ft', 'Cruise Altitude [feet]') ReqRng = Variable('R_{req}', 'nautical_miles', 'Required Cruise Range') h = climb['h'] hftClimb = climb['hft'] dhft = climb['dhft'] hftCruise = cruise['hft'] #make overall constraints constraints = [] constraints.extend([ #weight constraints TCS([ ac['W_{e}'] + ac['W_{payload}'] + W_ftotal + ac['numeng'] * ac['W_{engine}'] + ac['W_{wing}'] <= W_total ]), climb['W_{start}'][0] == W_total, climb['W_{end}'][-1] == cruise['W_{start}'][0], # similar constraint 1 TCS([climb['W_{start}'] >= climb['W_{end}'] + climb['W_{burn}']]), # similar constraint 2 TCS([ cruise['W_{start}'] >= cruise['W_{end}'] + cruise['W_{burn}'] ]), climb['W_{start}'][1:] == climb['W_{end}'][:-1], cruise['W_{start}'][1:] == cruise['W_{end}'][:-1], TCS([ ac['W_{e}'] + ac['W_{payload}'] + ac['numeng'] * ac['W_{engine}'] + ac['W_{wing}'] <= cruise['W_{end}'][-1] ]), TCS([W_ftotal >= W_fclimb + W_fcruise]), TCS([W_fclimb >= sum(climb['W_{burn}'])]), TCS([W_fcruise >= sum(cruise['W_{burn}'])]), #altitude constraints hftCruise == CruiseAlt, TCS([hftClimb[1:Ncruise] >= hftClimb[:Ncruise - 1] + dhft]), TCS([hftClimb[0] >= dhft[0]]), hftClimb[-1] <= hftCruise, #compute the dh dhft == hftCruise / Nclimb, #constrain the thrust climb['thrust'] <= 2 * max(cruise['thrust']), #set the range for each cruise segment, doesn't take credit for climb #down range disatnce covered cruise.cruiseP['Rng'] == ReqRng / (Ncruise), #set the TSFC climb['TSFC'] == .7 * units('1/hr'), cruise['TSFC'] == .5 * units('1/hr'), ]) # Model.setup(self, W_ftotal + s*units('N'), constraints + ac + climb + cruise, subs) return constraints + ac + climb + cruise
def setup(self, Nclimb, Ncruise, substitutions=None, **kwargs): eng = 0 # vectorize with Vectorize(Nclimb + Ncruise): enginestate = FlightState() #build the submodel ac = Aircraft(Nclimb, Ncruise, enginestate, eng) #Vectorize with Vectorize(Nclimb): climb = ClimbSegment(ac) with Vectorize(Ncruise): cruise = CruiseClimbSegment(ac) statelinking = StateLinking(climb.state, cruise.state, enginestate, Nclimb, Ncruise) #declare new variables W_ftotal = Variable('W_{f_{total}}', 'N', 'Total Fuel Weight') W_fclimb = Variable('W_{f_{climb}}', 'N', 'Fuel Weight Burned in Climb') W_fcruise = Variable('W_{f_{cruise}}', 'N', 'Fuel Weight Burned in Cruise') W_total = Variable('W_{total}', 'N', 'Total Aircraft Weight') CruiseAlt = Variable('CruiseAlt', 'ft', 'Cruise Altitude [feet]') ReqRng = Variable('ReqRng', 'nautical_miles', 'Required Cruise Range') W_dry = Variable('W_{dry}', 'N', 'Aircraft Dry Weight') dhftholdcl = Variable('dhftholdcl', 'ft', 'Climb Hold Variable') dhftholdcr = Variable('dhftholdcr', 'ft', 'Cruise Hold Variable') RCmin = Variable('RC_{min}', 'ft/min', 'Minimum allowed climb rate') h = climb['h'] hftClimb = climb['hft'] dhftcl = climb['dhft'] dhftcr = cruise['dhft'] hftCruise = cruise['hft'] #make overall constraints constraints = [] with SignomialsEnabled(): constraints.extend([ #weight constraints TCS([ ac['W_{e}'] + ac['W_{payload}'] + ac['numeng'] * ac['W_{engine}'] + ac['W_{wing}'] <= W_dry ]), TCS([W_dry + W_ftotal <= W_total]), climb['W_{start}'][0] == W_total, climb['W_{end}'][-1] == cruise['W_{start}'][0], # similar constraint 1 TCS([ climb['W_{start}'] >= climb['W_{end}'] + climb['W_{burn}'] ]), # similar constraint 2 TCS([ cruise['W_{start}'] >= cruise['W_{end}'] + cruise['W_{burn}'] ]), climb['W_{start}'][1:] == climb['W_{end}'][:-1], cruise['W_{start}'][1:] == cruise['W_{end}'][:-1], TCS([W_dry <= cruise['W_{end}'][-1]]), TCS([W_ftotal >= W_fclimb + W_fcruise]), TCS([W_fclimb >= sum(climb['W_{burn}'])]), TCS([W_fcruise >= sum(cruise['W_{burn}'])]), #altitude constraints hftCruise[0] == CruiseAlt, ## TCS([hftCruise[1:Ncruise] >= hftCruise[:Ncruise-1] + dhftcr]), SignomialEquality(hftCruise[1:Ncruise], hftCruise[:Ncruise - 1] + dhftholdcr), TCS([hftClimb[1:Nclimb] >= hftClimb[:Nclimb - 1] + dhftholdcl ]), TCS([hftClimb[0] >= dhftcl[0]]), hftClimb[-1] <= hftCruise, #compute the dh dhftholdcl == hftCruise[0] / Nclimb, dhftcl == dhftholdcl, dhftcr == dhftholdcr, sum(cruise['RngCruise']) + sum(climb['RngClimb']) >= ReqRng, #compute fuel burn from TSFC cruise['W_{burn}'] == ac['numeng'] * ac.engine['TSFC'][Nclimb:] * cruise['thr'] * ac.engine['F'][Nclimb:], climb['W_{burn}'] == ac['numeng'] * ac.engine['TSFC'][:Nclimb] * climb['thr'] * ac.engine['F'][:Nclimb], #min climb rate constraint ## climb['RC'][0] >= RCmin, ]) M2 = .8 M25 = .6 M4a = .1025 Mexit = 1 M0 = .8 engineclimb = [ ac.engine.engineP['M_2'][:Nclimb] == climb['M'], ac.engine.engineP['M_{2.5}'] == M25, ac.engine.engineP['hold_{2}'] == 1 + .5 * (1.398 - 1) * M2**2, ac.engine.engineP['hold_{2.5}'] == 1 + .5 * (1.354 - 1) * M25**2, ac.engine.engineP['c1'] == 1 + .5 * (.401) * M0**2, #constraint on drag and thrust ac['numeng'] * ac.engine['F'][:Nclimb] >= climb['D'] + climb['W_{avg}'] * climb['\\theta'], #climb rate constraints TCS([ climb['excessP'] + climb.state['V'] * climb['D'] <= climb.state['V'] * ac['numeng'] * ac.engine['F'][:Nclimb] ]), ] M2 = .8 M25 = .6 M4a = .1025 Mexit = 1 M0 = .8 enginecruise = [ ac.engine.engineP['M_2'][Nclimb:] == cruise['M'], ## cruise['M'] == .8, cruise['M'] >= .76, ac.engine.engineP['M_{2.5}'][2] == M25, ac.engine.engineP['M_{2.5}'][3] == M25, #constraint on drag and thrust ac['numeng'] * ac.engine['F'][Nclimb:] >= cruise['D'] + cruise['W_{avg}'] * cruise['\\theta'], #climb rate constraints TCS([ cruise['excessP'] + cruise.state['V'] * cruise['D'] <= cruise.state['V'] * ac['numeng'] * ac.engine['F'][Nclimb:] ]), ] return constraints + ac + climb + cruise + enginecruise + engineclimb + enginestate + statelinking
def setup(self, Nclimb1, Nclimb2, Ncruise, substitutions = None, **kwargs): eng = 0 # vectorize with Vectorize(Nclimb1 +Nclimb2 + Ncruise): enginestate = FlightState() #build the submodel ac = Aircraft(Nclimb1 + Nclimb2, Ncruise, enginestate, eng) #Vectorize with Vectorize(Nclimb1): climb1 = ClimbSegment(ac) #Vectorize with Vectorize(Nclimb2): climb2 = ClimbSegment(ac) with Vectorize(Ncruise): cruise = CruiseClimbSegment(ac) statelinking = StateLinking(climb1.state, climb2.state, cruise.state, enginestate, Nclimb1, Nclimb2, Ncruise) #declare new variables W_ftotal = Variable('W_{f_{total}}', 'N', 'Total Fuel Weight') W_fclimb1 = Variable('W_{f_{climb1}}', 'N', 'Fuel Weight Burned in Climb 1') W_fclimb2 = Variable('W_{f_{climb2}}', 'N', 'Fuel Weight Burned in Climb 2') W_fcruise = Variable('W_{f_{cruise}}', 'N', 'Fuel Weight Burned in Cruise') W_total = Variable('W_{total}', 'N', 'Total Aircraft Weight') CruiseAlt = Variable('CruiseAlt', 'ft', 'Cruise Altitude [feet]') ReqRng = Variable('ReqRng', 'nautical_miles', 'Required Cruise Range') W_dry = Variable('W_{dry}', 'N', 'Aircraft Dry Weight') RCmin = Variable('RC_{min}', 'ft/min', 'Minimum allowed climb rate') dhftholdcl1 = Variable('dhftholdcl1', 'ft', 'Climb 1 Hold Variable') dhftholdcl2 = Variable('dhftholdcl2', 'ft', 'Climb 2 Hold Variable') dhftholdcr = Variable('dhftholdcr', 'ft', 'Cruise Hold Variable') h1 = climb1['h'] hftClimb1 = climb1['hft'] dhftcl1 = climb1['dhft'] h2 = climb2['h'] hftClimb2 = climb2['hft'] dhftcl2 = climb2['dhft'] dhftcr = cruise['dhft'] hftCruise = cruise['hft'] #make overall constraints constraints = [] with SignomialsEnabled(): constraints.extend([ #weight constraints TCS([ac['W_{e}'] + ac['W_{payload}'] + ac['numeng'] * ac['W_{engine}'] + ac['W_{wing}'] <= W_dry]), TCS([W_dry + W_ftotal <= W_total]), climb1['W_{start}'][0] == W_total, climb1['W_{end}'][-1] == climb2['W_{start}'][0], climb2['W_{end}'][-1] == cruise['W_{start}'][0], TCS([climb1['W_{start}'] >= climb1['W_{end}'] + climb1['W_{burn}']]), TCS([climb2['W_{start}'] >= climb2['W_{end}'] + climb2['W_{burn}']]), TCS([cruise['W_{start}'] >= cruise['W_{end}'] + cruise['W_{burn}']]), climb1['W_{start}'][1:] == climb1['W_{end}'][:-1], climb2['W_{start}'][1:] == climb2['W_{end}'][:-1], cruise['W_{start}'][1:] == cruise['W_{end}'][:-1], TCS([W_dry <= cruise['W_{end}'][-1]]), TCS([W_ftotal >= W_fclimb1 + W_fclimb2 + W_fcruise]), TCS([W_fclimb1 >= sum(climb1['W_{burn}'])]), TCS([W_fclimb2 >= sum(climb2['W_{burn}'])]), TCS([W_fcruise >= sum(cruise['W_{burn}'])]), #altitude constraints hftCruise[0] == CruiseAlt, ## TCS([hftCruise[1:Ncruise] >= hftCruise[:Ncruise-1] + dhftcr]), SignomialEquality(hftCruise[1:Ncruise], hftCruise[:Ncruise-1] + dhftholdcr), TCS([hftClimb2[1:Nclimb2] <= hftClimb2[:Nclimb2-1] + dhftholdcl2]), TCS([hftClimb2[0] <= dhftholdcl2 + 10000*units('ft')]), hftClimb2[-1] == hftCruise, TCS([hftClimb1[1:Nclimb1] >= hftClimb1[:Nclimb1-1] + dhftholdcl1]), TCS([hftClimb1[0] == dhftcl1[0]]), hftClimb1[-1] == 10000*units('ft'), #compute the dh2 dhftholdcl2 >= (hftCruise-10000*units('ft'))/Nclimb2, ## SignomialEquality(dhftholdcl2, (hftCruise-10000*units('ft'))/Nclimb2,), dhftholdcl2 == dhftcl2, #compute the dh1 dhftholdcl1 == 10000*units('ft')/Nclimb1, dhftholdcl1 == dhftcl1, dhftcr == dhftholdcr, sum(cruise['RngCruise']) + sum(climb2['RngClimb']) + sum(climb1['RngClimb']) >= ReqRng, #compute fuel burn from TSFC cruise['W_{burn}'] == ac['numeng']*ac.engine['TSFC'][Nclimb1 + Nclimb2:] * cruise['thr'] * ac.engine['F'][Nclimb1 + Nclimb2:], climb1['W_{burn}'] == ac['numeng']*ac.engine['TSFC'][:Nclimb1] * climb1['thr'] * ac.engine['F'][:Nclimb1], climb2['W_{burn}'] == ac['numeng']*ac.engine['TSFC'][Nclimb1:Nclimb1 + Nclimb2] * climb2['thr'] * ac.engine['F'][Nclimb1:Nclimb1 + Nclimb2], #min climb rate constraint ## climb1['RC'][0] >= RCmin, climb1['V'] <= 250*units('knots'), ]) M2 = .8 M25 = .6 M4a = .1025 Mexit = 1 M0 = .8 engineclimb1 = [ ac.engine.engineP['M_2'][:Nclimb1] == climb1['M'], ac.engine.engineP['M_{2.5}'] == M25, ac.engine.engineP['hold_{2}'] == 1+.5*(1.398-1)*M2**2, ac.engine.engineP['hold_{2.5}'] == 1+.5*(1.354-1)*M25**2, ac.engine.engineP['c1'] == 1+.5*(.401)*M0**2, #constraint on drag and thrust ac['numeng']*ac.engine['F_{spec}'][:Nclimb1] >= climb1['D'] + climb1['W_{avg}'] * climb1['\\theta'], #climb rate constraints TCS([climb1['excessP'] + climb1.state['V'] * climb1['D'] <= climb1.state['V'] * ac['numeng'] * ac.engine['F_{spec}'][:Nclimb1]]), ] M2 = .8 M25 = .6 M4a = .1025 Mexit = 1 M0 = .8 engineclimb2 = [ ac.engine.engineP['M_2'][Nclimb1:Nclimb1 + Nclimb2] == climb2['M'], #constraint on drag and thrust ac['numeng']*ac.engine['F_{spec}'][Nclimb1:Nclimb1 + Nclimb2] >= climb2['D'] + climb2['W_{avg}'] * climb2['\\theta'], #climb rate constraints TCS([climb2['excessP'] + climb2.state['V'] * climb2['D'] <= climb2.state['V'] * ac['numeng'] * ac.engine['F_{spec}'][Nclimb1:Nclimb1 + Nclimb2]]), ] M2 = .8 M25 = .6 M4a = .1025 Mexit = 1 M0 = .8 enginecruise = [ ac.engine.engineP['M_2'][Nclimb1 + Nclimb2:] == cruise['M'], ## cruise['M'] >= .7, #constraint on drag and thrust ac['numeng'] * ac.engine['F_{spec}'][Nclimb1 + Nclimb2:] >= cruise['D'] + cruise['W_{avg}'] * cruise['\\theta'], #climb rate constraints TCS([cruise['excessP'] + cruise.state['V'] * cruise['D'] <= cruise.state['V'] * ac['numeng'] * ac.engine['F_{spec}'][Nclimb1 + Nclimb2:]]), ] return constraints + ac + climb1 + climb2 + cruise + enginecruise + engineclimb1 + engineclimb2 + enginestate + statelinking
def setup(self, **kwargs): #define new variables Avt = Variable('A_{vt}', '-', 'Vertical tail aspect ratio') CDwm = Variable('C_{D_{wm}}', '-', 'Windmill drag coefficient') Dwm = Variable('D_{wm}', 'N', 'Engine out windmill drag') Lvmax = Variable('L_{vt_{max}}', 'N', 'Maximum load for structural sizing') CLvmax = Variable('C_{L_{vt,max}}', '-', 'Max lift coefficient') CLvtEO = Variable('C_{L_{vt,EO}}', '-', 'Vertical tail lift coefficient (engine out)') clvtEO = Variable('c_{l_{vt,EO}}', '-', 'Sectional lift force coefficient (engine out)') LvtEO = Variable('L_{vt,EO}', 'N', 'Vertical tail lift in engine out') Svt = Variable('S_{vt}', 'm^2', 'Vertical tail reference area') V1 = Variable('V_1', 'm/s', 'Minimum takeoff velocity') bvt = Variable('b_{vt}', 'm', 'Vertical tail span') cma = Variable('\\bar{c}_{vt}', 'm', 'Vertical tail mean aero chord') croot = Variable('c_{root_{vt}}', 'm', 'Vertical tail root chord') ctip = Variable('c_{tip_{vt}}', 'm', 'Vertical tail tip chord') e = Variable('e_{vt}', '-', 'Span efficiency of vertical tail') dxlead = Variable('\\Delta x_{lead_{vt}}', 'm', 'Distance from CG to vertical tail leading edge') dxtrail = Variable('\\Delta x_{trail_{vt}}', 'm', 'Distance from CG to vertical tail trailing edge') lvt = Variable('l_{vt}', 'm', 'Vertical tail moment arm') mu0 = Variable('\\mu_0', 1.8E-5, 'N*s/m^2', 'Dynamic viscosity (SL)') p = Variable('p_{vt}', '-', 'Substituted variable = 1 + 2*taper') q = Variable('q_{vt}', '-', 'Substituted variable = 1 + taper') rho0 = Variable('\\rho_{TO}', 'kg/m^3', 'Air density (SL))') tanL = Variable('\\tan(\\Lambda_{vt})', '-', 'Tangent of leading edge sweep') taper = Variable('\\lambda_{vt}', '-', 'Vertical tail taper ratio') tau = Variable('\\tau_{vt}', '-', 'Vertical tail thickness/chord ratio') xCGvt = Variable('x_{CG_{vt}}', 'm', 'x-location of tail CG') y_eng = Variable('y_{eng}', 'm', 'Engine moment arm') ymac = Variable('y_{\\bar{c}_{vt}}', 'm', 'Spanwise location of mean aerodynamic chord') zmac = Variable('z_{\\bar{c}_{vt}}', 'm', 'Vertical location of mean aerodynamic chord') Vvt = Variable('V_{vt}', '-', 'Vertical Tail Volume Coefficient') Vvtmin = Variable('V_{vt_{min}}', '-', 'Minimum Vertical Tail Volume Coefficient') #engine values Te = Variable('T_e', 'N', 'Thrust per engine at takeoff') #variables specific to yaw rate sizing Vland = Variable('V_{land}', 'm/s', 'Aircraft Landing Speed') CLvyaw = Variable('C_{L_{vt,yaw}}', '-', 'VT CL at rotation') Iz = Variable('I_{z, max}', 'kg*m^2', 'Aircraft Z-axis Moment of Inertia') rreq = Variable('\\dot{r}_{req}', 's^-2', 'Required Yaw Rate at Landing') #constraints constraints = [] with SignomialsEnabled(): #non vectorized constraints constraints.extend([ #constraint tail Cl at flare CLvyaw == .85 * CLvmax, LvtEO == 0.5 * rho0 * V1**2 * Svt * CLvtEO, # Vertical tail force (y-direction) for engine out TCS([CLvtEO * (1 + clvtEO / (np.pi * e * Avt)) <= clvtEO]), #engine out CL computation Avt == bvt**2 / Svt, # Tail geometry relationship Svt <= bvt * (croot + ctip) / 2, # [SP] # Fuselage length constrains the tail trailing edge TCS([p >= 1 + 2 * taper]), TCS([2 * q >= 1 + p]), # Mean aerodynamic chord calculations ymac == (bvt / 3) * q / p, zmac == (bvt / 3) * q / p, ## TCS([(2./3)*(1 + taper + taper**2)*croot/q >= cma]), # [SP] SignomialEquality( (2. / 3) * (1 + taper + taper**2) * croot / q, cma), # Define vertical tail geometry taper == ctip / croot, # Moment arm and geometry -- same as for htail dxlead + croot <= dxtrail, TCS([dxlead + ymac * tanL + 0.25 * cma >= lvt], reltol=1e-2), # [SP] # TODO: Constrain taper by tip Reynolds number taper >= 0.25, #Enforce a minimum vertical tail volume Vvt >= Vvtmin, ]) return constraints
def setup(self): B = Variable('B', 'm', 'Landing gear base') E = Variable('E', 'GPa', 'Modulus of elasticity, 4340 steel') Eland = Variable('E_{land}', 'J', 'Max KE to be absorbed in landing') Fwm = Variable('F_{w_m}', '-', 'Weight factor (main)') Fwn = Variable('F_{w_n}', '-', 'Weight factor (nose)') I_m = Variable('I_m', 'm^4', 'Area moment of inertia (main strut)') I_n = Variable('I_n', 'm^4', 'Area moment of inertia (nose strut)') K = Variable('K', '-', 'Column effective length factor') L_m = Variable('L_m', 'N', 'Max static load through main gear') L_n = Variable('L_n', 'N', 'Min static load through nose gear') L_n_dyn = Variable('L_{n_{dyn}}', 'N', 'Dyn. braking load, nose gear') Lwm = Variable('L_{w_m}', 'N', 'Static load per wheel (main)') Lwn = Variable('L_{w_n}', 'N', 'Static load per wheel (nose)') N_s = Variable('N_s', '-', 'Factor of safety') S_sa = Variable('S_{sa}', 'm', 'Stroke of the shock absorber') ## S_t = Variable('S_t', 'm', 'Tire deflection') T = Variable('T', 'm', 'Main landing gear track') WAWm = Variable('W_{wa,m}', 'lbf', 'Wheel assembly weight for single main gear wheel') WAWn = Variable('W_{wa,n}', 'lbf', 'Wheel assembly weight for single nose gear wheel') ## W_0 = Variable('W_{0_{lg}}', 'N', ## 'Weight of aircraft excluding landing gear') Clg = Variable('C_{lg}', 1, '-', 'Landing Gear Weight Margin/Sens Factor') W_lg = Variable('W_{lg}', 'N', 'Weight of landing gear') W_mg = Variable('W_{mg}', 'N', 'Weight of main gear') W_ms = Variable('W_{ms}', 'N', 'Weight of main struts') W_mw = Variable('W_{mw}', 'N', 'Weight of main wheels (per strut)') W_ng = Variable('W_{ng}', 'N', 'Weight of nose gear') W_ns = Variable('W_{ns}', 'N', 'Weight of nose strut') W_nw = Variable('W_{nw}', 'N', 'Weight of nose wheels (total)') d_oleo = Variable('d_{oleo}', 'm', 'Diameter of oleo shock absorber') dtm = Variable('d_{t_m}', 'in', 'Diameter of main gear tires') dtn = Variable('d_{t_n}', 'in', 'Diameter of nose gear tires') dxm = Variable('\\Delta x_m', 'm', 'Distance b/w main gear and CG') dxn = Variable('\\Delta x_n', 'm', 'Distance b/w nose gear and CG') eta_s = Variable('\\eta_s', '-', 'Shock absorber efficiency') faddm = Variable('f_{add,m}', '-', 'Proportional added weight, main') faddn = Variable('f_{add,n}', '-', 'Proportional added weight, nose') g = Variable('g', 9.81, 'm/s^2', 'Gravitational acceleration') h_nac = Variable('h_{nacelle}', 'm', 'Min. nacelle clearance') hhold = Variable('h_{hold}', 'm', 'Hold height') l_m = Variable('l_m', 'm', 'Length of main gear') l_n = Variable('l_n', 'm', 'Length of nose gear') l_oleo = Variable('l_{oleo}', 'm', 'Length of oleo shock absorber') lam = Variable('\\lambda_{LG}', '-', 'Ratio of max to static load') # Torenbeek p360 n_mg = Variable('n_{mg}', '-', 'Number of main gear struts') nwps = Variable('n_{wps}', '-', 'Number of wheels per strut') p_oleo = Variable('p_{oleo}', 'lbf/in^2', 'Oleo pressure') #p_t = Variable('p_t', 170, 'lbf/in^2', 'Tyre pressure') r_m = Variable('r_m', 'm', 'Radius of main gear struts') r_n = Variable('r_n', 'm', 'Radius of nose gear struts') rho_st = Variable('\\rho_{st}', 'kg/m^3', 'Density of 4340 Steel') sig_y_c = Variable('\\sigma_{y_c}', 'Pa', 'Compressive yield strength 4340 steel') # AZOM t_m = Variable('t_m', 'm', 'Thickness of main gear strut wall') t_n = Variable('t_n', 'm', 'Thickness of nose gear strut wall') t_nac = Variable('t_{nacelle}', 'm', 'Nacelle thickness') tan_15 = Variable('\\tan(\\phi_{min})', '-', 'Lower bound on phi') tan_63 = Variable('\\tan(\\psi_{max})', '-', 'Upper bound on psi') tan_gam = Variable('\\tan(\\gamma)', '-', 'Dihedral angle') tan_phi = Variable('\\tan(\\phi)', '-', 'Angle b/w main gear and CG') tan_psi = Variable('\\tan(\\psi)', '-', 'Tip over angle') tan_th = Variable('\\tan(\\theta_{max})', '-', 'Max rotation angle') w_ult = Variable('w_{ult}', 'ft/s', 'Ultimate velocity of descent') wtm = Variable('w_{t_m}', 'm', 'Width of main tires') wtn = Variable('w_{t_n}', 'm', 'Width of nose tires') x_m = Variable('x_m', 'm', 'x-location of main gear') x_n = Variable('x_n', 'm', 'x-location of nose gear') x_upswp = Variable('x_{up}', 'm', 'Fuselage upsweep point') xcglg = Variable('x_{CG_{lg}}', 'm', 'Landing gear CG') y_m = Variable('y_m', 'm', 'y-location of main gear (symmetric)') z_CG_0 = Variable('z_{CG}', 'm', 'CG height relative to bottom of fuselage') zwing = Variable('z_{wing}', 'm', 'Height of wing relative to base of fuselage') d_nac = Variable('d_{nacelle}', 'm', 'Nacelle diameter') with SignomialsEnabled(): objective = W_lg constraints = [ # Track and Base geometry definitions TCS([l_n+zwing+y_m*tan_gam>=l_m], reltol=1E-3), #[SP] T == 2*y_m, TCS([x_n + B <= x_m]), x_n >= 5*units.m, # nose gear after nose # Longitudinal tip over (static) tan_phi == tan_15, # Lateral tip over in turn (dynamic) # www.dept.aoe.vt.edu/~mason/Mason_f/M96SC03.pdf # stricter constraint uses forward CG # cos(arctan(y/x))) = x/sqrt(x^2 + y^2) 1 >= (z_CG_0 + l_m)**2 * (y_m**2 + B**2) / (dxn * y_m * tan_psi)**2, tan_psi <= tan_63, # Tail strike: Longitudinal ground clearance in # takeoff, landing (Raymer says 10-15 degrees) # TODO?: 2 cases:(i) upsweep angle > rotation angle, # (ii) upsweep angle < rotation ang x_upswp - x_m <= l_m/tan_th, # [SP] # Size/Volume for retraction y_m >= l_m, # Brake sizing for stopping aircraft # Hard landing # http://www.boeing.com/commercial/aeromagazine/... # articles/qtr_3_07/AERO_Q307_article3.pdf # sink rate of 10 feet per second at the maximum # design landing weight # Landing condition from Torenbeek p360 ## Eland >= W/(2*g)*w_ult**2, # Torenbeek (10-26) # S_t == 0.5*lam*Lwm/(p*(dtm*bt)**0.5), # (10-30) S_sa == (1/eta_s)*(Eland/(L_m*lam)),# - eta_t*S_t), # [SP] Torenbeek (10-28) l_oleo == 2.5*S_sa, # Raymer 244 d_oleo == 1.3*(4*lam*L_m/n_mg/(np.pi*p_oleo))**0.5, l_m >= l_oleo + dtm/2, # Wheel weights Fwm == Lwm*dtm/(1000*Lwm.units*dtm.units), WAWm == 1.2*Fwm**0.609*units.lbf,# Currey p145 Fwn == Lwn*dtn/(1000*units.lbf*units.inches), WAWn == 1.2*Fwn**0.609*units.lbf,# Currey p145 Lwm == L_m/(n_mg*nwps), Lwn == L_n/nwps, # Main wheel diameter/width (Raymer p233) dtm == 1.63*(Lwm/(4.44*units.N))**0.315*units.inch, wtm == 0.1043*(Lwm/(4.44*units.N))**0.48*units.inch, dtn == 0.8*dtm, wtn == 0.8*wtm, # TODO: Beam sizing and max bending (limits track, # downward pressure on y_m) # Weight is a function of height and load through # each strut as well as tyre size (and obviously # number of struts) # Main gear strut weight (for a single strut) is a # function of length and load passing through strut W_ms >= 2*np.pi*r_m*t_m*l_m * rho_st * g, # Compressive yield in hard landing condition N_s * lam * L_m/n_mg <= sig_y_c * (2*np.pi*r_m*t_m), W_mw == nwps*WAWm, # Nose gear strut weight is a function of length and # load passing through strut W_ns >= 2*np.pi*r_n*t_n*l_n * rho_st * g, # find cross sectional area based on compressive yield N_s * (L_n + L_n_dyn) <= sig_y_c*(2*np.pi*r_n*t_n), W_nw >= nwps*WAWn, # Buckling constraint on main gear L_m <= np.pi**2*E*I_m/(K*l_m)**2, I_m == np.pi*r_m**3*t_m, # Buckling constraint on nose gear # source: https://en.wikipedia.org/wiki/Buckling L_n <= np.pi**2*E*I_n/(K*l_n)**2, I_n == np.pi*r_n**3*t_n, # Machining constraint # p89 Mason # www.dept.aoe.vt.edu/~mason/Mason_f/M96SC08.pdf 2*r_m/t_m <= 40, 2*r_m/t_n <= 40, # Retraction constraint on strut diameter 2*wtm + 2*r_m <= hhold, 2*wtn + 2*r_n <= 0.8*units.m, #TODO improve this # Weight accounting W_mg >= n_mg*(W_ms + W_mw*(1 + faddm)),# Currey p264 W_ng >= W_ns + W_nw*(1 + faddn), W_lg >= Clg * (W_mg + W_ng), #LG CG accounting TCS([W_lg*xcglg >= W_ng*x_n + W_mg*x_m], reltol=1E-2, raiseerror=False), x_m >= xcglg, ] return constraints
def setup(self, wing, state, **kwargs): self.wing = wing #declare variables #Vector Variables alpha = Variable('\\alpha_w', '-', 'Wing angle of attack') CLaw = Variable('C_{L_{\\alpha,w}}', '-', 'Lift curve slope, wing') Re = Variable('Re_w', '-', 'Reynolds number (wing)') CDp = Variable('C_{D_{p_w}}', '-', 'Wing parasitic drag coefficient') CDi = Variable('C_{D_{i_w}}', '-', 'Wing induced drag coefficient') CDw = Variable('C_{d_w}', '-', 'Drag coefficient, wing') CLw = Variable('C_{L}', '-', 'Lift coefficient, wing') D = Variable('D_{wing}', 'N', 'Wing drag') Lw = Variable('L_w', 'N', 'Wing lift') # Center wing section lift reduction variables dLo = Variable('\\Delta L_{o}','N','Center wing lift loss') etao = Variable('\\eta_{o}','-','Center wing span coefficient') po = Variable('p_{o}','N/m','Center section theoretical wing loading') fLo = Variable('f_{L_{o}}',0.5,'-','Center wing lift reduction coefficient') # Wing tip lift reduction variables dLt = Variable('\\Delta L_{t}','N','Wing tip lift loss') fLt = Variable('f_{L_{t}}',0.05,'-','Wing tip lift reduction coefficient') #wing moment variables -- need a good way to model this, currently using TAT cmw = Variable('c_{m_{w}}', '-', 'Wing Pitching Moment Coefficient') amax = Variable('\\alpha_{max,w}', '-', 'Max angle of attack') #make constraints constraints = [] with SignomialsEnabled(): constraints.extend([ 0.5*state['\\rho']*state['V']**2*self.wing['S']*CLw >= Lw + dLo + 2.*dLt, dLo == etao*fLo*self.wing['b']/2*po, dLt == fLt*po*self.wing['c_{root}']*self.wing['taper']**2, # TODO improve approximations croot~co and taper~gammat # DATCOM formula (Mach number makes it SP) # Swept wing lift curve slope constraint SignomialEquality((self.wing['AR']/self.wing['\\eta'])**2*(1 + self.wing['\\tan(\\Lambda)']**2 - state['M']**2) + 8*pi*self.wing['AR']/CLaw , (2*pi*self.wing['AR']/CLaw)**2), CLw == CLaw*alpha, alpha <= amax, # Drag D == 0.5*state['\\rho']*state['V']**2*self.wing['S']*CDw, TCS([CDw >= CDp + CDi]), TCS([CDi >= self.wing['TipReduct']*CLw**2/(pi*(self.wing['e'])*self.wing['AR'])]), Re == state['\\rho']*state['V']*self.wing['mac']/state['\\mu'], #original Philippe thesis fit ## TCS([CDp**6.5 >= (1.02458748e10 * CLw**15.587947404823325 * (self.wing['\\cos(\\Lambda)']*state['M'])**156.86410659495155 + ## 2.85612227e-13 * CLw**1.2774976672501526 * (self.wing['\\cos(\\Lambda)']*state['M'])**6.2534328002723703 + ## 2.08095341e-14 * CLw**0.8825277088649582 * (self.wing['\\cos(\\Lambda)']*state['M'])**0.0273667615730107 + ## 1.94411925e+06 * CLw**5.6547413360261691 * (self.wing['\\cos(\\Lambda)']*state['M'])**146.51920742858428)]), ## ]) #Martin's TASOPT c series airfoil fit TCS([ CDp**1.6515 >= 1.61418 * (Re/1000)**-0.550434 * (self.wing['\\tau'])**1.29151 * (self.wing['\\cos(\\Lambda)']*state['M'])**3.03609 * CLw**1.77743 + 0.0466407 * (Re/1000)**-0.389048 * (self.wing['\\tau'])**0.784123 * (self.wing['\\cos(\\Lambda)']*state['M'])**-0.340157 * CLw**0.950763 + 190.811 * (Re/1000)**-0.218621 * (self.wing['\\tau'])**3.94654 * (self.wing['\\cos(\\Lambda)']*state['M'])**19.2524 * CLw**1.15233 + 2.82283e-12 * (Re/1000)**1.18147 * (self.wing['\\tau'])**-1.75664 * (self.wing['\\cos(\\Lambda)']*state['M'])**0.10563 * CLw**-1.44114 ]), ]) return constraints
def setup(self, **kwargs): #declare variables #Variables Afuel = Variable('\\bar{A}_{fuel, max}', '-', 'Non-dim. fuel area') CLwmax = Variable('C_{L_{w,max}}', '-', 'Max lift coefficient, wing') Vfuel = Variable('V_{fuel, max}', 'm^3', 'Available fuel volume') cosL = Variable('\\cos(\\Lambda)', '-', 'Cosine of quarter-chord sweep angle') croot = Variable('c_{root}', 'm', 'Wing root chord') ctip = Variable('c_{tip}', 'm', 'Wing tip chord') eta = Variable('\\eta', '-', 'Lift efficiency (diff b/w sectional, actual lift)') fl = Variable('f(\\lambda_w)', '-', 'Empirical efficiency function of taper') g = Variable('g', 9.81, 'm/s^2', 'Gravitational acceleration') p = Variable('p', '-', 'Substituted variable = 1 + 2*taper') q = Variable('q', '-', 'Substituted variable = 1 + taper') rho0 = Variable('\\rho_0', 1.225, 'kg/m^3', 'Air density (0 ft)') rhofuel = Variable('\\rho_{fuel}', 'kg/m^3', 'Density of fuel') tanL = Variable('\\tan(\\Lambda)', '-', 'Tangent of quarter-chord sweep angle') taper = Variable('\\lambda', '-', 'Wing taper ratio') tau = Variable('\\tau', '-', 'Wing thickness/chord ratio') tau_max = Variable('\\tau_{max_w}', '-', 'Max allowed wing thickness') wwn = Variable('wwn', 0.5, '-', 'Wingbox-width-to-chord ratio') xw = Variable('x_w', 'm', 'Position of wing aerodynamic center') ymac = Variable('y_{mac}', 'm', 'Spanwise location of mean aerodynamic chord') bmax = Variable('b_{max}', 'm', 'Max Wing Span') #Linked Variables AR = Variable('AR', '-', 'Wing aspect ratio') Lmax = Variable('L_{max}', 'N', 'Maximum load') Sw = Variable('S', 'm^2', 'Wing area') WfuelWing = Variable('W_{fuel_{wing}}', 'N', 'Fuel weight') b = Variable('b', 'm', 'Wing span') mac = Variable('mac', 'm', 'Mean aerodynamic chord (wing)') e = Variable('e', '-', 'Oswald efficiency factor') FuelFrac = Variable('FuelFrac', '-', 'Usability Factor of Max Fuel Volume') #fractional componenet weights fflap = Variable('f_{flap}', '-', 'Flap Fractional Weight') fslat = Variable('f_{slat}', '-', 'Slat Fractional Weight') faile = Variable('f_{aileron}', '-', 'Aileron Fractional Weight') flete = Variable('f_{lete}', '-', 'Lete Fractional Weight') fribs = Variable('f_{ribs}', '-', 'Rib Fractional Weight') fspoi = Variable('f_{spoiler}', '-', 'Spoiler Fractional Weight') fwatt = Variable('f_{watt}', '-', 'Watt Fractional Weight') # Area fractions Atri = Variable('A_{tri}','m^2','Triangular Wing Area') Arect = Variable('A_{rect}','m^2','Rectangular Wing Area') #make constraints constraints = [] with SignomialsEnabled(): constraints.extend([ Arect == ctip*b, Atri >= 1./2.*(1-taper)*croot*b, #[SP] p >= 1 + 2*taper, 2*q >= 1 + p, ymac == (b/3)*q/p, TCS([(2./3)*(1+taper+taper**2)*croot/q <= mac], reltol=1E-2), taper == ctip/croot, SignomialEquality(Sw, b*(croot + ctip)/2), # Oswald efficiency # Nita, Scholz, "Estimating the Oswald factor from # basic aircraft geometrical parameters" TCS([fl >= 0.0524*taper**4 - 0.15*taper**3 + 0.1659*taper**2 - 0.0706*taper + 0.0119], reltol=1E-2), TCS([e*(1 + fl*AR) <= 1]), taper >= 0.15, # TODO # Fuel volume [TASOPT doc] # GP approx of the signomial constraint: Vfuel <= .3026*mac**2*b*tau, WfuelWing <= rhofuel*Vfuel*g, b <= bmax, ]) return constraints
def setup(self, substitutions=None, **kwargs): eng = 0 #define the number of each flight segment Ncruise = 2 # vectorize with Vectorize(Ncruise): enginestate = FlightState() ac = Aircraft(0, Ncruise, enginestate, eng) #Vectorize with Vectorize(Ncruise): cruise = CruiseSegment(ac) statelinking = StateLinking(cruise.state, enginestate, Ncruise) #declare new variables W_ftotal = Variable('W_{f_{total}}', 'N', 'Total Fuel Weight') W_fcruise = Variable('W_{f_{cruise}}', 'N', 'Fuel Weight Burned in Cruise') W_total = Variable('W_{total}', 'N', 'Total Aircraft Weight') CruiseAlt = Variable('CruiseAlt', 'ft', 'Cruise Altitude [feet]') ReqRng = Variable('ReqRng', 'nautical_miles', 'Required Cruise Range') hftCruise = cruise['hft'] #make overall constraints constraints = [] constraints.extend([ #weight constraints TCS([ ac['W_{e}'] + ac['W_{payload}'] + W_ftotal + ac['numeng'] * ac['W_{engine}'] + ac['W_{wing}'] <= W_total ]), cruise['W_{start}'][0] == W_total, TCS([ cruise['W_{start}'] >= cruise['W_{end}'] + cruise['W_{burn}'] ]), cruise['W_{start}'][1:] == cruise['W_{end}'][:-1], TCS([ ac['W_{e}'] + ac['W_{payload}'] + ac['numeng'] * ac['W_{engine}'] + ac['W_{wing}'] <= cruise['W_{end}'][-1] ]), TCS([W_ftotal >= W_fcruise]), TCS([W_fcruise >= sum(cruise['W_{burn}'])]), #altitude constraints hftCruise == CruiseAlt, CruiseAlt >= 30000 * units('ft'), #set the range for each cruise segment, doesn't take credit for climb #down range disatnce covered cruise.cruiseP['Rng'] == ReqRng / (Ncruise), #compute fuel burn from TSFC cruise['W_{burn}'] == ac['numeng'] * ac.engine['TSFC'] * cruise['thr'] * ac.engine['F'], ]) M2 = .8 M25 = .6 M4a = .1025 M0 = .8 enginecruise = [ ac.engine.engineP['M_2'][0] == cruise['M'][0], ac.engine.engineP['M_2'][1] == cruise['M'][1], ac.engine.engineP['M_{2.5}'][1] == M25, ac.engine.engineP['M_{2.5}'][0] == M25, ac.engine.engineP['hold_{2}'] == 1 + .5 * (1.398 - 1) * M2**2, ac.engine.engineP['hold_{2.5}'] == 1 + .5 * (1.354 - 1) * M25**2, ac.engine.engineP['c1'] == 1 + .5 * (.401) * M0**2, #steady level flight constraint on D cruise['D'] == ac['numeng'] * ac.engine['F'], #breguet range eqn TCS([ cruise['z_{bre}'] >= (ac.engine['TSFC'] * cruise['thr'] * cruise['D']) / cruise['W_{avg}'] ]), ] # Model.setup(self, W_ftotal + s*units('N'), constraints + ac + climb + cruise, subs) return constraints + ac + cruise + enginecruise + enginestate + statelinking
def setup(self, surface, surfacetype): # Variables Icap = Variable('I_{cap}', '-', 'Non-dim spar cap area moment of inertia') Mr = Variable('M_r', 'N', 'Root moment per root chord') nu = Variable('\\nu', '-', 'Dummy variable = $(t^2 + t + 1)/(t+1)^2$') Wcap = Variable('W_{cap}', 'N', 'Weight of spar caps') Wweb = Variable('W_{web}', 'N', 'Weight of shear web') Wstruct = Variable('W_{struct}', 'N', 'Structural weight') # Constants g = Variable('g', 9.81, 'm/s^2', 'Gravitational acceleration') Nlift = Variable('N_{lift}', '-', 'Wing loading multiplier') rh = Variable('r_h', 0.75, '-', 'Fractional wing thickness at spar web') rhocap = Variable('\\rho_{cap}', 2700, 'kg/m^3', 'Density of spar cap material') rhoweb = Variable('\\rho_{web}', 2700, 'kg/m^3', 'Density of shear web material') sigmax = Variable('\\sigma_{max}', 250e6, 'Pa', 'Allowable tensile stress') sigmaxshear = Variable('\\sigma_{max,shear}', 167e6, 'Pa', 'Allowable shear stress') wwb = Variable('r_{w/c}', 0.5, '-', 'Wingbox width-to-chord ratio') tcap = Variable('t_{cap}', '-', 'Non-dim. spar cap thickness') tweb = Variable('t_{web}', '-', 'Non-dim. shear web thickness') objective = Wstruct if surfacetype == "wing": taper = Variable('taper', '-', 'Taper ratio') AR = surface['AR'] b = surface['b'] S = surface['S'] p = surface['p'] q = surface['q'] tau = surface['\\tau'] tau_max = surface['\\tau_{max_w}'] Lmax = surface['L_{max}'] elif surfacetype == "vertical_tail": taper = Variable('taper', '-', 'Taper ratio') #factors of 2 required since the VT is only half span and the wing model #is for a full span wing AR = Variable('AR_{vt}', '-', 'Vertical tail aspect ratio (double span)') b = 2. * surface['b_{vt}'] S = 2. * surface['S_{vt}'] p = surface['p_{vt}'] q = surface['q_{vt}'] tau = surface['\\tau_{vt}'] Lmax = 2. * surface['L_{vt_{max}}'] taper = surface['\\lambda_{vt}'] elif surfacetype == "horizontal_tail": taper = Variable('taper', 0.3, '-', 'Taper ratio') AR = surface['AR_{ht}'] b = surface['b_{ht}'] S = surface['S_{ht}'] p = surface['p_{ht}'] q = surface['q_{ht}'] tau = surface['\\tau_{ht}'] Lmax = surface['L_{ht_{max}}'] # Pi tail sizing variables # Splits the max lift into triangular and rectangular components # for root bending sizing. bhtout = Variable('b_{ht_{out}}', 'm', 'Horizontal tail outboard half-span') Lhtri = Variable('L_{ht_{tri}}', 'N', 'Triangular HT load') Lhrect = Variable('L_{ht_{rect}}', 'N', 'Rectangular HT load') Lhtriout = Variable('L_{ht_{tri_{out}}}', 'N', 'Triangular HT load outboard') Lhrectout = Variable('L_{ht_{rect_{out}}}', 'N', 'Rectangular HT load outboard') Mrout = Variable('M_{r_{out}}', 'N', 'Wing moment at pin joint ') #TODO Lshear = Variable('L_{shear}', 'N', 'Maximum shear load (at pin joint)') piMfac = Variable('\\pi_{M-fac}', '-', 'Pi-tail bending structural factor') constraints = [ # Aspect ratio definition AR == b**2 / S, # Root stiffness (see Hoburg 2014) # Assumes rh = 0.75, so that rms box height = ~0.92*tmax TCS([ 0.92 * wwb * tau * tcap**2 + Icap <= 0.92**2 / 2 * wwb * tau**2 * tcap ]), # Posynomial approximation of nu=(1+lam+lam^2)/(1+lam)^2 nu**3.94 >= 0.86 * p**(-2.38) + 0.14 * p**0.56, # Woody's fit # Weight of spar caps and shear webs Wweb >= 8 * rhoweb * g * rh * tau * tweb * S**1.5 * nu / (3 * AR**0.5), ] if surfacetype == "wing": constraints += [ # Upper bound on maximum thickness tau <= tau_max, Wstruct >= (Wweb + Wcap), # Shear web sizing # Assumes all shear loads are carried by web and rh=0.75 TCS([12 >= AR * Lmax * q**2 / (tau * S * tweb * sigmaxshear)]), # Weight of spar caps and shear webs Wcap >= 8 * rhocap * g * wwb * tcap * S**1.5 * nu / (3 * AR**0.5), Nlift == 3, # Stress limit # Assumes bending stress carried by caps (Icap >> Iweb) TCS([8 >= Mr * AR * q**2 * tau / (S * Icap * sigmax)]), ] elif surfacetype == "vertical_tail": constraints += [ # Upper bound on maximum thickness tau <= 0.14, Wstruct >= 0.5 * (Wweb + Wcap), # Root moment calculation (see Hoburg 2014) # Assumes lift per unit span proportional to local chord Mr >= Lmax * AR * p / 24, # Shear web sizing # Assumes all shear loads are carried by web and rh=0.75 TCS([ 12 >= AR * Lmax * Nlift * q**2 / (tau * S * tweb * sigmaxshear) ]), # Weight of spar caps and shear webs Wcap >= 8 * rhocap * g * wwb * tcap * S**1.5 * nu / (3 * AR**0.5), Nlift == 1, # Stress limit # Assumes bending stress carried by caps (Icap >> Iweb) TCS([8 >= Nlift * Mr * AR * q**2 * tau / (S * Icap * sigmax)]), ] elif surfacetype == "horizontal_tail": constraints += [ # Upper bound on maximum thickness tau <= 0.14, Wstruct >= (Wweb + Wcap), Lhtriout >= Lhtri * bhtout**2 / (0.5 * b)**2, Lhrectout >= Lhrect * bhtout / (0.5 * b), 12 >= 2 * AR * Lshear * Nlift * q**2 / (tau * S * tweb * sigmaxshear), #TODO Wcap >= piMfac * 8 * rhocap * g * wwb * tcap * S**1.5 * nu / (3 * AR**0.5), #TODO Nlift == 1, # Stress limit # Assumes bending stress carried by caps (Icap >> Iweb) TCS([8 >= Nlift * Mr * AR * q**2 * tau / (S * Icap * sigmax)]), ] return constraints