def test_softmax(plot=False):
    # Test softmax
    x = np.linspace(-10, 10, 100)
    y1 = x
    y2 = -2 * x - 3
    hardness = 0.5

    y_soft = np.softmax(y1, y2, hardness=hardness)

    assert np.softmax(0, 0, hardness=1) == np.log(2)

    if plot:
        import matplotlib.pyplot as plt
        import seaborn as sns

        sns.set(font_scale=1)

        fig, ax = plt.subplots(1, 1, figsize=(6.4, 4.8), dpi=200)
        plt.plot(x, y1, label="y1")
        plt.plot(x, y2, label="y2")
        plt.plot(x, y_soft, label="softmax")
        plt.xlabel(r"x")
        plt.ylabel(r"y")
        plt.title(r"Softmax")
        plt.tight_layout()
        plt.legend()
        plt.show()
Ejemplo n.º 2
0
def Cf_flat_plate(Re_L: float, method="hybrid-sharpe-convex") -> float:
    """
    Returns the mean skin friction coefficient over a flat plate.

    Don't forget to double it (two sides) if you want a drag coefficient.

    Args:

        Re_L: Reynolds number, normalized to the length of the flat plate.

        method: The method of computing the skin friction coefficient. One of:

            * "blasius": Uses the Blasius solution. Citing Cengel and Cimbala, "Fluid Mechanics: Fundamentals and
            Applications", Table 10-4.

                Valid approximately for Re_L <= 5e5.

            * "turbulent": Uses turbulent correlations for smooth plates. Citing Cengel and Cimbala,
            "Fluid Mechanics: Fundamentals and Applications", Table 10-4.

                Valid approximately for 5e5 <= Re_L <= 1e7.

            * "hybrid-cengel": Uses turbulent correlations for smooth plates, but accounts for a
            non-negligible laminar run at the beginning of the plate. Citing Cengel and Cimbala, "Fluid Mechanics:
            Fundamentals and Applications", Table 10-4. Returns: Mean skin friction coefficient over a flat plate.

                Valid approximately for 5e5 <= Re_L <= 1e7.

            * "hybrid-schlichting": Schlichting's model, that roughly accounts for a non-negligtible laminar run.
            Citing "Boundary Layer Theory" 7th Ed., pg. 644

            * "hybrid-sharpe-convex": A hybrid model that blends the Blasius and Schlichting models. Convex in
            log-log space; however, it may overlook some truly nonconvex behavior near transitional Reynolds numbers.

            * "hybrid-sharpe-nonconvex": A hybrid model that blends the Blasius and Cengel models. Nonconvex in
            log-log-space; however, it may capture some truly nonconvex behavior near transitional Reynolds numbers.

    You can view all of these functions graphically using
    `aerosandbox.library.aerodynamics.test_aerodynamics.test_Cf_flat_plate.py`

    """
    Re_L = np.abs(Re_L)

    if method == "blasius":
        return 1.328 / Re_L**0.5
    elif method == "turbulent":
        return 0.074 / Re_L**(1 / 5)
    elif method == "hybrid-cengel":
        return 0.074 / Re_L**(1 / 5) - 1742 / Re_L
    elif method == "hybrid-schlichting":
        return 0.02666 * Re_L**-0.139
    elif method == "hybrid-sharpe-convex":
        return np.softmax(Cf_flat_plate(Re_L, method="blasius"),
                          Cf_flat_plate(Re_L, method="hybrid-schlichting"),
                          hardness=1e3)
    elif method == "hybrid-sharpe-nonconvex":
        return np.softmax(Cf_flat_plate(Re_L, method="blasius"),
                          Cf_flat_plate(Re_L, method="hybrid-cengel"),
                          hardness=1e3)
Ejemplo n.º 3
0
def mass_hpa_propeller(
        diameter,
        max_power,
        include_variable_pitch_mechanism=False
):
    """
    Returns the estimated mass of a propeller assembly for low-disc-loading applications (human powered airplane, paramotor, etc.)

    :param diameter: diameter of the propeller [m]
    :param max_power: maximum power of the propeller [W]
    :param include_variable_pitch_mechanism: boolean, does this propeller have a variable pitch mechanism?
    :return: estimated weight [kg]
    """

    mass_propeller = (
            0.495 *
            (diameter / 1.25) ** 1.6 *
            np.softmax(0.6, max_power / 14914, hardness=5) ** 2
    )  # Baselining to a 125cm E-Props Top 80 Propeller for paramotor, with some sketchy scaling assumptions
    # Parameters on diameter exponent and min power were chosen such that Daedalus propeller is roughly on the curve.

    mass_variable_pitch_mech = 216.8 / 800 * mass_propeller
    # correlation to Daedalus data: http://journals.sfu.ca/ts/index.php/ts/article/viewFile/760/718
    if include_variable_pitch_mechanism:
        mass_propeller += mass_variable_pitch_mech

    return mass_propeller
