def test_quadcopter_navigation(): opti = asb.Opti() N = 300 time_final = 1 time = np.linspace(0, time_final, N) left_thrust = opti.variable(init_guess=0.5, scale=1, n_vars=N, lower_bound=0, upper_bound=1) right_thrust = opti.variable(init_guess=0.5, scale=1, n_vars=N, lower_bound=0, upper_bound=1) mass = 0.1 dyn = asb.FreeBodyDynamics( opti_to_add_constraints_to=opti, time=time, xe=opti.variable(init_guess=np.linspace(0, 1, N)), ze=opti.variable(init_guess=np.linspace(0, -1, N)), u=opti.variable(init_guess=0, n_vars=N), w=opti.variable(init_guess=0, n_vars=N), theta=opti.variable(init_guess=np.linspace(np.pi / 2, np.pi / 2, N)), q=opti.variable(init_guess=0, n_vars=N), X=left_thrust + right_thrust, M=(right_thrust - left_thrust) * 0.1 / 2, mass=mass, Iyy=0.5 * mass * 0.1 ** 2, g=9.81, ) opti.subject_to([ # Starting state dyn.xe[0] == 0, dyn.ze[0] == 0, dyn.u[0] == 0, dyn.w[0] == 0, dyn.theta[0] == np.radians(90), dyn.q[0] == 0, ]) opti.subject_to([ # Final state dyn.xe[-1] == 1, dyn.ze[-1] == -1, dyn.u[-1] == 0, dyn.w[-1] == 0, dyn.theta[-1] == np.radians(90), dyn.q[-1] == 0, ]) effort = np.sum( # The average "effort per second", where effort is integrated as follows: np.trapz(left_thrust ** 2 + right_thrust ** 2) * np.diff(time) ) / time_final opti.minimize(effort) sol = opti.solve() dyn.substitute_solution(sol) assert sol.value(effort) == pytest.approx(0.714563, rel=0.01) print(sol.value(effort))
def test_quadcopter_flip(): opti = asb.Opti() N = 300 time_final = opti.variable(init_guess=1, lower_bound=0) time = np.linspace(0, time_final, N) left_thrust = opti.variable(init_guess=0.7, scale=1, n_vars=N, lower_bound=0, upper_bound=1) right_thrust = opti.variable(init_guess=0.6, scale=1, n_vars=N, lower_bound=0, upper_bound=1) mass = 0.1 dyn = asb.FreeBodyDynamics( opti_to_add_constraints_to=opti, time=time, xe=opti.variable(init_guess=np.linspace(0, 1, N)), ze=opti.variable(init_guess=0, n_vars=N), u=opti.variable(init_guess=0, n_vars=N), w=opti.variable(init_guess=0, n_vars=N), theta=opti.variable(init_guess=np.linspace(np.pi / 2, np.pi / 2 - 2 * np.pi, N)), q=opti.variable(init_guess=0, n_vars=N), X=left_thrust + right_thrust, M=(right_thrust - left_thrust) * 0.1 / 2, mass=mass, Iyy=0.5 * mass * 0.1 ** 2, g=9.81, ) opti.subject_to([ # Starting state dyn.xe[0] == 0, dyn.ze[0] == 0, dyn.u[0] == 0, dyn.w[0] == 0, dyn.theta[0] == np.radians(90), dyn.q[0] == 0, ]) opti.subject_to([ # Final state dyn.xe[-1] == 1, dyn.ze[-1] == 0, dyn.u[-1] == 0, dyn.w[-1] == 0, dyn.theta[-1] == np.radians(90 - 360), dyn.q[-1] == 0, ]) opti.minimize(time_final) sol = opti.solve(verbose=False) dyn.substitute_solution(sol) assert sol.value(time_final) == pytest.approx(0.824, abs=0.01)
def test_airfoil_multielement(): a = asb.AirfoilInviscid(airfoil=[ asb.Airfoil("e423").repanel(n_points_per_side=50), asb.Airfoil("naca6408").repanel(n_points_per_side=25).scale( 0.4, 0.4).rotate(np.radians(-20)).translate(0.9, -0.05), ], op_point=asb.OperatingPoint(velocity=1, alpha=5))
def default_CL_function(alpha, Re, mach, deflection): """ Lift coefficient. """ print_default_warning() Cl_inc = 2 * np.pi * np.radians(alpha) beta = (1 - mach)**2 Cl = Cl_inc * beta return Cl
def compute_rotation_matrix_wind_to_geometry(self) -> np.ndarray: """ Computes the 3x3 rotation matrix that transforms from wind axes to geometry axes. Returns: a 3x3 rotation matrix. """ alpha_rotation = np.rotation_matrix_3D(angle=np.radians(self.alpha), axis=np.array([0, 1, 0]), _axis_already_normalized=True) beta_rotation = np.rotation_matrix_3D(angle=np.radians(self.beta), axis=np.array([0, 0, 1]), _axis_already_normalized=True) axes_flip = np.rotation_matrix_3D( angle=np.pi, axis=np.array([0, 1, 0]), _axis_already_normalized=True ) # Since in geometry axes, X is downstream by convention, while in wind axes, X is upstream by convetion. Same with Z being up/down respectively. r = axes_flip @ alpha_rotation @ beta_rotation # where "@" is the matrix multiplication operator return r
def scattering_factor(elevation_angle): """ Calculates a scattering factor (a factor that gives losses due to atmospheric scattering at low elevation angles). Source: AeroSandbox/studies/SolarPanelScattering :param elevation_angle: Angle between the horizon and the sun [degrees] :return: Fraction of the light that is not lost to scattering. """ elevation_angle = np.clip(elevation_angle, 0, 90) theta = 90 - elevation_angle # Angle between panel normal and the sun, in degrees # # Model 1 # c = ( # 0.27891510500505767300438719757949, # -0.015994330894744987481281839336589, # -19.707332432605799255043166340329, # -0.66260979582573353852126274432521 # ) # scattering_factor = c[0] + c[3] * theta_rad + cas.exp( # c[1] * ( # cas.tan(theta_rad) + c[2] * theta_rad # ) # ) # Model 2 c = ( -0.04636, -0.3171 ) scattering_factor = np.exp( c[0] * ( np.tand(theta * 0.999) + c[1] * np.radians(theta) ) ) # # Model 3 # p1 = -21.74 # p2 = 282.6 # p3 = -1538 # p4 = 1786 # q1 = -923.2 # q2 = 1456 # x = theta_rad # scattering_factor = ((p1*x**3 + p2*x**2 + p3*x + p4) / # (x**2 + q1*x + q2)) # Keep this: # scattering_factor = cas.fmin(cas.fmax(scattering_factor, 0), 1) return scattering_factor
plt.ylim(-4, 1.1) plt.gca().invert_yaxis() plt.xlabel(r"$x/c$") plt.ylabel(r"$C_p$") plt.title(r"$C_p$ on Surface") plt.tight_layout() if show: plt.show() if __name__ == '__main__': a = AirfoilInviscid( airfoil=[ # Airfoil("naca4408") # .repanel(50) Airfoil("e423").repanel(n_points_per_side=50), Airfoil("naca6408").repanel(n_points_per_side=25).scale( 0.4, 0.4).rotate(np.radians(-20)).translate(0.9, -0.05), ], op_point=OperatingPoint( velocity=1, alpha=5, )) a.draw_streamlines() a.draw_cp() opti2 = Opti() b = AirfoilInviscid(airfoil=Airfoil("naca4408"), op_point=OperatingPoint(velocity=1, alpha=5), opti=opti2)
def display_graph(n_clicks, alpha, height, streamline_density, operating_checklist, *kulfan_inputs): ### Figure out if a button was pressed global n_clicks_last if n_clicks is None: n_clicks = 0 analyze_button_pressed = n_clicks > n_clicks_last n_clicks_last = n_clicks ### Parse the checklist ground_effect = "ground_effect" in operating_checklist ### Start constructing the figure airfoil = asb.Airfoil(coordinates=asb.get_kulfan_coordinates( lower_weights=np.array(kulfan_inputs[n_kulfan_inputs_per_side:]), upper_weights=np.array(kulfan_inputs[:n_kulfan_inputs_per_side]), TE_thickness=0, enforce_continuous_LE_radius=False, n_points_per_side=200, )) ### Do coordinates output coordinates_output = "\n".join( ["```"] + ["AeroSandbox Airfoil"] + ["\t%f\t%f" % tuple(coordinate) for coordinate in airfoil.coordinates] + ["```"]) ### Continue doing the airfoil things airfoil = airfoil.rotate(angle=-np.radians(alpha)) airfoil = airfoil.translate(0, height + 0.5 * np.sind(alpha)) fig = go.Figure() fig.add_trace( go.Scatter( x=airfoil.x(), y=airfoil.y(), mode="lines", name="Airfoil", fill="toself", line=dict(color="blue"), )) ### Default text output text_output = 'Click "Analyze" to compute aerodynamics!' xrng = (-0.5, 1.5) yrng = (-0.6, 0.6) if not ground_effect else (0, 1.2) if analyze_button_pressed: analysis = asb.AirfoilInviscid( airfoil=airfoil.repanel(50), op_point=asb.OperatingPoint( velocity=1, alpha=0, ), ground_effect=ground_effect, ) x = np.linspace(*xrng, 100) y = np.linspace(*yrng, 100) X, Y = np.meshgrid(x, y) u, v = analysis.calculate_velocity(x_field=X.flatten(), y_field=Y.flatten()) U = u.reshape(X.shape) V = v.reshape(Y.shape) streamline_fig = ff.create_streamline( x, y, U, V, arrow_scale=1e-16, density=streamline_density, line=dict(color="#ff82a3"), name="Streamlines", ) fig = go.Figure(data=streamline_fig.data + fig.data) text_output = make_table( pd.DataFrame({ "Engineering Quantity": ["C_L"], "Value": [f"{analysis.Cl:.3f}"] })) fig.update_layout( xaxis_title="x/c", yaxis_title="y/c", showlegend=False, yaxis=dict(scaleanchor="x", scaleratio=1), margin={"t": 0}, title=None, ) fig.update_xaxes(range=xrng) fig.update_yaxes(range=yrng) return fig, text_output, [coordinates_output]
plt.tight_layout() if show: plt.show() if __name__ == '__main__': a = AirfoilInviscid( airfoil=[ # Airfoil("naca4408") # .repanel(50) Airfoil("e423") .repanel(n_points_per_side=50), Airfoil("naca6408") .repanel(n_points_per_side=25) .scale(0.4, 0.4) .rotate(np.radians(-20)) .translate(0.9, -0.05), ], op_point=OperatingPoint( velocity=1, alpha=5, ) ) a.draw_streamlines() a.draw_cp() opti2 = Opti() b = AirfoilInviscid( airfoil=Airfoil("naca4408"), op_point=OperatingPoint( velocity=1,
def CL_function(alpha, Re, mach, deflection): return 2 * np.pi * np.radians(alpha)