def draw( self, scalar_to_plot: str = "potential", # "potential", "streamfunction", "xvel", "yvel", "velmag", "Cp" x_points: np.ndarray = np.linspace(-10, 10, 400), y_points: np.ndarray = np.linspace(-10, 10, 300), percentiles_to_include=99.7, show=True, ): X, Y = np.meshgrid(x_points, y_points) X_r = np.reshape(X, -1) Y_r = np.reshape(Y, -1) points = np.vstack((X_r, Y_r)).T if scalar_to_plot == "potential": scalar_to_plot_value = sum( [object.get_potential_at(points) for object in self.objects]) elif scalar_to_plot == "streamfunction": scalar_to_plot_value = sum([ object.get_streamfunction_at(points) for object in self.objects ]) elif scalar_to_plot == "xvel": scalar_to_plot_value = sum( [object.get_x_velocity_at(points) for object in self.objects]) elif scalar_to_plot == "yvel": scalar_to_plot_value = sum( [object.get_y_velocity_at(points) for object in self.objects]) elif scalar_to_plot == "velmag": x_vels = sum( [object.get_x_velocity_at(points) for object in self.objects]) y_vels = sum( [object.get_y_velocity_at(points) for object in self.objects]) scalar_to_plot_value = np.sqrt(x_vels**2 + y_vels**2) elif scalar_to_plot == "Cp": x_vels = sum( [object.get_x_velocity_at(points) for object in self.objects]) y_vels = sum( [object.get_y_velocity_at(points) for object in self.objects]) V = np.sqrt(x_vels**2 + y_vels**2) scalar_to_plot_value = 1 - V**2 else: raise ValueError("Bad value of `scalar_to_plot`!") min = np.nanpercentile(scalar_to_plot_value, 50 - percentiles_to_include / 2) max = np.nanpercentile(scalar_to_plot_value, 50 + percentiles_to_include / 2) contour(x_points, y_points, scalar_to_plot_value.reshape(X.shape), levels=np.linspace(min, max, 80), linelabels=False, cmap=plt.get_cmap("rainbow"), contour_kwargs={ "linestyles": 'solid', "alpha": 0.4 }) plt.gca().set_aspect("equal", adjustable='box') show_plot(f"Potential Flow: {scalar_to_plot}", "$x$", "$y$", show=show)
def stack_coordinates(x: np.ndarray, y: np.ndarray) -> np.ndarray: """ Stacks a pair of x, y coordinate arrays into a Nx2 ndarray. Args: x: A 1D ndarray of x-coordinates y: A 1D ndarray of y-coordinates Returns: A Nx2 ndarray of [x, y] coordinates. """ return np.vstack((x, y)).T
def get_kulfan_coordinates( lower_weights=-0.2 * np.ones(5), # type: np.ndarray upper_weights=0.2 * np.ones(5), # type: np.ndarray enforce_continuous_LE_radius=True, TE_thickness=0., # type: float n_points_per_side=_default_n_points_per_side, # type: int N1=0.5, # type: float N2=1.0, # type: float ) -> np.ndarray: """ Calculates the coordinates of a Kulfan (CST) airfoil. To make a Kulfan (CST) airfoil, use the following syntax: asb.Airfoil("My Airfoil Name", coordinates = asb.kulfan_coordinates(*args)) More on Kulfan (CST) airfoils: http://brendakulfan.com/docs/CST2.pdf Notes on N1, N2 (shape factor) combinations: * 0.5, 1: Conventional airfoil * 0.5, 0.5: Elliptic airfoil * 1, 1: Biconvex airfoil * 0.75, 0.75: Sears-Haack body (radius distribution) * 0.75, 0.25: Low-drag projectile * 1, 0.001: Cone or wedge airfoil * 0.001, 0.001: Rectangle, circular duct, or circular rod. :param lower_weights: :param upper_weights: :param enforce_continuous_LE_radius: Enforces a continous leading-edge radius by throwing out the first lower weight. :param TE_thickness: :param n_points_per_side: :param N1: LE shape factor :param N2: TE shape factor :return: """ if enforce_continuous_LE_radius: lower_weights[0] = -1 * upper_weights[0] x_lower = np.cosspace(0, 1, n_points_per_side) x_upper = x_lower[::-1] x_lower = x_lower[ 1:] # Trim off the nose coordinate so there are no duplicates def shape(w, x): # Class function C = x**N1 * (1 - x)**N2 # Shape function (Bernstein polynomials) n = len(w) - 1 # Order of Bernstein polynomials K = comb(n, np.arange(n + 1)) # Bernstein polynomial coefficients S_matrix = (w * K * np.expand_dims(x, 1)**np.arange(n + 1) * np.expand_dims(1 - x, 1)**(n - np.arange(n + 1)) ) # Polynomial coefficient * weight matrix # S = np.sum(S_matrix, axis=1) S = np.array( [np.sum(S_matrix[i, :]) for i in range(S_matrix.shape[0])]) # Calculate y output y = C * S return y y_lower = shape(lower_weights, x_lower) y_upper = shape(upper_weights, x_upper) # TE thickness y_lower -= x_lower * TE_thickness / 2 y_upper += x_upper * TE_thickness / 2 x = np.concatenate([x_upper, x_lower]) y = np.concatenate([y_upper, y_lower]) coordinates = np.vstack((x, y)).T return coordinates