Ejemplo n.º 4
0
        def separation_parameter(alpha, Re=0):
            """
            Positive if separated, negative if attached.

            This will be an input to a tanh() sigmoid blend via asb.numpy.blend(), so a value of 1 means the flow is
            ~90% separated, and a value of -1 means the flow is ~90% attached.
            """
            return 0.5 * np.softmax(alpha - alpha_stall_positive,
                                    alpha_stall_negative - alpha)
def underlying_function(x):  # Softmax of three linear functions
    def f1(x):
        return -3 * x - 4

    def f2(x):
        return -0.25 * x + 1

    def f3(x):
        return 2 * x - 12

    return np.softmax(f1(x), f2(x), f3(x), hardness=1)
Ejemplo n.º 6
0
def induced_drag_ratio_from_ground_effect(h_over_b  # type: float
                                          ):
    """
    Gives the ratio of actual induced drag to free-flight induced drag experienced by a wing in ground effect.
    Artificially smoothed below around h/b == 0.05 to retain differentiability and practicality.
    Source: W. F. Phillips, D. F. Hunsaker, "Lifting-Line Predictions for Induced Drag and Lift in Ground Effect".
        Using Equation 5 from the paper, which is modified from a model from Torenbeek:
            Torenbeek, E. "Ground Effects", 1982.
    :param h_over_b: (Height above ground) divided by (wingspan).
    :return: Ratio of induced drag in ground effect to induced drag out of ground effect [unitless]
    """
    h_over_b = np.softmax(h_over_b, 0, hardness=1 / 0.03)
    return 1 - np.exp(-4.01 * (2 * h_over_b)**0.717)
Ejemplo n.º 7
0
def Cd_wave_Korn(Cl, t_over_c, mach, sweep=0, kappa_A=0.95):
    """
    Wave drag_force coefficient prediction using the low-fidelity Korn Equation method;
    derived in "Configuration Aerodynamics" by W.H. Mason, Sect. 7.5.2, pg. 7-18

    :param Cl: Sectional lift coefficient
    :param t_over_c: thickness-to-chord ratio
    :param sweep: sweep angle, in degrees
    :param kappa_A: Airfoil technology factor (0.95 for supercritical section, 0.87 for NACA 6-series)
    :return: Wave drag coefficient
    """
    smooth_abs_Cl = np.softmax(Cl, -Cl, hardness=10)

    mach = np.fmax(mach, 0)
    Mdd = kappa_A / np.cosd(sweep) - t_over_c / np.cosd(
        sweep)**2 - smooth_abs_Cl / (10 * np.cosd(sweep)**3)
    Mcrit = Mdd - (0.1 / 80)**(1 / 3)
    Cd_wave = np.where(mach > Mcrit, 20 * (mach - Mcrit)**4, 0)

    return Cd_wave
Ejemplo n.º 8
0
def mach_crit_Korn(
        CL,
        t_over_c,
        sweep=0,
        kappa_A=0.95
):
    """
        Wave drag_force coefficient prediction using the low-fidelity Korn Equation method;
    derived in "Configuration Aerodynamics" by W.H. Mason, Sect. 7.5.2, pg. 7-18

    Args:
        CL: Sectional lift coefficient
        t_over_c: thickness-to-chord ratio
        sweep: sweep angle, in degrees
        kappa_A: Airfoil technology factor (0.95 for supercritical section, 0.87 for NACA 6-series)

    Returns:

    """
    smooth_abs_CL = np.softmax(CL, -CL, hardness=10)

    M_dd = kappa_A / np.cosd(sweep) - t_over_c / np.cosd(sweep) ** 2 - smooth_abs_CL / (10 * np.cosd(sweep) ** 3)
    M_crit = M_dd - (0.1 / 80) ** (1 / 3)
    return M_crit
