Beispiel #1
0
    def setup(self, alt, **kwargs):
        g = Variable('g', 'm/s^2', 'Gravitational acceleration')
        p_sl = Variable("p_{sl}", 101325, "Pa", "Pressure at sea level")
        T_sl = Variable("T_{sl}", 288.15, "K", "Temperature at sea level")
        L_atm = Variable("L_{atm}", .0065, "K/m", "Temperature lapse rate")
        M_atm = Variable("M_{atm}", .0289644, "kg/mol",
                         "Molar mass of dry air")
        p_atm = Variable("P_{atm}", "Pa", "air pressure")
        R_atm = Variable("R_{atm}", 8.31447, "J/mol/K", "air specific heating value")
        TH = 5.257386998354459 #(g*M_atm/R_atm/L_atm).value
        rho = Variable('\\rho', 'kg/m^3', 'Density of air')
        T_atm = Variable("T_{atm}", "K", "air temperature")

        """
        Dynamic viscosity (mu) as a function of temperature
        References:
        http://www-mdp.eng.cam.ac.uk/web/library/enginfo/aerothermal_dvd_only/aero/
            atmos/atmos.html
        http://www.cfd-online.com/Wiki/Sutherland's_law
        """
        mu  = Variable('\\mu', 'kg/(m*s)', 'Dynamic viscosity')

        T_s = Variable('T_s', 110.4, "K", "Sutherland Temperature")
        C_1 = Variable('C_1', 1.458E-6, "kg/(m*s*K^0.5)",
                       'Sutherland coefficient')

        with SignomialsEnabled():
            constraints = [
                # Pressure-altitude relation
                (p_atm/p_sl)**(1/5.257) == T_atm/T_sl,

                # Ideal gas law
                rho == p_atm/(R_atm/M_atm*T_atm),

                #temperature equation
                SignomialEquality(T_sl, T_atm + L_atm*alt['h']),

                #constraint on mu
                SignomialEquality((T_atm + T_s) * mu, C_1 * T_atm**1.5),
##                TCS([(T_atm + T_s) * mu >= C_1 * T_atm**1.5])
                ]

        #like to use a local subs here in the future
        subs = None

        return constraints
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #4
0
    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
Beispiel #5
0
    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, 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