def _enforce_governing_equations(self): # Calculate unsteady lift due to pitching wagner = wagners_function(self.reduced_time) ds = self.reduced_time[1:] - self.reduced_time[:-1] da_ds = (self.angles_of_attack[1:] - self.angles_of_attack[:-1]) / ds init_term = self.angles_of_attack[0] * wagner[:-1] for i in range(self.timesteps - 1): integral_term = np.sum(da_ds[j] * wagner[i - j] * ds[j] for j in range(i)) self.lift_coefficients[i] = 2 * np.pi * (integral_term + init_term[i]) # Calculate unsteady lift due to transverse gust kussner = kussners_function(self.reduced_time) dw_ds = (self.gust_profile[1:] - self.gust_profile[:-1]) / ds init_term = self.gust_profile[0] * kussner for i in range(self.timesteps - 1): integral_term = 0 for j in range(i): integral_term += dw_ds[j] * kussner[i - j] * ds[j] self.lift_coefficients[i] += 2 * np.pi / self.velocity * ( init_term[i] + integral_term) # Calculate unsteady lift due to added mass self.lift_coefficients += np.pi / 2 * np.cos( self.angles_of_attack[:-1])**2 * da_ds # Integral of lift to be minimized lift_squared_integral = np.sum(self.lift_coefficients**2) # Constraints and objective to minimize self.opti.subject_to(self.angles_of_attack[0] == 0) self.opti.minimize(lift_squared_integral)
def make_matrix(x): matrix = np.ones((len(x), degree + 1)) for j in range(1, degree - 2): matrix[:, j] = matrix[:, j - 1] * x matrix[:, degree - 2] = np.cos(x) matrix[:, degree - 1] = np.sin(x) matrix[:, degree] = np.exp(x) return matrix
def firefly_CLA_and_CDA_fuse_hybrid( # TODO remove fuse_fineness_ratio, fuse_boattail_angle, fuse_TE_diameter, fuse_length, fuse_diameter, alpha, V, mach, rho, mu, ): """ Estimated equiv. lift area and equiv. drag area of the Firefly fuselage, component buildup. :param fuse_fineness_ratio: Fineness ratio of the fuselage nose (length / diameter). 0.5 is hemispherical. :param fuse_boattail_angle: Boattail half-angle [deg] :param fuse_TE_diameter: Diameter of the fuselage's base at the "trailing edge" [m] :param fuse_length: Length of the fuselage [m] :param fuse_diameter: Diameter of the fuselage [m] :param alpha: Angle of attack [deg] :param V: Airspeed [m/s] :param mach: Mach number [unitless] :param rho: Air density [kg/m^3] :param mu: Dynamic viscosity of air [Pa*s] :return: A tuple of (CLA, CDA) [m^2] """ alpha_rad = alpha * np.pi / 180 sin_alpha = np.sin(alpha_rad) cos_alpha = np.cos(alpha_rad) """ Lift of a truncated fuselage, following slender body theory. """ separation_location = 0.3 # 0 for separation at trailing edge, 1 for separation at start of boat-tail. Calibrate this. diameter_at_separation = ( 1 - separation_location ) * fuse_TE_diameter + separation_location * fuse_diameter fuse_TE_area = np.pi / 4 * diameter_at_separation**2 CLA_slender_body = 2 * fuse_TE_area * alpha_rad # Derived from FVA 6.6.5, Eq. 6.75 """ Crossflow force, following the method of Jorgensen, 1977: "Prediction of Static Aero. Chars. for Slender..." """ V_crossflow = V * sin_alpha Re_crossflow = rho * np.abs(V_crossflow) * fuse_diameter / mu mach_crossflow = mach * np.abs(sin_alpha) eta = 1 # TODO make this a function of overall fineness ratio Cdn = 0.2 # Taken from suggestion for supercritical cylinders in Hoerner's Fluid Dynamic Drag, pg. 3-11 S_planform = fuse_diameter * fuse_length CNA = eta * Cdn * S_planform * sin_alpha * np.abs(sin_alpha) CLA_crossflow = CNA * cos_alpha CDA_crossflow = CNA * sin_alpha CLA = CLA_slender_body + CLA_crossflow r""" Zero-lift_force drag_force Model derived from high-fidelity data at: C:\Projects\GitHub\firefly_aerodynamics\Design_Opt\studies\Circular Firefly Fuse CFD\Zero-Lift Drag """ c = np.array([ 112.57153128951402720758778741583, -7.1720570832587240417410612280946, -0.01765596807595304351679033061373, 0.0026135564778264172743071913629365, 550.8775012129947299399645999074, 3.3166868391027000129156476759817, 11774.081980549422951298765838146, 3073258.204571904614567756652832, 0.0299, ]) # coefficients fuse_Re = rho * np.abs(V) * fuse_length / mu CDA_zero_lift = ( (c[0] * np.exp(c[1] * fuse_fineness_ratio) + c[2] * fuse_boattail_angle + c[3] * fuse_boattail_angle**2 + c[4] * fuse_TE_diameter**2 + c[5]) / c[6] * (fuse_Re / c[7])**(-1 / 7) * (fuse_length * fuse_diameter) / c[8]) r""" Assumes a ring-shaped wake projection into the Trefftz plane with a sinusoidal circulation distribution. This results in a uniform grad(phi) within the ring in the Trefftz plane. Derivation at C:\Projects\GitHub\firefly_aerodynamics\Gists and Ideas\Ring Wing Potential Flow """ fuse_oswalds_efficiency = 0.5 CDiA_slender_body = CLA**2 / ( diameter_at_separation**2 * np.pi * fuse_oswalds_efficiency) / 2 # or equivalently, use the next line # CDiA_slender_body = fuse_TE_area * alpha_rad ** 2 / 2 CDA = CDA_crossflow + CDiA_slender_body + CDA_zero_lift return CLA, CDA
opti = asb.Opti() # v_a = opti.variable(init_guess=15) # v_t = opti.variable(init_guess=15) # u_a = opti.variable(init_guess=5) Psi = opti.variable(init_guess=pi/2) # h_ati = opti.variable(init_guess=0.01) # f = opti.variable(init_guess=300) # F = opti.variable(init_guess=1) # gamma = opti.variable(init_guess=1) ### Define velocity triangle components U_a = airspeed #+ u_a # Axial velocity w/o induced eff. assuming u_a = 0 U_t = omega * radial_loc # Tangential velocity w/o induced eff. 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
def performance( self, speed, throttle_state=1, grade=0, headwind=0, ): ##### Figure out electric thrust force wheel_radius = self.wheel_diameter / 2 wheel_rads_per_sec = speed / wheel_radius wheel_rpm = wheel_rads_per_sec * 30 / np.pi motor_rpm = wheel_rpm / self.gear_ratio ### Limit performance by either max voltage or max current perf_via_max_voltage = self.motor.performance( voltage=self.max_voltage, rpm=motor_rpm, ) perf_via_throttle = self.motor.performance( current=self.max_current * throttle_state, rpm=motor_rpm, ) if perf_via_max_voltage['torque'] > perf_via_throttle['torque']: perf = perf_via_throttle else: perf = perf_via_max_voltage motor_torque = perf['torque'] wheel_torque = motor_torque / self.gear_ratio wheel_force = wheel_torque / wheel_radius thrust = wheel_force ##### Gravity gravity_drag = 9.81 * np.sin(np.arctan(grade)) * self.mass ##### Rolling Resistance # Crr = 0.0020 # Concrete Crr = 0.0050 # Asphalt # Crr = 0.0060 # Gravel # Crr = 0.0070 # Grass # Crr = 0.0200 # Off-road # Crr = 0.0300 # Sand rolling_drag = 9.81 * np.cos(np.arctan(grade)) * self.mass * Crr ##### Aerodynamics # CDA = 0.408 # Tops CDA = 0.324 # Hoods # CDA = 0.307 # Drops # CDA = 0.2914 # Aerobars eff_speed = speed + headwind air_drag = 0.5 * atmo.density() * eff_speed * np.abs(eff_speed) * CDA ##### Summation net_force = thrust - gravity_drag - rolling_drag - air_drag net_accel = net_force / self.mass return { "net acceleration": net_accel, "motor state": perf, }