def test_basic_math(types):
    for x in types["all"]:
        for y in types["all"]:
            ### Arithmetic
            x + y
            x - y
            x * y
            x / y
            np.sum(x)  # Sum of all entries of array-like object x

            ### Exponentials & Powers
            x**y
            np.power(x, y)
            np.exp(x)
            np.log(x)
            np.log10(x)
            np.sqrt(x)  # Note: do x ** 0.5 rather than np.sqrt(x).

            ### Trig
            np.sin(x)
            np.cos(x)
            np.tan(x)
            np.arcsin(x)
            np.arccos(x)
            np.arctan(x)
            np.arctan2(y, x)
            np.sinh(x)
            np.cosh(x)
            np.tanh(x)
            np.arcsinh(x)
            np.arccosh(x)
            np.arctanh(x - 0.5)  # `- 0.5` to give valid argument
Exemple #2
0
    def TE_angle(self) -> float:
        """
        Returns the trailing edge angle of the airfoil, in degrees
        """
        upper_TE_vec = self.coordinates[0, :] - self.coordinates[1, :]
        lower_TE_vec = self.coordinates[-1, :] - self.coordinates[-2, :]

        return 180 / np.pi * (np.arctan2(
            upper_TE_vec[0] * lower_TE_vec[1] - upper_TE_vec[1] *
            lower_TE_vec[0], upper_TE_vec[0] * lower_TE_vec[0] +
            upper_TE_vec[1] * upper_TE_vec[1]))
import aerosandbox as asb
import aerosandbox.numpy as np
import pytest
from scipy import integrate

u_e_0 = 100
w_e_0 = -100
speed_0 = (u_e_0**2 + w_e_0**2)**0.5
gamma_0 = np.arctan2(-w_e_0, u_e_0)

time = np.linspace(0, 10, 501)


