示例#1
0
    def vs_keas(self, loadfactor):
        """Equivalent air speed in knots for stall at design weight, for some loadfactor"""

        if self.acobj.weight_n is False:
            defmsg = 'Maximum take-off weight was not specified in the design definitions dictionary'
            raise ValueError(defmsg)

        if self.acobj.wingarea_m2 is False:
            defmsg = 'Reference wing area was not specified in the design definitions dictionary'
            raise ValueError(defmsg)

        weight_n = self.acobj.weight_n
        wingarea_m2 = self.acobj.wingarea_m2

        if loadfactor >= 0:

            if self.acobj.clmaxclean is False:
                perfmsg = 'Maximum lift coefficient in clean configuration was not specified in performance dictionary'
                raise ValueError(perfmsg)
            cl = self.acobj.clmaxclean

        else:

            if self.acobj.clminclean is False:
                perfmsg = 'Minimum lift coefficient in clean configuration was not specified in performance dictionary'
                raise ValueError(perfmsg)
            cl = self.acobj.clminclean

        rho0_kgm3 = self.acobj.designatm.airdens_kgpm3(altitudes_m=0)
        vs_keas = co.mps2kts(
            math.sqrt((loadfactor * weight_n) /
                      (0.5 * rho0_kgm3 * wingarea_m2 * cl)))

        return vs_keas
示例#2
0
    def vstall_kias(self, wingloadinglist_pa, clmax):
        """Calculates the stall speed (indicated) for a given wing loading
        in a specified cofiguration.

        **Parameters:**

        wingloading_pa
            float or numpy array, list of wing loading values in Pa.

        clmax
            maximum lift coefficient (float) or the name of a standard configuration
            (string) for which a maximum lift coefficient was specified in the
            :code:`performance` dictionary (currently implemented: 'take-off').

        **Returns:**

        stall speed in knots (float or numpy array)

        **Note:**

        The calculation is performed assuming standard day ISA sea level
        conditions (not in the consitions specified in the atmosphere used
        when instantiating the :code:`AircraftConcept` object!) so the
        speed returned is an indicated (IAS) / calibrated (CAS) value.

        **Example**::

            from ADRpy import constraintanalysis as ca

            designperformance = {'CLmaxTO':1.6}

            concept = ca.AircraftConcept({}, {}, designperformance, {})

            wingloading_pa = 3500

            print("VS1(take-off):",
                concept.vstall_kias(wingloading_pa, 'take-off'))

        """

        isa = at.Atmosphere()
        rho0_kgpm3 = isa.airdens_kgpm3()

        if clmax == 'take-off':
            clmax = self.clmaxto

        vs_mps = math.sqrt(2 * wingloadinglist_pa / (rho0_kgpm3 * clmax))

        return co.mps2kts(vs_mps)
示例#3
0
    def keas2kcas(self, keas, altitude_m):
        """Converts equivalent airspeed into calibrated airspeed.

        The relationship between the two depends on the Mach number :math:`M` and the
        ratio :math:`\\delta` of the pressure at the current altitude
        :math:`P_\\mathrm{alt}` and the sea level pressure :math:`P_\\mathrm{0}`.
        We approximate this relationship with the expression:

        .. math::

            \\mathrm{CAS}\\approx\\mathrm{EAS}\\left[1 + \\frac{1}{8}(1-\\delta)M^2 +
            \\frac{3}{640}\\left(1-10\\delta+9\\delta^2 \\right)M^4 \\right]

        **Parameters**

        keas
            float or numpy array, equivalent airspeed in knots.

        altitude_m
            float, altitude in metres.

        **Returns**

        kcas
            float or numpy array, calibrated airspeed in knots.

        mach
            float, Mach number.

        **See also** ``mpseas2mpscas``

        **Notes**

        The reverse conversion is slightly more complicated, as their relationship
        depends on the Mach number. This, in turn, requires the computation of the
        true airspeed and that can only be computed from EAS, not CAS. The unit-
        specific nature of the function is also the result of the need for computing
        the Mach number.

        **Example** ::

            import numpy as np
            from ADRpy import atmospheres as at
            from ADRpy import unitconversions as co

            isa = at.Atmosphere()

            keas = np.array([100, 200, 300])
            altitude_m = co.feet2m(40000)

            kcas, mach = isa.keas2kcas(keas, altitude_m)

            print(kcas)

        Output: ::

            [ 101.25392563  209.93839073  333.01861569]

        """
        # Note: unit specific, as the calculation requires Mach no.
        np.asarray(keas)
        mpseas = co.kts2mps(keas)
        mpscas, machno = self.mpseas2mpscas(mpseas, altitude_m)
        kcas = co.mps2kts(mpscas)
        return kcas, machno
