def contains_points( self, x: Union[float, np.ndarray], y: Union[float, np.ndarray], ) -> np.ndarray: """ Returns a boolean array of whether or not some (x, y) point(s) are contained within the Polygon. Args: x: x-coordinate(s) of the query points. y: y-coordinate(s) of the query points. Returns: A boolean array of the same size as x and y. """ x = np.array(x) y = np.array(y) try: input_shape = (x + y).shape except ValueError as e: # If arrays are not broadcastable raise ValueError( "Inputs x and y could not be broadcast together!") from e x = x.reshape(-1, 1) y = y.reshape(-1, 1) points = np.hstack((x, y)) contained = path.Path( vertices=self.coordinates).contains_points(points) contained = np.array(contained).reshape(input_shape) return contained
def contains_points( self, x, y, ): x = np.array(x) y = np.array(y) try: input_shape = (x + y).shape except ValueError as e: # If arrays are not broadcastable raise ValueError( "Inputs x and y could not be broadcast together!") from e x = x.reshape(-1, 1) y = y.reshape(-1, 1) points = np.hstack((x, y)) contained = path.Path( vertices=self.coordinates).contains_points(points) contained = np.array(contained).reshape(input_shape) return contained
import aerosandbox.numpy as np np.random.seed(0) # Fix a seed for reproducibility ### Create some data (some fictional system where temperature is a function of time, and we're measuring it) n = 20 time = 100 * np.random.rand(n) actual_temperature = 2 * time + 20 # True physics of the system noise = 10 * np.random.randn(n) measured_temperature = actual_temperature + noise # Measured temperature of the system ### Add in a dropout measurement (say, the sensor wire randomly came loose and gave us a 0 reading) time = np.hstack((time, 90)) measured_temperature = np.hstack((measured_temperature, 0)) if __name__ == '__main__': from aerosandbox.tools.pretty_plots import plt, sns, mpl, show_plot fig, ax = plt.subplots() plt.plot(time, measured_temperature, ".") show_plot(xlabel="Time", ylabel="Measured Temperature")
# (3, 1, 1) # ), # winds_95_world # ), # axis=0 # ) # Downsample latitudes_world = latitudes_world[::5] winds_95_world = winds_95_world[:, ::5, :] # Extend boundaries so that cubic spline interpolates around day_of_year appropriately. extend_bounds = 3 day_of_year_world = np.hstack(( day_of_year_world[-extend_bounds:] - 365, day_of_year_world, day_of_year_world[:extend_bounds] + 365 )) winds_95_world = np.dstack(( winds_95_world[:, :, -extend_bounds:], winds_95_world, winds_95_world[:, :, :extend_bounds] )) # Make the model winds_95_world_model = InterpolatedModel( x_data_coordinates={ "altitude" : altitudes_world, "latitude" : latitudes_world, "day of year": day_of_year_world, },
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))
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)
lats_v = data["lats"].flatten() alts_v = data["alts"].flatten() speeds = data["speeds"].reshape(len(alts_v), len(lats_v)).T.flatten() lats, alts = np.meshgrid(lats_v, alts_v, indexing="ij") lats = lats.flatten() alts = alts.flatten() # %% lats_scaled = (lats - 37.5) / 11.5 alts_scaled = (alts - 24200) / 24200 speeds_scaled = (speeds - 7) / 56 alt_diff = np.diff(alts_v) alt_diff_aug = np.hstack((alt_diff[0], alt_diff, alt_diff[-1])) weights_1d = (alt_diff_aug[:-1] + alt_diff_aug[1:]) / 2 weights_1d = weights_1d / np.mean(weights_1d) # region_of_interest = np.logical_and( # alts_v > 10000, # alts_v < 40000 # ) # true_weights = np.where( # region_of_interest, # 2, # 1 # ) weights = np.tile(weights_1d, (93, 1)).flatten() # %%