def test_basic_math(types): for x in types["all"]: for y in types["all"]: ### Arithmetic x + y x - y x * y x / y np.sum(x) # Sum of all entries of array-like object x ### Exponentials & Powers x**y np.power(x, y) np.exp(x) np.log(x) np.log10(x) np.sqrt(x) # Note: do x ** 0.5 rather than np.sqrt(x). ### Trig np.sin(x) np.cos(x) np.tan(x) np.arcsin(x) np.arccos(x) np.arctan(x) np.arctan2(y, x) np.sinh(x) np.cosh(x) np.tanh(x) np.arcsinh(x) np.arccosh(x) np.arctanh(x - 0.5) # `- 0.5` to give valid argument
def TE_angle(self) -> float: """ Returns the trailing edge angle of the airfoil, in degrees """ upper_TE_vec = self.coordinates[0, :] - self.coordinates[1, :] lower_TE_vec = self.coordinates[-1, :] - self.coordinates[-2, :] return 180 / np.pi * (np.arctan2( upper_TE_vec[0] * lower_TE_vec[1] - upper_TE_vec[1] * lower_TE_vec[0], upper_TE_vec[0] * lower_TE_vec[0] + upper_TE_vec[1] * upper_TE_vec[1]))
import aerosandbox as asb import aerosandbox.numpy as np import pytest from scipy import integrate u_e_0 = 100 w_e_0 = -100 speed_0 = (u_e_0**2 + w_e_0**2)**0.5 gamma_0 = np.arctan2(-w_e_0, u_e_0) time = np.linspace(0, 10, 501) def get_trajectory(parameterization: type = asb.DynamicsPointMass2DCartesian, gravity=True, drag=True, plot=False): if parameterization is asb.DynamicsPointMass2DCartesian: dyn = parameterization( mass_props=asb.MassProperties(mass=1), x_e=0, z_e=0, u_e=u_e_0, w_e=w_e_0, ) elif parameterization is asb.DynamicsPointMass2DSpeedGamma: dyn = parameterization( mass_props=asb.MassProperties(mass=1), x_e=0, z_e=0, speed=speed_0,
def _calculate_induced_velocity_line_singularity_panel_coordinates( xp_field: Union[float, np.ndarray], yp_field: Union[float, np.ndarray], gamma_start: float = 0., gamma_end: float = 0., sigma_start: float = 0., sigma_end: float = 0., xp_panel_end: float = 1., ) -> [Union[float, np.ndarray], Union[float, np.ndarray]]: """ Calculates the induced velocity at a point (xp_field, yp_field) in a 2D potential-flow flowfield. The `p` suffix in `xp...` and `yp...` denotes the use of the panel coordinate system, where: * xp_hat is along the length of the panel * yp_hat is orthogonal (90 deg. counterclockwise) to it. In this flowfield, there is only one singularity element: A line vortex going from (0, 0) to (xp_panel_end, 0). The strength of this vortex varies linearly from: * gamma_start at (0, 0), to: * gamma_end at (xp_panel_end, 0). # TODO update paragraph By convention here, positive gamma induces clockwise swirl in the flow field. Function returns the 2D velocity u, v in the local coordinate system of the panel. Inputs x and y can be 1D ndarrays representing various field points, in which case the resulting velocities u and v have corresponding dimensionality. Equations from the seminal textbook "Low Speed Aerodynamics" by Katz and Plotkin. Vortex equations are Eq. 11.99 and Eq. 11.100. * Note: there is an error in equation 11.100 in Katz and Plotkin, at least in the 2nd ed: The last term of equation 11.100, which is given as: (x_{j+1} - x_j) / z + (theta_{j+1} - theta_j) has a sign error and should instead be written as: (x_{j+1} - x_j) / z - (theta_{j+1} - theta_j) Source equations are Eq. 11.89 and Eq. 11.90. """ ### Modify any incoming floats if isinstance(xp_field, (float, int)): xp_field = np.array([xp_field]) if isinstance(yp_field, (float, int)): yp_field = np.array([yp_field]) ### Determine if you can skip either the vortex or source parts skip_vortex_math = not (isinstance(gamma_start, cas.MX) or isinstance( gamma_end, cas.MX)) and gamma_start == 0 and gamma_end == 0 skip_source_math = not (isinstance(sigma_start, cas.MX) or isinstance( sigma_end, cas.MX)) and sigma_start == 0 and sigma_end == 0 ### Determine which points are effectively on the panel, necessitating different math: is_on_panel = np.fabs(yp_field) <= 1e-8 ### Do some geometry calculation r_1 = (xp_field**2 + yp_field**2)**0.5 r_2 = ((xp_field - xp_panel_end)**2 + yp_field**2)**0.5 ### Regularize is_on_endpoint = ((r_1 == 0) | (r_2 == 0)) r_1 = np.where( r_1 == 0, 1, r_1, ) r_2 = np.where(r_2 == 0, 1, r_2) ### Continue geometry calculation theta_1 = np.arctan2(yp_field, xp_field) theta_2 = np.arctan2(yp_field, xp_field - xp_panel_end) ln_r_2_r_1 = np.log(r_2 / r_1) d_theta = theta_2 - theta_1 tau = 2 * np.pi ### Regularize if the point is on the panel. yp_field_regularized = np.where(is_on_panel, 1, yp_field) ### VORTEX MATH if skip_vortex_math: u_vortex = 0 v_vortex = 0 else: d_gamma = gamma_end - gamma_start u_vortex_term_1_quantity = (yp_field / tau * d_gamma / xp_panel_end) u_vortex_term_2_quantity = (gamma_start * xp_panel_end + d_gamma * xp_field) / (tau * xp_panel_end) # Calculate u_vortex u_vortex_term_1 = u_vortex_term_1_quantity * ln_r_2_r_1 u_vortex_term_2 = u_vortex_term_2_quantity * d_theta u_vortex = u_vortex_term_1 + u_vortex_term_2 # Correct the u-velocity if field point is on the panel u_vortex = np.where(is_on_panel, 0, u_vortex) # Calculate v_vortex v_vortex_term_1 = u_vortex_term_2_quantity * ln_r_2_r_1 v_vortex_term_2 = np.where( is_on_panel, d_gamma / tau, u_vortex_term_1_quantity * (xp_panel_end / yp_field_regularized - d_theta), ) v_vortex = v_vortex_term_1 + v_vortex_term_2 ### SOURCE MATH if skip_source_math: u_source = 0 v_source = 0 else: d_sigma = sigma_end - sigma_start v_source_term_1_quantity = (yp_field / tau * d_sigma / xp_panel_end) v_source_term_2_quantity = (sigma_start * xp_panel_end + d_sigma * xp_field) / (tau * xp_panel_end) # Calculate v_source v_source_term_1 = -v_source_term_1_quantity * ln_r_2_r_1 v_source_term_2 = v_source_term_2_quantity * d_theta v_source = v_source_term_1 + v_source_term_2 # Correct the v-velocity if field point is on the panel v_source = np.where(is_on_panel, 0, v_source) # Calculate u_source u_source_term_1 = -v_source_term_2_quantity * ln_r_2_r_1 u_source_term_2 = np.where( is_on_panel, -d_sigma / tau, -v_source_term_1_quantity * (xp_panel_end / yp_field_regularized - d_theta), ) u_source = u_source_term_1 + u_source_term_2 ### Return u = u_vortex + u_source v = v_vortex + v_source ### If the field point is on the endpoint of the panel, replace the NaN with a zero. u = np.where(is_on_endpoint, 0, u) v = np.where(is_on_endpoint, 0, v) return u, v
U = (U_a ** 2 + U_t ** 2) ** 0.5 # Velocity magnitude W_a = 0.5 * U_a + 0.5 * U * np.sin(Psi) # Axial velocity w/ induced eff. W_t = 0.5 * U_t + 0.5 * U * np.cos(Psi) # Tangential velocity w/ induced eff. v_a = W_a - U_a # Axial induced velocity v_t = U_t - W_t # Tangential induced velocity W = (W_a ** 2 + W_t ** 2) ** 0.5 Re = air_density * W * chord_local / mu Ma = W / speed_of_sound v = (v_a ** 2 + v_t ** 2) ** 0.5 loc_wake_adv_ratio = (radial_loc / tip_radius) * (W_a / W_t) f = (n_blades / 2) * (1 - radial_loc / tip_radius) * 1 / loc_wake_adv_ratio F = 2 / pi * np.arccos(np.exp(-f)) ## Compute local blade quantities phi_rad = np.arctan2(W_a, W_t) #local flow angle phi_deg = phi_rad * 180 / pi alpha_rad = twist_local_rad - phi_rad alpha_deg = alpha_rad * 180 / pi ### Compute sectional lift and drag cl = airfoil_CL(alpha_deg, Re, Ma) cd = airfoil_CDp(alpha_deg, Re, Ma, cl) gamma = 0.5 * W * chord_local * cl ### Add governing equations opti.subject_to([ # 0.5 * v == 0.5 * U * cas.sin(Psi / 4), # v_a == v_t * W_t / W_a, # U ** 2 == v ** 2 + W ** 2, # gamma == -0.0145,