示例#4
0
 def vsound_kts(self, altitudes_m=0):
     """Speed of sound in knots."""
     return co.mps2kts(self.vsound_mps(altitudes_m))
示例#5
0
    def _paragraph335(self):
        """Design airspeeds, as per CS-23.335 (see also 14 CFR 23.335).

        For all categories of aircraft, this specification item produces limits for design
        airspeeds.

        **Outputs:**

        eas_dict
            dictionary, containing minimum and maximum allowable design airspeeds in KEAS.

        """

        if self.cruisespeed_keas is False:
            cruisemsg = 'Cruise speed not specified in the csbrief or designbrief dictionary.'
            raise ValueError(cruisemsg)
        vc_keas = self.cruisespeed_keas

        if self.acobj.clmaxclean is False:
            perfmsg = 'CLmaxclean must be specified in the performance dictionary.'
            raise ValueError(perfmsg)
        clmaxclean = self.acobj.clmaxclean

        if self.acobj.weight_n is False:
            designmsg = 'Maximum take-off weight must be specified in the design dictionary.'
            raise ValueError(designmsg)

        if self.acobj.wingarea_m2 is False:
            designmsg = 'Reference wing area must be specified in the design dictionary.'
            raise ValueError(designmsg)
        wingloading_pa = self.acobj.weight_n / self.acobj.wingarea_m2

        wingloading_lbft2 = co.pa2lbfft2(wingloading_pa)

        # Create a dictionary of empty dictionaries for each aircraft category
        cs23categories_list = ['norm', 'util', 'comm', 'aero']
        eas_dict = dict(
            zip(cs23categories_list,
                [{} for _ in range(len(cs23categories_list))]))

        # (a) Design cruising speed, V_C

        # (a)(1, 2)
        vcfactor_1i = np.interp(wingloading_lbft2, [20, 100], [33, 28.6])
        vcfactor_1ii = np.interp(wingloading_lbft2, [20, 100], [36, 28.6])

        eas_dict['norm'].update(
            {'vcmin_keas': vcfactor_1i * np.sqrt(wingloading_lbft2)})
        eas_dict['util'].update(
            {'vcmin_keas': vcfactor_1i * np.sqrt(wingloading_lbft2)})
        eas_dict['comm'].update(
            {'vcmin_keas': vcfactor_1i * np.sqrt(wingloading_lbft2)})
        eas_dict['aero'].update(
            {'vcmin_keas': vcfactor_1ii * np.sqrt(wingloading_lbft2)})

        # (a)(3)
        if self.maxlevelspeed_keas is False:
            for category in cs23categories_list:
                eas_dict[category].update({'vcsoftmax_keas': False})
        else:
            for category in cs23categories_list:
                eas_dict[category].update(
                    {'vcsoftmax_keas':
                     0.9 * self.maxlevelspeed_keas})  # This is not strict

        # (a)(4) Requires Mach

        # (b) Design dive speed, V_D

        # (b) (1, 2, 3)
        vdfactor_2i = np.interp(wingloading_lbft2, [20, 100], [1.4, 1.35])
        vdfactor_2ii = np.interp(wingloading_lbft2, [20, 100], [1.5, 1.35])
        vdfactor_2iii = np.interp(wingloading_lbft2, [20, 100], [1.55, 1.35])

        eas_dict['norm'].update({
            'vdmin_keas':
            np.fmax(1.25 * vc_keas,
                    vdfactor_2i * eas_dict['norm']['vcmin_keas'])
        })
        eas_dict['util'].update({
            'vdmin_keas':
            np.fmax(1.25 * vc_keas,
                    vdfactor_2ii * eas_dict['util']['vcmin_keas'])
        })
        eas_dict['comm'].update({
            'vdmin_keas':
            np.fmax(1.25 * vc_keas,
                    vdfactor_2i * eas_dict['comm']['vcmin_keas'])
        })
        eas_dict['aero'].update({
            'vdmin_keas':
            np.fmax(1.25 * vc_keas,
                    vdfactor_2iii * eas_dict['aero']['vcmin_keas'])
        })

        # (b)(4) Requires Mach

        # (c) Design manoeuvring speed, V_A

        # (c)(1, 2)
        wfract = self.weightfraction
        vs_keas = self.vs_keas(loadfactor=wfract)
        manoeuvrelimits = self._paragraph337()

        for category in cs23categories_list:
            eas_dict[category].update({
                'vamin_keas':
                vs_keas * math.sqrt(manoeuvrelimits[category]['npos_min'])
            })
            eas_dict[category].update({'vasoftmax_keas':
                                       vc_keas})  # This is not strict

        # (d) Design speed for maximum gust intensity, V_B

        # (d)(1)
        _, gustspeedsmps = self._paragraph333()
        gustloads, k_g, liftslope_prad = self._paragraph341(
            speedatgust_keas={'Uc': vc_keas})

        vs1_keas = self.vs_keas(loadfactor=wfract)

        for category in cs23categories_list:

            if category == 'comm':
                gust_de_mps = gustspeedsmps[category]['Ub_mps']
            else:
                gust_de_mps = gustspeedsmps[category]['Uc_mps']

            trueloading_pa = wingloading_pa * wfract
            rho0_kgm3 = self.acobj.designatm.airdens_kgpm3(altitudes_m=0)

            a = 1
            b = -(liftslope_prad / clmaxclean) * k_g * gust_de_mps
            c = -2 * trueloading_pa / (rho0_kgm3 * clmaxclean) * wfract
            vbmin1_keas = co.mps2kts((-b + (b**2 - 4 * a * c)**0.5) / (2 * a))
            eas_dict[category].update({'vbmin1_keas': vbmin1_keas})

            vbmin2_keas = vs1_keas * math.sqrt(gustloads[category]['npos_Uc'])
            eas_dict[category].update({'vbmin2_keas': vbmin2_keas})

            vbmin_keas = np.fmin(vbmin1_keas, vbmin2_keas)
            eas_dict[category].update({'vbmin_keas': vbmin_keas})

            # (d)(2)
            eas_dict[category].update(
                {'vbmax_keas': np.fmax(vbmin_keas, vc_keas)})

        return eas_dict
