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 test_fuselage_aerodynamics_optimization(): opti = asb.Opti() alpha = opti.variable(init_guess=0, lower_bound=0, upper_bound=30) beta = opti.variable(init_guess=0) fuselage = asb.Fuselage(xsecs=[ asb.FuselageXSec(xyz_c=[xi, 0, 0], radius=asb.Airfoil("naca0010").local_thickness( 0.8 * xi)) for xi in np.cosspace(0, 1, 20) ], ) aero = asb.AeroBuildup(airplane=asb.Airplane(fuselages=[fuselage]), op_point=asb.OperatingPoint(velocity=10, alpha=alpha, beta=beta)).run() opti.minimize(-aero["L"] / aero["D"]) sol = opti.solve(verbose=False) print(sol.value(alpha)) assert sol.value(alpha) > 10 and sol.value(alpha) < 20 assert sol.value(beta) == pytest.approx(0, abs=1e-3) opti.minimize(aero["D"]) sol = opti.solve(verbose=False) assert sol.value(alpha) == pytest.approx(0, abs=1e-2) assert sol.value(beta) == pytest.approx(0, abs=1e-2)
def _create_winglet(self): """Create winglet. Returns ------- aerosandbox.Wing """ # Extract winglet configuration parameters = self.winglet_parameters dimensions = self.winglet_dimensions location_tip = self.__get_winglet_vector__( length=dimensions["length"], sweep=parameters[W_ANGLE_SWEEP], cant=parameters[W_ANGLE_CANT], ) # Slightly separete from the wing _epsilon_winglet_wing = Point([0, 0, 0.01]) # Get wing tip section _section = self.__wingtip_section__ _coordinates = list(_section[LE_LOCATION]) coordinates_weld = _coordinates + _epsilon_winglet_wing # Match TE of wing tip and winglet root chord chord_root = dimensions["chord_root"] chord_tip = dimensions["chord_tip"] coordinates_weld.x += _section[CHORD] - chord_root winglet_airfoil = sbx.Airfoil(name=parameters[W_AIRFOIL]) twist_root = parameters[W_ANGLE_TWIST_ROOT] twist_tip = parameters[W_ANGLE_TWIST_TIP] winglet = sbx.Wing( name="Winglet", xyz_le=list(coordinates_weld), symmetric=True, xsecs=[ sbx.WingXSec( xyz_le=[0, 0, 0], chord=chord_root, twist=twist_root, airfoil=winglet_airfoil, ), sbx.WingXSec( xyz_le=list(location_tip), chord=chord_tip, twist=twist_tip, airfoil=winglet_airfoil, ), ], ) self.winglet = [winglet] return winglet
def test_airfoil_symmetric_NACA(): a = asb.AirfoilInviscid(airfoil=[asb.Airfoil("naca0012").repanel(50)], op_point=asb.OperatingPoint( velocity=1, alpha=0, )) assert a.Cl == pytest.approx(0, abs=1e-8)
def create_wing_planform(self): """Create planform object. Returns ------- aerosanbox.Wing """ # Create sections sections = [] for _section in self.sections: _coordinates = list(_section[LE_LOCATION]) _airfoil = sbx.Airfoil(name=_section[AIRFOIL]) _sbx_section = sbx.WingXSec( xyz_le= _coordinates, # Coordinates of the XSec's leading edge, **relative** to the wing's leading edge. chord=_section[CHORD], twist=_section[TWIST], # degrees airfoil=_airfoil, ) sections.append(_sbx_section) # Create wing planform = sbx.Wing( name=self.NAME, xyz_le=[0.0, 0.0, 0.0], # Coordinates of the wing's leading edge symmetric=True, xsecs=sections, ) self.planform = planform return planform
with open(filename) as f: data = f.readlines() data = [json.loads(line) for line in data] data = {k: np.array([d[k] for d in data]) for k in data[0].keys()} order = np.argsort(data['mach']) data = {k: v[order] for k, v in data.items()} return data machs = np.linspace(0, 1.3, 500) airfoil = asb.Airfoil("rae2822") airfoil.generate_polars(cache_filename="./cache/rae2822.json") aerobuildup_data = { "mach": machs, "CL": airfoil.CL_function(1, 6.5e6, machs, 0) * np.ones_like(machs), "CD": airfoil.CD_function(1, 6.5e6, machs, 0) * np.ones_like(machs), "CM": airfoil.CM_function(1, 6.5e6, machs, 0) * np.ones_like(machs), } datas = { "XFoil v6 (P-G)": get_data(data_folder / "xfoil6.csv"), "MSES (Euler + IBL)": get_data(data_folder / "mses.csv"), "SU2 (RANS)": get_data(data_folder / "su2.csv"), "ASB AeroBuildup": aerobuildup_data, }
import aerosandbox as asb import aerosandbox.numpy as np from aerosandbox.library import airfoils wing_airfoil = asb.Airfoil("sd7037") tail_airfoil = asb.Airfoil("naca0010") ### Define the 3D geometry you want to analyze/optimize. # Here, all distances are in meters and all angles are in degrees. airplane = asb.Airplane( name="Peter's Glider", xyz_ref=[0.04, 0, 0], # CG location wings=[ asb.Wing( name="Main Wing", symmetric=True, # Should this wing be mirrored across the XZ plane? xsecs=[ # The wing's cross ("X") sections asb.WingXSec( # Root xyz_le=[0, 0, 0], # Coordinates of the XSec's leading edge, relative to the wing's leading edge. chord=0.18, twist=0, # degrees airfoil=wing_airfoil, # Airfoils are blended between a given XSec and the next one. control_surface_is_symmetric=True, # Flap (ctrl. surfs. applied between this XSec and the next one.) control_surface_deflection=0, # degrees ), asb.WingXSec( # Mid xyz_le=[0.01, 0.5, 0], chord=0.16, twist=0, airfoil=wing_airfoil,
import aerosandbox as asb import aerosandbox.numpy as np import pytest airplane = asb.Airplane(wings=[ asb.Wing(xsecs=[ asb.WingXSec( xyz_le=[0, 0, 0], chord=1, airfoil=asb.Airfoil("naca0001")), asb.WingXSec( xyz_le=[0, 1, 0], chord=1, airfoil=asb.Airfoil("naca0001")) ]) ]) def LD_from_alpha(alpha): op_point = asb.OperatingPoint( velocity=1, alpha=alpha, ) vlm = asb.VortexLatticeMethod(airplane, op_point, align_trailing_vortices_with_wind=True) aero = vlm.run() CD0 = 0.01 LD = aero["CL"] / (aero["CD"] + CD0) return LD
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]
import aerosandbox as asb import aerosandbox.numpy as np if __name__ == '__main__': af = asb.Airfoil("dae11") af.generate_polars() alpha = np.linspace(-40, 40, 300) re = np.geomspace(1e4, 1e12, 100) Alpha, Re = np.meshgrid(alpha, re) af.CL_function(alpha=0, Re=1e6) CL = af.CL_function(Alpha.flatten(), Re.flatten()).reshape(Alpha.shape) CD = af.CD_function(Alpha.flatten(), Re.flatten()).reshape(Alpha.shape) CM = af.CM_function(Alpha.flatten(), Re.flatten()).reshape(Alpha.shape) ##### Plot alpha-Re contours from aerosandbox.tools.pretty_plots import plt, show_plot, contour fig, ax = plt.subplots() contour(Alpha, Re, CL, levels=30, colorbar_label=r"$C_L$") plt.scatter(af.xfoil_data["alpha"], af.xfoil_data["Re"], color="k", alpha=0.2) plt.yscale('log') show_plot( f"Auto-generated Polar for {af.name} Airfoil", "Angle of Attack [deg]", "Reynolds Number [-]", )
testPlane = ae.Airplane( name='ju87', xyz_ref=[0, 0, 0], # CG location wings=[ ae.Wing( name="Main Wing", xyz_le=[0, 0, 0], # Coordinates of the wing's leading edge symmetric=True, xsecs=[ # The wing's cross ("X") sections ae.WingXSec( # Root xyz_le=[ 0, 0, x_trans[0] ], # Coordinates of the XSec's leading edge, relative to the wing's leading edge. chord=CHORD, twist=0, # degrees airfoil=ae.Airfoil(name="naca4412"), control_surface_type='symmetric', # Flap # Control surfaces are applied between a given XSec and the next one. control_surface_deflection=0, # degrees control_surface_hinge_point=0.75 # as chord fraction ), ae.WingXSec( # Mid xyz_le=[0, x_trans[1], 0], chord=CHORD, twist=0, airfoil=ae.Airfoil(name="naca4412"), control_surface_type='asymmetric', # Aileron control_surface_deflection=0, control_surface_hinge_point=0.75), ae.WingXSec( # Tip xyz_le=[0, 1, x_trans[2]],
def test_airfoil_with_TE_gap(): a = asb.AirfoilInviscid(airfoil=asb.Airfoil("naca4408").repanel(100), op_point=asb.OperatingPoint(velocity=1, alpha=5)) assert a.Cl == pytest.approx(1.0754, abs=0.01) # From XFoil
def test_airfoil_ground_effect(): a = asb.AirfoilInviscid( airfoil=asb.Airfoil("naca4408").repanel(100).translate(0, 0.2), op_point=asb.OperatingPoint(velocity=1, alpha=0), ground_effect=True) assert a.calculate_velocity(0, 0)[1] == pytest.approx(0)
def CL_function(alpha, Re, mach, deflection): return 2 * np.pi * np.radians(alpha) def CD_function(alpha, Re, mach, deflection): return 0 * Re**0 def CM_function(alpha, Re, mach, deflection): return 0 * alpha ideal_airfoil = asb.Airfoil(name="Ideal Airfoil", coordinates=get_NACA_coordinates('naca0012'), CL_function=CL_function, CD_function=CD_function, CM_function=CM_function) wing = asb.Wing(xsecs=[ asb.WingXSec(xyz_le=[0, y_i, 0], chord=1, airfoil=ideal_airfoil) for y_i in [0, 10] ], symmetric=True) airplane = asb.Airplane(wings=[wing]) op_point = asb.OperatingPoint(velocity=340, alpha=0) aero = asb.AeroBuildup(airplane=airplane, op_point=op_point).run()
import aerosandbox as asb import aerosandbox.numpy as np af = asb.Airfoil("naca0012") cache = asb._asb_root / "geometry/airfoil/test_airfoil/naca0012.json" def make_cache(): af.generate_polars(cache_filename=cache) def test_load_cache(): af.generate_polars(cache_filename=cache) if __name__ == '__main__': make_cache() test_load_cache()
import aerosandbox as asb import aerosandbox.numpy as np import matplotlib.pyplot as plt import aerosandbox.tools.pretty_plots as p fuselage = asb.Fuselage(xsecs=[ asb.FuselageXSec(xyz_c=[xi, 0, 0], radius=asb.Airfoil("naca0020").local_thickness(xi) / 2) for xi in np.cosspace(0, 1, 20) ], ) fig, ax = plt.subplots(figsize=(7, 6)) V = np.linspace(10, 1000, 1001) op_point = asb.OperatingPoint(velocity=V, ) aero = asb.AeroBuildup( airplane=asb.Airplane(fuselages=[fuselage]), op_point=op_point, ).run() plt.plot(op_point.mach(), aero["CD"], label="Full Model") aero = asb.AeroBuildup(airplane=asb.Airplane(fuselages=[fuselage]), op_point=op_point, include_wave_drag=False).run() plt.plot(op_point.mach(), aero["CD"], zorder=1.9, label="Model without Wave Drag")
import aerosandbox as asb import aerosandbox.numpy as np sd7037 = asb.Airfoil("sd7037") airplane = asb.Airplane( name="Vanilla", xyz_ref=[0.5, 0, 0], s_ref=9, c_ref=0.9, b_ref=10, wings=[ asb.Wing(name="Wing", symmetric=True, xsecs=[ asb.WingXSec( xyz_le=[0, 0, 0], chord=1, twist=2, airfoil=sd7037, ), asb.WingXSec( xyz_le=[0.2, 5, 1], chord=0.6, twist=2, airfoil=sd7037, ) ]), asb.Wing(name="H-stab", symmetric=True, xyz_le=[4, 0, 0],
def __init__(self, filePrefix="", fileDirectory="", CLfitOrder=3, CDpfitOrder=2, CmfitOrder=3, weightedFit=False, plotFit=False): """ Class containing airfoil read from file, as well as CL, CDp and Cm interpolation functions Point pairs (Re, AOA) as well as CL, CDp and Cm at each point is .points and .values(CL/CDp/Cm) :param filePrefix: an identifier for the airfoil file names, such as "NACA 0012_" :param fileDirectory: location of files :param fitOrder: degree of polynomial fit for CL, CDp and Cm, default is 3 for performance :param weightedFit: if True, the reader will add weight to polars near 2.5deg AOA using a cos function with period T = 40deg :param plotFit: if True, the data will be scatter-plotted with poly fits """ # Airfoil instance with characteristics read from files self.airfoil = None # Polars read from file self.polars = [] # Point pairs (Re, AOA) as well as CL CDp and Cm at each point self.points = [] self.valuesCL = [] self.valuesCDp = [] self.valuesCm = [] # A list of Reynolds numbers self.Res = [] # AOA, CL, CDp, Cm at each Re self.AOAs_Re = [] self.CLs_Re = [] self.CDps_Re = [] self.Cms_Re = [] # Fit characteristics self.CLfitOrder = CLfitOrder self.CDpfitOrder = CDpfitOrder self.CmfitOrder = CmfitOrder self.weightedFit = weightedFit self.plotFit = plotFit # Polyfit coeffs at each Re w.r.t. angle self.CLfit_Re = [] self.CDpfit_Re = [] self.Cmfit_Re = [] # Keep some handy flags self.importedPolars = False self.createdCPolyfitTables = False if filePrefix is not "" and fileDirectory is not "": print("\nReading from xflr5 files...") try: # Try getting polars from file self.xflr5AirfoilPolarReader(filePrefix, fileDirectory) self.importedPolars = True except: self.importedPolars = False print("Read unsuccessful!") if self.importedPolars: print("Read successful!") print("\nCreating polynomial fits for coefficients...") try: # Create lookup tables for CL, CDp and Cm self.CreateCoefficientPolyfitTables() self.createdCPolyfitTables = True except: print("Fit unsuccessful!") self.createdCPolyfitTables = False if self.createdCPolyfitTables: print("Fit successful!") # Create Airfoil instance self.airfoil = asb.Airfoil( CL_function=lambda alpha, Re, mach, deflection: ( # Lift coefficient function self.CL_function(Re * 10e-6, alpha)), CDp_function=lambda alpha, Re, mach, deflection: ( # Profile drag coefficient function self.CDp_function(Re * 10e-6, alpha)), Cm_function=lambda alpha, Re, mach, deflection: ( # Moment coefficient function self.Cm_function(Re * 10e-6, alpha))) try: self.PlotPolyFit() except: print("Plot unsuccessful")
import aerosandbox as asb import aerosandbox.numpy as np airfoil = asb.Airfoil("naca0008") airplane = asb.Airplane(name="Flat Plate", xyz_ref=[0, 0, 0], wings=[ asb.Wing(name="Wing", symmetric=True, xsecs=[ asb.WingXSec( xyz_le=[0, 0, 0], chord=1, twist=0, airfoil=airfoil, ), asb.WingXSec( xyz_le=[0, 5, 0], chord=1, twist=0, airfoil=airfoil, ), ]) ]) if __name__ == '__main__': airplane.draw()
import aerosandbox as asb import aerosandbox.numpy as np from aerosandbox.tools import units as u def ft_to_m(feet, inches=0): # Converts feet (and inches) to meters return feet * u.foot + inches * u.inch naca2412 = asb.Airfoil("naca2412") naca0012 = asb.Airfoil("naca0012") naca2412.generate_polars(cache_filename="assets/naca2412.json") naca0012.generate_polars(cache_filename="assets/naca0012.json") airplane = asb.Airplane( name="Cessna 152", wings=[ asb.Wing(name="Wing", xsecs=[ asb.WingXSec(xyz_le=[0, 0, 0], chord=ft_to_m(5, 4), airfoil=naca2412), asb.WingXSec( xyz_le=[0, ft_to_m(7), ft_to_m(7) * np.sind(1)], chord=ft_to_m(5, 4), airfoil=naca2412), asb.WingXSec(xyz_le=[ ft_to_m(4, 3 / 4) - ft_to_m(3, 8 + 1 / 2), ft_to_m(33, 4) / 2, ft_to_m(33, 4) / 2 * np.sind(1)