def get_trajectory(parameterization: type = asb.DynamicsPointMass2DCartesian,
                   gravity=True,
                   drag=True,
                   plot=False):
    if parameterization is asb.DynamicsPointMass2DCartesian:
        dyn = parameterization(
            mass_props=asb.MassProperties(mass=1),
            x_e=0,
            z_e=0,
            u_e=u_e_0,
            w_e=w_e_0,
        )
    elif parameterization is asb.DynamicsPointMass2DSpeedGamma:
        dyn = parameterization(
            mass_props=asb.MassProperties(mass=1),
            x_e=0,
            z_e=0,
            speed=speed_0,
def _calculate_induced_velocity_line_singularity_panel_coordinates(
    xp_field: Union[float, np.ndarray],
    yp_field: Union[float, np.ndarray],
    gamma_start: float = 0.,
    gamma_end: float = 0.,
    sigma_start: float = 0.,
    sigma_end: float = 0.,
    xp_panel_end: float = 1.,
) -> [Union[float, np.ndarray], Union[float, np.ndarray]]:
    """
    Calculates the induced velocity at a point (xp_field, yp_field) in a 2D potential-flow flowfield.

    The `p` suffix in `xp...` and `yp...` denotes the use of the panel coordinate system, where:
        * xp_hat is along the length of the panel
        * yp_hat is orthogonal (90 deg. counterclockwise) to it.

    In this flowfield, there is only one singularity element: A line vortex going from (0, 0) to (xp_panel_end, 0).
    The strength of this vortex varies linearly from:
        * gamma_start at (0, 0), to:
        * gamma_end at (xp_panel_end, 0). # TODO update paragraph

    By convention here, positive gamma induces clockwise swirl in the flow field.
        
    Function returns the 2D velocity u, v in the local coordinate system of the panel.

    Inputs x and y can be 1D ndarrays representing various field points,
    in which case the resulting velocities u and v have corresponding dimensionality.

    Equations from the seminal textbook "Low Speed Aerodynamics" by Katz and Plotkin.
    Vortex equations are Eq. 11.99 and Eq. 11.100.
        * Note: there is an error in equation 11.100 in Katz and Plotkin, at least in the 2nd ed:
        The last term of equation 11.100, which is given as:
            (x_{j+1} - x_j) / z + (theta_{j+1} - theta_j)
        has a sign error and should instead be written as:
            (x_{j+1} - x_j) / z - (theta_{j+1} - theta_j)
    Source equations are Eq. 11.89 and Eq. 11.90.

    """
    ### Modify any incoming floats
    if isinstance(xp_field, (float, int)):
        xp_field = np.array([xp_field])
    if isinstance(yp_field, (float, int)):
        yp_field = np.array([yp_field])

    ### Determine if you can skip either the vortex or source parts
    skip_vortex_math = not (isinstance(gamma_start, cas.MX) or isinstance(
        gamma_end, cas.MX)) and gamma_start == 0 and gamma_end == 0
    skip_source_math = not (isinstance(sigma_start, cas.MX) or isinstance(
        sigma_end, cas.MX)) and sigma_start == 0 and sigma_end == 0

    ### Determine which points are effectively on the panel, necessitating different math:
    is_on_panel = np.fabs(yp_field) <= 1e-8

    ### Do some geometry calculation
    r_1 = (xp_field**2 + yp_field**2)**0.5
    r_2 = ((xp_field - xp_panel_end)**2 + yp_field**2)**0.5

    ### Regularize
    is_on_endpoint = ((r_1 == 0) | (r_2 == 0))
    r_1 = np.where(
        r_1 == 0,
        1,
        r_1,
    )
    r_2 = np.where(r_2 == 0, 1, r_2)

    ### Continue geometry calculation
    theta_1 = np.arctan2(yp_field, xp_field)
    theta_2 = np.arctan2(yp_field, xp_field - xp_panel_end)
    ln_r_2_r_1 = np.log(r_2 / r_1)
    d_theta = theta_2 - theta_1
    tau = 2 * np.pi

    ### Regularize if the point is on the panel.
    yp_field_regularized = np.where(is_on_panel, 1, yp_field)

    ### VORTEX MATH
    if skip_vortex_math:
        u_vortex = 0
        v_vortex = 0
    else:
        d_gamma = gamma_end - gamma_start
        u_vortex_term_1_quantity = (yp_field / tau * d_gamma / xp_panel_end)
        u_vortex_term_2_quantity = (gamma_start * xp_panel_end +
                                    d_gamma * xp_field) / (tau * xp_panel_end)

        # Calculate u_vortex
        u_vortex_term_1 = u_vortex_term_1_quantity * ln_r_2_r_1
        u_vortex_term_2 = u_vortex_term_2_quantity * d_theta
        u_vortex = u_vortex_term_1 + u_vortex_term_2

        # Correct the u-velocity if field point is on the panel
        u_vortex = np.where(is_on_panel, 0, u_vortex)

        # Calculate v_vortex
        v_vortex_term_1 = u_vortex_term_2_quantity * ln_r_2_r_1

        v_vortex_term_2 = np.where(
            is_on_panel,
            d_gamma / tau,
            u_vortex_term_1_quantity *
            (xp_panel_end / yp_field_regularized - d_theta),
        )

        v_vortex = v_vortex_term_1 + v_vortex_term_2

    ### SOURCE MATH
    if skip_source_math:
        u_source = 0
        v_source = 0
    else:
        d_sigma = sigma_end - sigma_start
        v_source_term_1_quantity = (yp_field / tau * d_sigma / xp_panel_end)
        v_source_term_2_quantity = (sigma_start * xp_panel_end +
                                    d_sigma * xp_field) / (tau * xp_panel_end)
        # Calculate v_source
        v_source_term_1 = -v_source_term_1_quantity * ln_r_2_r_1
        v_source_term_2 = v_source_term_2_quantity * d_theta
        v_source = v_source_term_1 + v_source_term_2

        # Correct the v-velocity if field point is on the panel
        v_source = np.where(is_on_panel, 0, v_source)

        # Calculate u_source
        u_source_term_1 = -v_source_term_2_quantity * ln_r_2_r_1

        u_source_term_2 = np.where(
            is_on_panel,
            -d_sigma / tau,
            -v_source_term_1_quantity *
            (xp_panel_end / yp_field_regularized - d_theta),
        )

        u_source = u_source_term_1 + u_source_term_2

    ### Return
    u = u_vortex + u_source
    v = v_vortex + v_source

    ### If the field point is on the endpoint of the panel, replace the NaN with a zero.
    u = np.where(is_on_endpoint, 0, u)
    v = np.where(is_on_endpoint, 0, v)

    return u, v
    U = (U_a ** 2 + U_t ** 2) ** 0.5  # Velocity magnitude
    W_a = 0.5 * U_a + 0.5 * U * np.sin(Psi)  # Axial velocity w/ induced eff.
    W_t = 0.5 * U_t + 0.5 * U * np.cos(Psi)  # Tangential velocity w/ induced eff.
    v_a = W_a - U_a  # Axial induced velocity
    v_t = U_t - W_t  # Tangential induced velocity
    W = (W_a ** 2 + W_t ** 2) ** 0.5
    Re = air_density * W * chord_local / mu
    Ma = W / speed_of_sound
    v = (v_a ** 2 + v_t ** 2) ** 0.5
    loc_wake_adv_ratio = (radial_loc / tip_radius) * (W_a / W_t)
    f = (n_blades / 2) * (1 - radial_loc / tip_radius) * 1 / loc_wake_adv_ratio
    F = 2 / pi * np.arccos(np.exp(-f))


    ## Compute local blade quantities
    phi_rad = np.arctan2(W_a, W_t) #local flow angle
    phi_deg = phi_rad * 180 / pi
    alpha_rad = twist_local_rad - phi_rad
    alpha_deg = alpha_rad * 180 / pi

    ### Compute sectional lift and drag
    cl = airfoil_CL(alpha_deg, Re, Ma)
    cd = airfoil_CDp(alpha_deg, Re, Ma, cl)
    gamma = 0.5 * W * chord_local * cl

    ### Add governing equations
    opti.subject_to([
        # 0.5 * v == 0.5 * U * cas.sin(Psi / 4),
        # v_a == v_t * W_t / W_a,
        # U ** 2 == v ** 2 + W ** 2,
        # gamma == -0.0145,