示例#6
0
def PlantSizing(plant, rEM=0.6):

    PP = plant.copy()

    isa = at.Atmosphere()

    # aircraft design concept object creation requires a design-brief
    designbrief = {
        'rwyelevation_m': 0,  # altitudue of the runway
        'groundrun_m': 50,  # maximumm allowed take-off distance
        'climbrate_fpm':
        3.5 * (60 / 0.3048),  # required climb rate that must be achieved
        'climbspeed_kias':
        co.mps2kts(40),  # indidcated airspeed requirement for climb
        'climbalt_m':
        5000,  # altitude at which the climb rate must be achieved
        'secclimbspd_kias':
        co.mps2kts(25),  # speed at which service ceiling is reached
        'cruisespeed_ktas': co.mps2kts(40),  # cruise velocity
        'cruisealt_m':
        5000,  # altitude at which the cruise speed must be achieved
        'cruisethrustfact': 1,  # ratio of cruise thrust to total thrust
        'servceil_m':
        5000,  # alt at which the max rate of climb drops to 100 ft/min
        # dummy values to prevent errors, not needed since no turns are simulated
        'turnspeed_ktas': 10,
        'stloadfactor': 1.5
    }

    # aircraft design concept object creation requires a design spec
    design = {
        'aspectratio': AC['AR'],
        'bpr':
        -3  # bypass ratio; -3 means no thrust correction (neglected for the aircraft)
    }

    # aircraft design concept object creation requires a performance estimate
    designperf = {
        'CDTO': GetCd(AC['maxPitch'], 0),  # take-off coefficient of drag
        'CLTO': GetCl(AC['maxPitch']),  # take-off coefficient of lift
        'CLmaxTO':
        GetCl(AC['maxPitch']),  # take-off maximum coefficient of lift
        'CLmaxclean': GetCl(AC['maxPitch']),  # max lift coefficient in flight,
        # with (non-existant) flaps retracted
        'CDminclean': AC['parasiteDrag'],  # min, zero lift drag coefficient
        'etaprop': {
            'take-off': 0.45,
            'climb': 0.75,
            'cruise': 0.85,
            'turn': 0.85,
            'servceil': 0.65
        },  # propeller efficiencies
    }

    # An aircraft concept object can now be instantiated
    concept = ca.AircraftConcept(designbrief, design, designperf, isa)

    tow = PP['battMassList'][-1] + PP['fullTankMass'] + AC['emptyMass']\
        + AC['payloadMass'] + PP['EMMass'] + PP['ICEMass']

    wingloading = tow * AC['g'] / AC['S']

    power = concept.powerrequired(wingloading, tow, feasibleonly=False)

    # select largest power requirement, covnert from HP to W
    powerReq = max(power['take-off'], power['climb'], power['cruise'])

    powerReq = co.hp2kw(powerReq) * 1000

    # power is satisfied by ICE and EM
    ICEPowerReq = (1 - rEM) * powerReq
    EMPowerReq = rEM * powerReq

    # power available before sizing
    ICEPower = 2 * math.pi * PP['ICEMaprps'][-1] * PP['ICEMapTorque'][-1]
    EMPower = 2 * math.pi * PP['maxEMrps'] * PP['maxEMTorque']

    ICEFactor = ICEPowerReq / ICEPower
    EMFactor = EMPowerReq / EMPower

    # resize the torque scales to get adjusted power limits
    PP['ICEMapTorque'] *= ICEFactor
    PP['maxEMTorque'] *= EMFactor

    PP['maxEMPower'] = 2 * math.pi * PP['maxEMrps'] * PP['maxEMTorque']

    # resize the mass
    PP['ICEMass'] *= ICEFactor
    PP['EMMass'] *= EMFactor

    # propeller sizing
    maxICETorque = PP['ICEMapTorque'][-1]
    maxICErps = PP['ICEMaprps'][-1]

    dFun = lambda D: maxICETorque - PropQFun(1, maxICErps, 0, D)

    PP['D'] = fsolve(dFun, 0.3)

    return PP
