def types(): ### NumPy data types scalar_np = np.array(1) vector_np = np.array([1, 1]) matrix_np = np.ones((2, 2)) ### CasADi data types opti = Opti() scalar_cas = opti.variable(init_guess=1) vector_cas = opti.variable(n_vars=2, init_guess=1) ### Dynamically-typed data type creation (i.e. type depends on inputs) vector_dynamic = np.array([scalar_cas, scalar_cas]) # vector as a dynamic-typed array matrix_dynamic = np.array([ # matrix as an dynamic-typed array [scalar_cas, scalar_cas], [scalar_cas, scalar_cas] ]) ### Create lists of possible variable types for scalars, vectors, and matrices. scalar_options = [scalar_cas, scalar_np] vector_options = [vector_cas, vector_np, vector_dynamic] matrix_options = [matrix_np, matrix_dynamic] return { "scalar": scalar_options, "vector": vector_options, "matrix": matrix_options, "all": scalar_options + vector_options + matrix_options }
def test_where_numpy(): a = np.ones(4) b = 2 * np.ones(4) c = np.where(np.array([True, False, True, False]), a, b) assert np.all(c == np.array([1, 2, 1, 2]))
def test_array_numpy_equivalency_2D(): inputs = [[1, 2], [3, 4]] a = array(inputs) a_np = np.array(inputs) assert np.all(a == a_np)
def draw_streamlines(self, res=200, show=True): fig, ax = plt.subplots(1, 1, figsize=(6.4, 4.8), dpi=200) plt.xlim(-0.5, 1.5) plt.ylim(-0.5, 0.5) xrng = np.diff(np.array(ax.get_xlim())) yrng = np.diff(np.array(ax.get_ylim())) x = np.linspace(*ax.get_xlim(), int(np.round(res * xrng / yrng))) y = np.linspace(*ax.get_ylim(), res) X, Y = np.meshgrid(x, y) shape = X.shape X = X.flatten() Y = Y.flatten() U, V = self.calculate_velocity(X, Y) X = X.reshape(shape) Y = Y.reshape(shape) U = U.reshape(shape) V = V.reshape(shape) # NaN out any points inside the airfoil for airfoil in self.airfoils: contains = airfoil.contains_points(X, Y) U[contains] = np.NaN V[contains] = np.NaN speed = (U**2 + V**2)**0.5 Cp = 1 - speed**2 ### Draw the airfoils for airfoil in self.airfoils: plt.fill(airfoil.x(), airfoil.y(), "k", linewidth=0, zorder=4) plt.streamplot( x, y, U, V, color=speed, density=2.5, arrowsize=0, cmap=plt.get_cmap('coolwarm_r'), ) CB = plt.colorbar( orientation="horizontal", shrink=0.8, aspect=40, ) CB.set_label(r"Relative Airspeed ($U/U_\infty$)") plt.clim(0.6, 1.4) plt.gca().set_aspect('equal', adjustable='box') plt.xlabel(r"$x/c$") plt.ylabel(r"$y/c$") plt.title(rf"Inviscid Airfoil: Flow Field") plt.tight_layout() if show: plt.show()
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 test_basic_logicals_numpy(): a = np.array([True, True, False, False]) b = np.array([True, False, True, False]) assert np.all( a & b == np.array([True, False, False, False]) )
def __init__( self, xyz_le: np.ndarray = np.array([0, 0, 0]), chord: float = 1., twist_angle: float = 0, twist_axis: np.ndarray = np.array([0, 1, 0]), airfoil: Airfoil = Airfoil("naca0012"), control_surface_is_symmetric: bool = True, control_surface_hinge_point: float = 0.75, control_surface_deflection: float = 0., ): """ Initialize a new wing cross section. Args: xyz_le: xyz-coordinates of the leading edge of the cross section, relative to the wing's datum. chord: Chord of the wing at this cross section twist_angle: Twist angle, in degrees, as defined about the leading edge. twist_axis: The twist axis vector, used if twist_angle != 0. airfoil: Airfoil associated with this cross section. [aerosandbox.Airfoil] control_surface_is_symmetric: Is the control surface symmetric? (e.g. True for flaps, False for ailerons.) control_surface_hinge_point: The location of the control surface hinge, as a fraction of chord. control_surface_deflection: Control deflection, in degrees. Downwards-positive. """ self.xyz_le = xyz_le self.chord = chord self.twist = twist_angle self.twist_axis = twist_axis self.airfoil = airfoil self.control_surface_is_symmetric = control_surface_is_symmetric self.control_surface_hinge_point = control_surface_hinge_point self.control_surface_deflection = control_surface_deflection
def test_numpy_equivalency_1D(): inputs = [1, 2] a = array(inputs) a_np = np.array(inputs) assert np.all(a == a_np)
def test_general_3D_shorthands(): rotx = np.rotation_matrix_3D(1, np.array([1, 0, 0])) assert pytest.approx(rotx) == np.rotation_matrix_3D(1, "x") roty = np.rotation_matrix_3D(1, np.array([0, 1, 0])) assert pytest.approx(roty) == np.rotation_matrix_3D(1, "y") rotz = np.rotation_matrix_3D(1, np.array([0, 0, 1])) assert pytest.approx(rotz) == np.rotation_matrix_3D(1, "z")
def chain_conversion(axes: List[str] = ["geometry", "body", "geometry"]): x, y, z = copy.deepcopy(vector) for from_axes, to_axes in zip(axes, axes[1:]): x, y, z = op_point.convert_axes(x_from=x, y_from=y, z_from=z, from_axes=from_axes, to_axes=to_axes) return np.array(vector) == pytest.approx(np.array([x, y, z]))
def test_interpolated_model_at_vector(): model = interpolated_model() x_data = { "x1": np.array([1.5, 2.5]), "x2": np.array([2.5, 3.5]), } assert np.all( model(x_data) == pytest.approx(underlying_function_2D( *x_data.values())))
def test_uniform_forward_difference_first_degree(): assert np.finite_difference_coefficients( x=np.arange(2), x0=0, derivative_degree=1) == pytest.approx(np.array([-1, 1])) assert np.finite_difference_coefficients( x=np.arange(9), x0=0, derivative_degree=1) == pytest.approx( np.array([ -761 / 280, 8, -14, 56 / 3, -35 / 2, 56 / 5, -14 / 3, 8 / 7, -1 / 8 ]))
def test_uniform_central_difference(): assert np.finite_difference_coefficients( x=[-1, 0, 1], x0=0, derivative_degree=1) == pytest.approx(np.array([-0.5, 0, 0.5])) assert np.finite_difference_coefficients( x=[-1, 0, 1], x0=0, derivative_degree=2) == pytest.approx(np.array([1, -2, 1])) assert np.finite_difference_coefficients( x=[-2, -1, 0, 1, 2], x0=0, derivative_degree=2) == pytest.approx( np.array([-1 / 12, 4 / 3, -5 / 2, 4 / 3, -1 / 12]))
def test_length(): assert length(5) == 1 assert length(5.) == 1 assert length([1, 2, 3]) == 3 assert length(np.array(5)) == 1 assert length(np.array([5])) == 1 assert length(np.array([1, 2, 3])) == 3 assert length(np.ones((3, 2))) == 3 assert length(cas.GenMX_ones(5)) == 5
def main(): time = np.linspace(0, 10, 100) # Time in seconds wing_velocity = 2 # Wing horizontal velocity in m/s chord = 2 reduced_time = calculate_reduced_time( time, wing_velocity, chord) # Number of semi chords travelled # Visualize the gust profiles as well as the pitch maneuvers fig, ax1 = plt.subplots(dpi=300) ln1 = ax1.plot(reduced_time, np.array([top_hat_gust(s) for s in reduced_time]), label="Top-Hat Gust", lw=3) ln2 = ax1.plot(reduced_time, np.array([sine_squared_gust(s) for s in reduced_time]), label="Sine-Squared Gust", lw=3) ax1.set_xlabel("Reduced time") ax1.set_ylabel("Velocity (m/s)") ax2 = ax1.twinx() ln3 = ax2.plot(reduced_time, np.array([gaussian_pitch(s) for s in reduced_time]), label="Guassian Pitch", c="red", ls="--", lw=3) ax2.set_ylabel("Angle of Attack, degrees") lns = ln1 + ln2 + ln3 labs = [l.get_label() for l in lns] ax2.legend(lns, labs, loc="lower right") plt.title("Gust and pitch example profiles") total_lift = pitching_through_transverse_gust(reduced_time, top_hat_gust, wing_velocity, gaussian_pitch) gust_lift = calculate_lift_due_to_transverse_gust(reduced_time, top_hat_gust, wing_velocity, gaussian_pitch) pitch_lift = calculate_lift_due_to_pitching_profile( reduced_time, gaussian_pitch) added_mass_lift = added_mass_due_to_pitching(reduced_time, gaussian_pitch) # Visualize the different sources of lift plt.figure(dpi=300) plt.plot(reduced_time, total_lift, label="Total Lift", lw=2) plt.plot(reduced_time, gust_lift, label="Gust Lift", lw=2) plt.plot(reduced_time, pitch_lift, label="Pitching Lift", lw=2) plt.plot(reduced_time, added_mass_lift, label="Added Mass Lift", lw=2) plt.legend() plt.xlabel("Reduced time") plt.ylabel("$C_\ell$") plt.title("Guassian Pitch Maneuver Through Top-Hat Gust")
def draw(self, draw_mcl=True, backend="matplotlib", show=True): """ Draw the airfoil object. :param draw_mcl: Should we draw the mean camber line (MCL)? [boolean] :param backend: Which backend should we use? "plotly" or "matplotlib" :return: None """ x = np.array(self.x()).reshape(-1) y = np.array(self.y()).reshape(-1) if draw_mcl: x_mcl = np.linspace(np.min(x), np.max(x), len(x)) y_mcl = self.local_camber(x_mcl) if backend == "matplotlib": color = '#280887' plt.plot(x, y, ".-", zorder=11, color=color) plt.fill(x, y, zorder=10, color=color, alpha=0.2) if draw_mcl: plt.plot(x_mcl, y_mcl, "-", zorder=4, color=color, alpha=0.4) plt.axis("equal") plt.xlabel(r"$x/c$") plt.ylabel(r"$y/c$") plt.title(f"{self.name} Airfoil") plt.tight_layout() if show: plt.show() elif backend == "plotly": from aerosandbox.visualization.plotly import go fig = go.Figure() fig.add_trace( go.Scatter(x=x, y=y, mode="lines+markers", name="Airfoil", fill="toself", line=dict(color="blue")), ) if draw_mcl: fig.add_trace( go.Scatter(x=x_mcl, y=y_mcl, mode="lines+markers", name="Mean Camber Line (MCL)", line=dict(color="navy"))) fig.update_layout(xaxis_title="x/c", yaxis_title="y/c", yaxis=dict(scaleanchor="x", scaleratio=1), title=f"{self.name} Airfoil") if show: fig.show() else: return fig
def test_euler_angles_equivalence_to_general_3D(): phi = 1 theta = 2 psi = 3 rot_euler = np.rotation_matrix_from_euler_angles(phi, theta, psi) rot_manual = ( np.rotation_matrix_3D(psi, np.array([0, 0, 1])) @ np.rotation_matrix_3D(theta, np.array([0, 1, 0])) @ np.rotation_matrix_3D(phi, np.array([1, 0, 0])) ) assert rot_euler == pytest.approx(rot_manual)
def test_cross_1D_input(): a = np.array([1, 1, 1]) b = np.array([1, 2, 3]) cas_a = cas.DM(a) cas_b = cas.DM(b) correct_result = np.cross(a, b) cas_correct_result = cas.DM(correct_result) assert np.all(np.cross(a, cas_b) == cas_correct_result) assert np.all(np.cross(cas_a, b) == cas_correct_result) assert np.all(np.cross(cas_a, cas_b) == cas_correct_result)
def test_cross_2D_input_first_axis(): a = np.tile(np.array([1, 1, 1]), (3, 1)).T b = np.tile(np.array([1, 2, 3]), (3, 1)).T cas_a = cas.DM(a) cas_b = cas.DM(b) correct_result = np.cross(a, b, axis=0) cas_correct_result = cas.DM(correct_result) assert np.all(np.cross(a, cas_b, axis=0) == cas_correct_result) assert np.all(np.cross(cas_a, b, axis=0) == cas_correct_result) assert np.all(np.cross(cas_a, cas_b, axis=0) == cas_correct_result)
def draw(self, draw_mcl=True, backend="plotly", show=True): """ Draw the airfoil object. :param draw_mcl: Should we draw the mean camber line (MCL)? [boolean] :param backend: Which backend should we use? "plotly" or "matplotlib" :return: None """ x = np.array(self.x()).reshape(-1) y = np.array(self.y()).reshape(-1) if draw_mcl: x_mcl = np.linspace(np.min(x), np.max(x), len(x)) y_mcl = self.local_camber(x_mcl) if backend == "plotly": fig = go.Figure() fig.add_trace( go.Scatter(x=x, y=y, mode="lines+markers", name="Airfoil", fill="toself", line=dict(color="blue")), ) if draw_mcl: fig.add_trace( go.Scatter(x=x_mcl, y=y_mcl, mode="lines+markers", name="Mean Camber Line (MCL)", line=dict(color="navy"))) fig.update_layout(xaxis_title="x/c", yaxis_title="y/c", yaxis=dict(scaleanchor="x", scaleratio=1), title="%s Airfoil" % self.name) if show: fig.show() else: return fig elif backend == "matplotlib": fig, ax = plt.subplots(1, 1, figsize=(6.4, 4.8), dpi=200) plt.plot(x, y, ".-", zorder=11, color='#280887') if draw_mcl: plt.plot(x_mcl, y_mcl, "-", zorder=4, color='#28088744') plt.axis("equal") plt.xlabel(r"$x/c$") plt.ylabel(r"$y/c$") plt.title("%s Airfoil" % self.name) plt.tight_layout() if show: plt.show() else: return fig, ax
def xyz_te(self) -> np.ndarray: """ Returns the (wing-relative) coordinates of the trailing edge of the cross section. """ rot = np.rotation_matrix_3D(self.twist * pi / 180, self.twist_axis) xyz_te = self.xyz_le + rot @ np.array([self.chord, 0, 0]) return xyz_te
def test_interpn_bounds_error_multiple_samples(): def value_func_3d(x, y, z): return 2 * x + 3 * y - z x = np.linspace(0, 5, 10) y = np.linspace(0, 5, 20) z = np.linspace(0, 5, 30) points = (x, y, z) values = value_func_3d(*np.meshgrid(*points, indexing="ij")) point = np.array([ [2.21, 3.12, 1.15], [3.42, 5.81, 2.43] ]) with pytest.raises(ValueError): value = np.interpn( points, values, point ) ### CasADi test point = cas.DM(point) with pytest.raises(ValueError): value = np.interpn( points, values, point )
def get_coordinates_from_raw_dat(raw_text) -> np.ndarray: """ Returns a Nx2 ndarray of airfoil coordinates from the raw text of a airfoil *.dat file. Args: raw_text: The raw text of the *.dat file, as read by file.readlines() Returns: A Nx2 ndarray of airfoil coordinates [x, y]. """ raw_coordinates = [] def is_number( s): # determines whether a string is representable as a float try: float(s) except ValueError: return False return True for line in raw_text: try: line_split = re.split(r'[; |, |\*|\n]', line) line_items = [s for s in line_split if s != "" and is_number(s)] if len(line_items) == 2: raw_coordinates.append(line_items) except: pass if len(raw_coordinates) == 0: raise ValueError("File was found, but could not read any coordinates!") coordinates = np.array(raw_coordinates, dtype=float) return coordinates
def test_reshape(): a = np.array([ [1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], ]) b = cas.DM(a) test_inputs = [ -1, (4, 3), (3, 4), (12, 1), (1, 12), (-1), (12, -1), (-1, 12), ] for i in test_inputs: ra = np.reshape(a, i) rb = np.reshape(b, i) if len(ra.shape) == 1: ra = ra.reshape(-1, 1) assert np.all(ra == rb)
def test_assert_equal_shape(): a = np.array([1, 2, 3]) b = cas.DM(a) np.assert_equal_shape([a, a]) np.assert_equal_shape({ "thing1": a, "thing2": a, }) with pytest.raises(ValueError): np.assert_equal_shape([np.array([1, 2, 3]), np.array([1, 2, 3, 4])]) np.assert_equal_shape({ "thing1": np.array([1, 2, 3]), "thing2": np.array([1, 2, 3, 4]) }) np.assert_equal_shape([2, 3, 4])
def test_sum2(): # Check it returns the same results with casadi and numpy a = np.array([[1, 2, 3], [1, 2, 3]]) b = cas.SX(a) assert np.all(np.sum(a) == cas.DM(np.sum(b))) assert np.all(np.sum(a, axis=1) == cas.DM(np.sum(b, axis=1)))
def convert_mesh_to_polydata_format( points, faces ): """ Pyvista uses a slightly different convention for the standard (points, faces) format as described above. They give `faces` as a single 1D vector of roughly length (M*3), or (M*4) in the case of quadrilateral meshing. Basically, the mesh displayer goes down the `faces` array, and when it sees a number N, it interprets that as the number of vertices in the following face. Then, the next N entries are interpreted as integer references to the vertices of the face. This has the benefit of allowing for mixed tri/quad meshes. Args: points: `points` array of the original standard-format mesh faces: `faces` array of the original standard-format mesh Returns: (points, faces), except that `faces` is now in a pyvista.PolyData compatible format. """ faces = [ [len(face), *face] for face in faces ] faces = np.array(faces) faces = np.reshape(faces, -1) return points, faces
def rotate(self, angle: float, x_center: float = 0., y_center: float = 0.) -> 'Airfoil': """ Rotates the airfoil clockwise by the specified amount, in radians. Rotates about the point (x_center, y_center), which is (0, 0) by default. Args: angle: Angle to rotate, counterclockwise, in radians. x_center: The x-coordinate of the center of rotation. y_center: The y-coordinate of the center of rotation. Returns: The rotated Airfoil. """ coordinates = np.copy(self.coordinates) ### Translate translation = np.array([x_center, y_center]) coordinates -= translation ### Rotate rotation_matrix = np.rotation_matrix_2D(angle=angle, ) coordinates = (rotation_matrix @ coordinates.T).T ### Translate coordinates += translation return Airfoil(name=self.name, coordinates=coordinates)
def test_interpn_fill_value(): def value_func_3d(x, y, z): return 2 * x + 3 * y - z x = np.linspace(0, 5, 10) y = np.linspace(0, 5, 20) z = np.linspace(0, 5, 30) points = (x, y, z) values = value_func_3d(*np.meshgrid(*points, indexing="ij")) point = np.array([5.21, 3.12, 1.15]) value = np.interpn( points, values, point, method="bspline", bounds_error=False, fill_value=-17 ) assert value == pytest.approx(-17) value = np.interpn( points, values, point, method="bspline", bounds_error=False, ) assert np.isnan(value) value = np.interpn( points, values, point, method="bspline", bounds_error=None, fill_value=None ) assert value == pytest.approx(value_func_3d(5, 3.12, 1.15))
def test_cas_vector(): output = reflect_over_XZ_plane(cas.DM(vec)) assert isinstance(output, cas.DM) assert np.all( output == np.array([0, -1, 2]) )