Esempio n. 1
0
    def translate(
        self,
        translate_x: float = 0.,
        translate_y: float = 0.,
    ) -> 'Airfoil':
        """
        Translates an Airfoil by a given amount.
        Args:
            translate_x: Amount to translate in the x-direction
            translate_y: Amount to translate in the y-direction

        Returns: The translated Airfoil.

        """
        x = self.x() + translate_x
        y = self.y() + translate_y

        return Airfoil(name=self.name, coordinates=stack_coordinates(x, y))
Esempio n. 2
0
    def scale(
        self,
        scale_x: float = 1.,
        scale_y: float = 1.,
    ) -> 'Airfoil':
        """
        Scales an Airfoil about the origin.
        Args:
            scale_x: Amount to scale in the x-direction.
            scale_y: Amount to scale in the y-direction.

        Returns: The scaled Airfoil.
        """
        x = self.x() * scale_x
        y = self.y() * scale_y

        if scale_y < 0:
            x = x[::-1]
            y = y[::-1]

        return Airfoil(name=self.name, coordinates=stack_coordinates(x, y))
Esempio n. 3
0
    def repanel(
        self,
        n_points_per_side: int = 100,
    ) -> 'Airfoil':
        """
        Returns a repaneled version of the airfoil with cosine-spaced coordinates on the upper and lower surfaces.
        :param n_points_per_side: Number of points per side (upper and lower) of the airfoil [int]
            Notes: The number of points defining the final airfoil will be n_points_per_side*2-1,
            since one point (the leading edge point) is shared by both the upper and lower surfaces.
        :return: Returns the new airfoil.
        """

        upper_original_coors = self.upper_coordinates(
        )  # Note: includes leading edge point, be careful about duplicates
        lower_original_coors = self.lower_coordinates(
        )  # Note: includes leading edge point, be careful about duplicates

        # Find distances between coordinates, assuming linear interpolation
        upper_distances_between_points = (
            (upper_original_coors[:-1, 0] - upper_original_coors[1:, 0])**2 +
            (upper_original_coors[:-1, 1] - upper_original_coors[1:, 1])**
            2)**0.5
        lower_distances_between_points = (
            (lower_original_coors[:-1, 0] - lower_original_coors[1:, 0])**2 +
            (lower_original_coors[:-1, 1] - lower_original_coors[1:, 1])**
            2)**0.5
        upper_distances_from_TE = np.hstack(
            (0, np.cumsum(upper_distances_between_points)))
        lower_distances_from_LE = np.hstack(
            (0, np.cumsum(lower_distances_between_points)))
        upper_distances_from_TE_normalized = upper_distances_from_TE / upper_distances_from_TE[
            -1]
        lower_distances_from_LE_normalized = lower_distances_from_LE / lower_distances_from_LE[
            -1]

        distances_from_TE_normalized = np.hstack(
            (upper_distances_from_TE_normalized,
             1 + lower_distances_from_LE_normalized[1:]))

        # Generate a cosine-spaced list of points from 0 to 1
        cosspaced_points = np.cosspace(0, 1, n_points_per_side)
        s = np.hstack((
            cosspaced_points,
            1 + cosspaced_points[1:],
        ))

        # Check that there are no duplicate points in the airfoil.
        if np.any(np.diff(distances_from_TE_normalized) == 0):
            raise ValueError(
                "This airfoil has a duplicated point (i.e. two adjacent points with the same (x, y) coordinates), so you can't repanel it!"
            )

        x = interp1d(
            distances_from_TE_normalized,
            self.x(),
            kind="cubic",
        )(s)
        y = interp1d(
            distances_from_TE_normalized,
            self.y(),
            kind="cubic",
        )(s)

        return Airfoil(name=self.name, coordinates=stack_coordinates(x, y))
Esempio n. 4
0
def get_NACA_coordinates(
        name: str = 'naca2412',
        n_points_per_side: int = _default_n_points_per_side) -> np.ndarray:
    """
    Returns the coordinates of a specified 4-digit NACA airfoil.
    Args:
        name: Name of the NACA airfoil.
        n_points_per_side: Number of points per side of the airfoil (top/bottom).

    Returns: The coordinates of the airfoil as a Nx2 ndarray [x, y]

    """
    name = name.lower().strip()

    if not "naca" in name:
        raise ValueError("Not a NACA airfoil!")

    nacanumber = name.split("naca")[1]
    if not nacanumber.isdigit():
        raise ValueError("Couldn't parse the number of the NACA airfoil!")

    if not len(nacanumber) == 4:
        raise NotImplementedError(
            "Only 4-digit NACA airfoils are currently supported!")

    # Parse
    max_camber = int(nacanumber[0]) * 0.01
    camber_loc = int(nacanumber[1]) * 0.1
    thickness = int(nacanumber[2:]) * 0.01

    # Referencing https://en.wikipedia.org/wiki/NACA_airfoil#Equation_for_a_cambered_4-digit_NACA_airfoil
    # from here on out

    # Make uncambered coordinates
    x_t = np.cosspace(0, 1,
                      n_points_per_side)  # Generate some cosine-spaced points
    y_t = 5 * thickness * (
        +0.2969 * x_t**0.5 - 0.1260 * x_t - 0.3516 * x_t**2 + 0.2843 * x_t**3 -
        0.1015 * x_t**4  # 0.1015 is original, #0.1036 for sharp TE
    )

    if camber_loc == 0:
        camber_loc = 0.5  # prevents divide by zero errors for things like naca0012's.

    # Get camber
    y_c = np.where(
        x_t <= camber_loc,
        max_camber / camber_loc**2 * (2 * camber_loc * x_t - x_t**2),
        max_camber / (1 - camber_loc)**2 *
        ((1 - 2 * camber_loc) + 2 * camber_loc * x_t - x_t**2))

    # Get camber slope
    dycdx = np.where(x_t <= camber_loc,
                     2 * max_camber / camber_loc**2 * (camber_loc - x_t),
                     2 * max_camber / (1 - camber_loc)**2 * (camber_loc - x_t))
    theta = np.arctan(dycdx)

    # Combine everything
    x_U = x_t - y_t * np.sin(theta)
    x_L = x_t + y_t * np.sin(theta)
    y_U = y_c + y_t * np.cos(theta)
    y_L = y_c - y_t * np.cos(theta)

    # Flip upper surface so it's back to front
    x_U, y_U = x_U[::-1], y_U[::-1]

    # Trim 1 point from lower surface so there's no overlap
    x_L, y_L = x_L[1:], y_L[1:]

    x = np.hstack((x_U, x_L))
    y = np.hstack((y_U, y_L))

    return stack_coordinates(x, y)