Ejemplo n.º 9
0
def approximate_CD_wave(
    mach,
    mach_crit,
    CD_wave_at_fully_supersonic,
):
    """
    An approximate relation for computing transonic wave drag, based on an object's Mach number.

    Considered reasonably valid from Mach 0 up to around Mach 2 or 3-ish.

    Methodology is a combination of:

        * The methodology described in Raymer, "Aircraft Design: A Conceptual Approach", Section 12.5.10 Transonic Parasite Drag (pg. 449 in Ed. 2)

        and

        * The methodology described in W.H. Mason's Configuration Aerodynamics, Chapter 7. Transonic Aerodynamics of Airfoils and Wings.

    Args:

        mach: Mach number at the operating point to be evaluated

        mach_crit: Critical mach number, a function of the body geometry

        CD_wave_at_fully_supersonic: The wave drag coefficient of the body at the speed that it first goes (
        effectively) fully supersonic.

            Here, that is taken to mean at the Mach 1.2 case.

            This value should probably be derived using something similar to a Sears-Haack relation for the body in
            question, with a markup depending on geometry smoothness.

            The CD_wave predicted by this function will match this value exactly at M=1.2 and M=1.05.

            The peak CD_wave that is predicted is ~1.23 * this value, which occurs at M=1.10.

            In the high-Mach limit, this function asymptotes at 0.80 * this value, as empirically stated by Raymer.
            However, this model is only approximate and is likely not valid for high-supersonic flows.

    Returns: The approximate wave drag coefficient at the specified Mach number.

        The reference area is whatever the reference area used in the `CD_wave_at_fully_supersonic` parameter is.

    """
    mach_crit_max = 1 - (0.1 / 80)**(1 / 3)

    mach_crit = -np.softmax(-mach_crit, -mach_crit_max, hardness=50)

    ### The following approximate relation is derived in W.H. Mason, "Configuration Aerodynamics", Chapter 7. Transonic Aerodynamics of Airfoils and Wings.
    ### Equation 7-8 on Page 7-19.
    ### This is in turn based on Lock's proposed empirically-derived shape of the drag rise, from Hilton, W.F., High Speed Aerodynamics, Longmans, Green & Co., London, 1952, pp. 47-49
    mach_dd = mach_crit + (0.1 / 80)**(1 / 3)

    ### Model drag sections and cutoffs:
    return CD_wave_at_fully_supersonic * np.where(
        mach < mach_crit,
        0,
        np.where(
            mach < mach_dd,
            20 * (mach - mach_crit)**4,
            np.where(
                mach < 1.05,
                cubic_hermite_patch(mach,
                                    x_a=mach_dd,
                                    x_b=1.05,
                                    f_a=20 * (0.1 / 80)**(4 / 3),
                                    f_b=1,
                                    dfdx_a=0.1,
                                    dfdx_b=10),
                np.where(mach < 1.2,
                         cubic_hermite_patch(mach,
                                             x_a=1.05,
                                             x_b=1.2,
                                             f_a=1,
                                             f_b=1,
                                             dfdx_a=10,
                                             dfdx_b=-4),
                         np.blend(
                             switch=4 * 2 * (mach - 1.2) / (1.2 - 0.8),
                             value_switch_high=0.8,
                             value_switch_low=1.2,
                         )
                         # 0.8 + 0.2 * np.exp(20 * (1.2 - mach))
                         ))))
Ejemplo n.º 10
0
def CL_over_Cl(
        aspect_ratio: float,
        mach: float = 0.,
        sweep: float = 0.,
        Cl_is_compressible: bool = True
) -> float:
    """
    Returns the ratio of 3D lift coefficient (with compressibility) to the 2D lift coefficient.

    Specifically: CL_3D / CL_2D

    Args:

        aspect_ratio: The aspect ratio of the wing.

        mach: The freestream Mach number.

        sweep: The sweep of the wing, in degrees. To be most accurate, this should be the sweep at the locus of
        thickest points along the wing.

        Cl_is_compressible: This flag indicates whether the 2D airfoil data already has compressibility effects
        modeled.

            For example:

                * If this flag is True, this function returns: CL_3D / CL_2D, where CL_2D is the sectional lift
                coefficient based on the local profile at the freestream mach number.

                * If this flag is False, this function returns: CL_3D / CL_2D_at_mach_zero, where CL_2D_... is the
                sectional lift coefficient based on the local profile at mach zero.

            For most accurate results, set this flag to True, and then model profile characteristics separately.

    """
    prandtl_glauert_beta_squared_ideal = 1 - mach ** 2

    # beta_squared = 1 - mach ** 2
    beta_squared = np.softmax(
        prandtl_glauert_beta_squared_ideal,
        -prandtl_glauert_beta_squared_ideal,
        hardness=3.0
    )

    ### Alternate formulations
    # CL_ratio = aspect_ratio / (aspect_ratio + 2) # Equivalent to equation in Drela's FVA in incompressible, 2*pi*alpha limit.
    # CL_ratio = aspect_ratio / (2 + np.sqrt(4 + aspect_ratio ** 2))  # more theoretically sound at low aspect_ratio

    ### Formulation from Raymer, Sect. 12.4.1; citing DATCOM.
    # Comparison to experiment suggests this is the most accurate.
    # Symbolically simplified to remove the PG singularity.
    eta = 0.95
    CL_ratio = aspect_ratio / (
            2 + (
            4 + (aspect_ratio ** 2 * beta_squared / eta ** 2) + (np.tand(sweep) * aspect_ratio / eta) ** 2
    ) ** 0.5
    )

    if Cl_is_compressible:
        CL_ratio = CL_ratio * beta_squared ** 0.5

    return CL_ratio