示例#7
0
    def _paragraph335(self, wingloading_pa):
        """EASA specification for CS-23 Design Airspeeds.

        For all categories of aircraft, this specification item produces limits for design
        airspeeds.

        **Parameters:**

        wingloading_pa
            float or array, list of wing-loading values in Pa.

        **Returns:**

        eas_dict
            dictionary, containing minimum and maximum allowable design airspeeds in KEAS.

        **Note:**

        This method is not fully implemented, a future revision would allow the cruise and
        design speeds to be selected as functions of Mach number based on compressibility
        limits. Furthermore, the cruise speed should be limited by maximum level flight speed
        but is not currently implemented.

        """
        if self.acobj.cruisealt_m is False:
            cruisemsg = "Cruise altitude not specified in the designbrief dictionary."
            raise ValueError(cruisemsg)
        rho_kgpm3 = self.acobj.designatm.airdens_kgpm3(self.acobj.cruisealt_m)

        if self.acobj.cruisespeed_ktas is False:
            cruisemsg = "Cruise speed not specified in the designbrief dictionary."
            raise ValueError(cruisemsg)
        vc_keas = co.tas2eas(self.acobj.cruisespeed_ktas, rho_kgpm3)

        if self.acobj.clmaxclean is False:
            perfmsg = "CLmaxclean must be specified in the performance dictionary."
            raise ValueError(perfmsg)
        clmaxclean = self.acobj.clmaxclean

        wingloading_pa = actools.recastasnpfloatarray(wingloading_pa)
        wingloading_lbft2 = co.pa2lbfft2(wingloading_pa)

        # Create a dictionary of empty dictionaries for each aircraft category
        cs23categories_list = ['norm', 'util', 'comm', 'aero']
        eas_dict = dict(
            zip(cs23categories_list,
                [{} for _ in range(len(cs23categories_list))]))

        # (a) Design cruising speed, V_C

        # (a)(1, 2)
        vcfactor_1i = np.interp(wingloading_lbft2, [20, 100], [33, 28.6])
        vcfactor_1ii = np.interp(wingloading_lbft2, [20, 100], [36, 28.6])

        eas_dict['norm'].update(
            {'vcmin_keas': vcfactor_1i * np.sqrt(wingloading_lbft2)})
        eas_dict['util'].update(
            {'vcmin_keas': vcfactor_1i * np.sqrt(wingloading_lbft2)})
        eas_dict['comm'].update(
            {'vcmin_keas': vcfactor_1i * np.sqrt(wingloading_lbft2)})
        eas_dict['aero'].update(
            {'vcmin_keas': vcfactor_1ii * np.sqrt(wingloading_lbft2)})

        # (a)(3) Requires vh_keas
        # (a)(4) Requires Mach

        # (b) Design dive speed, V_D

        # (b) (1, 2, 3)
        vdfactor_2i = np.interp(wingloading_lbft2, [20, 100], [1.4, 1.35])
        vdfactor_2ii = np.interp(wingloading_lbft2, [20, 100], [1.5, 1.35])
        vdfactor_2iii = np.interp(wingloading_lbft2, [20, 100], [1.55, 1.35])

        eas_dict['norm'].update({
            'vdmin_keas':
            np.fmax(1.25 * vc_keas,
                    vdfactor_2i * eas_dict['norm']['vcmin_keas'])
        })
        eas_dict['util'].update({
            'vdmin_keas':
            np.fmax(1.25 * vc_keas,
                    vdfactor_2ii * eas_dict['util']['vcmin_keas'])
        })
        eas_dict['comm'].update({
            'vdmin_keas':
            np.fmax(1.25 * vc_keas,
                    vdfactor_2i * eas_dict['comm']['vcmin_keas'])
        })
        eas_dict['aero'].update({
            'vdmin_keas':
            np.fmax(1.25 * vc_keas,
                    vdfactor_2iii * eas_dict['aero']['vcmin_keas'])
        })

        # (b)(4) Requires Mach

        # (c) Design manoeuvring speed, V_A

        # (c)(1, 2)
        vs_keas = self.vs_keas(loadfactor=1)
        manoeuvrelimits = self._paragraph337()

        for category in cs23categories_list:
            eas_dict[category].update({
                'vamin_keas':
                vs_keas * math.sqrt(manoeuvrelimits[category]['npos_min'])
            })
            eas_dict[category].update({'vamax_keas': vc_keas})

        # (d) Design speed for maximum gust intensity, V_B

        # (d)(1)
        _, gustspeedsmps = self._paragraph333()
        _, k_g, liftslope = self._paragraph341(
            wingloading_pa, speedatgust_keas={'Uc': vc_keas})

        cruisewfraction = self.acobj.cruise_weight_fraction
        vs1_keas = self.vs_keas(loadfactor=cruisewfraction)

        for category in cs23categories_list:

            if category == 'comm':
                gust_de_mps = gustspeedsmps[category]['Ub_mps']
            else:
                gust_de_mps = gustspeedsmps[category]['Uc_mps']

            cruiseloading_pa = wingloading_pa * self.acobj.cruise_weight_fraction
            rho0_kgm3 = self.acobj.designatm.airdens_kgpm3(altitudes_m=0)

            a = 1
            b = -(liftslope / clmaxclean) * k_g * gust_de_mps
            c = -2 * cruiseloading_pa / (rho0_kgm3 * clmaxclean)
            vbmin1_keas = co.mps2kts((-b + (b**2 - 4 * a * c)**0.5) / (2 * a))
            eas_dict[category].update({'vbmin1_keas': vbmin1_keas})

            vbmin2_keas = vs1_keas * math.sqrt(
                manoeuvrelimits[category]['npos_min'])
            eas_dict[category].update({'vbmin2_keas': vbmin2_keas})

            vbmin_keas = np.fmin(vbmin1_keas, vbmin2_keas)
            eas_dict[category].update({'vbmin_keas': vbmin_keas})

            # (d)(2)
            eas_dict[category].update(
                {'vbmax_keas': np.fmax(vbmin_keas, vc_keas)})

        return eas_dict