aerosandbox.geometry.airfoil ============================ .. py:module:: aerosandbox.geometry.airfoil Submodules ---------- .. toctree:: :maxdepth: 1 /autoapi/aerosandbox/geometry/airfoil/airfoil/index /autoapi/aerosandbox/geometry/airfoil/airfoil_families/index /autoapi/aerosandbox/geometry/airfoil/default_airfoil_aerodynamics/index /autoapi/aerosandbox/geometry/airfoil/kulfan_airfoil/index Classes ------- .. autoapisummary:: aerosandbox.geometry.airfoil.Airfoil aerosandbox.geometry.airfoil.KulfanAirfoil Package Contents ---------------- .. py:class:: Airfoil(name = 'Untitled', coordinates = None, **deprecated_keyword_arguments) Bases: :py:obj:`aerosandbox.geometry.polygon.Polygon` An airfoil. See constructor docstring for usage details. .. py:attribute:: name :value: 'Untitled' .. py:attribute:: coordinates :value: None .. py:method:: __repr__() .. py:method:: to_kulfan_airfoil(n_weights_per_side = 8, N1 = 0.5, N2 = 1.0, normalize_coordinates = True, use_leading_edge_modification = True) .. py:method:: generate_polars(alphas=np.linspace(-13, 13, 27), Res=np.geomspace(1000.0, 100000000.0, 12), cache_filename = None, xfoil_kwargs = None, unstructured_interpolated_model_kwargs = None, include_compressibility_effects = True, transonic_buffet_lift_knockdown = 0.3, make_symmetric_polars = False) Generates airfoil polar surrogate models (CL, CD, CM functions) from XFoil data and assigns them in-place to this Airfoil's polar functions. In other words, when this function is run, the following functions will be added (or overwritten) to the instance: * Airfoil.CL_function(alpha, Re, mach) * Airfoil.CD_function(alpha, Re, mach) * Airfoil.CM_function(alpha, Re, mach) Where alpha is in degrees. Warning: In-place operation! Modifies this Airfoil object by setting Airfoil.CL_function, etc. to the new polars. :param alphas: The range of alphas to sample from XFoil at. Given in degrees. :param Res: The range of Reynolds numbers to sample from XFoil at. Dimensionless. :param cache_filename: A path-like filename (ideally a "*.json" file) that can be used to cache the XFoil results, making it much faster to regenerate the results. * If the file does not exist, XFoil will be run, and a cache file will be created. * If the file does exist, XFoil will not be run, and the cache file will be read instead. :param xfoil_kwargs: Keyword arguments to pass into the AeroSandbox XFoil module. See the aerosandbox.XFoil constructor for options. :param unstructured_interpolated_model_kwargs: Keyword arguments to pass into the UnstructuredInterpolatedModels that contain the polars themselves. See the aerosandbox.UnstructuredInterpolatedModel constructor for options. :param include_compressibility_effects: Includes compressibility effects in the polars, such as wave drag, mach tuck, CL effects across normal shocks. Note that accuracy here is dubious in the transonic regime and above - you should really specify your own CL/CD/CM models Returns: None (in-place), adds the following functions to the instance: * Airfoil.CL_function(alpha, Re, mach) * Airfoil.CD_function(alpha, Re, mach) * Airfoil.CM_function(alpha, Re, mach) .. py:method:: get_aero_from_neuralfoil(alpha, Re, mach = 0.0, n_crit = 9.0, xtr_upper = 1.0, xtr_lower = 1.0, model_size = 'large', control_surfaces = None, include_360_deg_effects = True) .. py:method:: plot_polars(alphas = np.linspace(-20, 20, 500), Res = 10**np.arange(3, 9), mach = 0.0, show = True, Re_colors=None) .. py:method:: local_camber(x_over_c = np.linspace(0, 1, 101)) Returns the local camber of the airfoil at a given point or points. :param x_over_c: The x/c locations to calculate the camber at [1D array, more generally, an iterable of floats] :returns: Local camber of the airfoil (y/c) [1D array]. .. py:method:: local_thickness(x_over_c = np.linspace(0, 1, 101)) Returns the local thickness of the airfoil at a given point or points. :param x_over_c: The x/c locations to calculate the thickness at [1D array, more generally, an iterable of floats] :returns: Local thickness of the airfoil (y/c) [1D array]. .. py:method:: max_camber(x_over_c_sample = np.linspace(0, 1, 101)) Returns the maximum camber of the airfoil. :param x_over_c_sample: Where should the airfoil be sampled to determine the max camber? Returns: The maximum thickness, as a fraction of chord. .. py:method:: max_thickness(x_over_c_sample = np.linspace(0, 1, 101)) Returns the maximum thickness of the airfoil. :param x_over_c_sample: Where should the airfoil be sampled to determine the max thickness? Returns: The maximum thickness, as a fraction of chord. .. py:method:: draw(draw_mcl=False, draw_markers=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" :param show: Should we show the plot? [boolean] Returns: None .. py:method:: LE_index() Returns the index of the leading edge point in the airfoil coordinates. .. py:method:: lower_coordinates() Returns an Nx2 ndarray of [x, y] coordinates that describe the lower surface of the airfoil. Order is from the leading edge to the trailing edge. Includes the leading edge point; be careful about duplicates if using this method in conjunction with Airfoil.upper_coordinates(). .. py:method:: upper_coordinates() Returns an Nx2 ndarray of [x, y] coordinates that describe the upper surface of the airfoil. Order is from the trailing edge to the leading edge. Includes the leading edge point; be careful about duplicates if using this method in conjunction with Airfoil.lower_coordinates(). .. py:method:: LE_radius(softness = 1e-06) .. py:method:: TE_thickness() Returns the thickness of the trailing edge of the airfoil. .. py:method:: TE_angle() Returns the trailing edge angle of the airfoil, in degrees. .. py:method:: repanel(n_points_per_side = 100, spacing_function_per_side=np.cosspace) Returns a repaneled copy of the airfoil with cosine-spaced coordinates on the upper and lower surfaces. :param n_points_per_side: Number of points per side (upper and lower) of the airfoil [int] Notes: The number of points defining the final airfoil will be `n_points_per_side * 2 - 1`, since one point (the leading edge point) is shared by both the upper and lower surfaces. :param spacing_function_per_side: Determines how to space the points on each side of the airfoil. Can be `np.linspace` or `np.cosspace`, or any other function of the call signature `f(a, b, n)` that returns a spaced array of `n` points between `a` and `b`. [function] Returns: A copy of the airfoil with the new coordinates. .. py:method:: normalize(return_dict = False) Returns a copy of the Airfoil with a new set of `coordinates`, such that: - The leading edge (LE) is at (0, 0) - The trailing edge (TE) is at (1, 0) - The chord length is equal to 1 The trailing-edge (TE) point is defined as the midpoint of the line segment connecting the first and last coordinate points (upper and lower surface TE points, respectively). The TE point is not necessarily one of the original points in the airfoil coordinates (`Airfoil.coordinates`); in general, it will not be one of the points if the TE thickness is nonzero. The leading-edge (LE) point is defined as the coordinate point with the largest Euclidian distance from the trailing edge. (In other words, if you were to center a circle on the trailing edge and progressively grow it, what's the last coordinate point that it would intersect?) The LE point is always one of the original points in the airfoil coordinates. The chord is defined as the Euclidian distance between the LE and TE points. Coordinate modifications to achieve the constraints described above (LE @ origin, TE at (1, 0), and chord of 1) are done by means of a translation and rotation. :param return_dict: Determines the output type of the function. - If `False` (default), returns a copy of the Airfoil with the new coordinates. - If `True`, returns a dictionary with keys: - "airfoil": a copy of the Airfoil with the new coordinates - "x_translation": the amount by which the airfoil's LE was translated in the x-direction - "y_translation": the amount by which the airfoil's LE was translated in the y-direction - "scale_factor": the amount by which the airfoil was scaled (if >1, the airfoil had to get bigger) - "rotation_angle": the angle (in degrees) by which the airfoil was rotated about the LE. Sign convention is that positive angles rotate the airfoil counter-clockwise. All of thes values represent the "required change", e.g.: - "x_translation" is the amount by which the airfoil's LE had to be translated in the x-direction to get it to the origin. - "rotation_angle" is the angle (in degrees) by which the airfoil had to be rotated (CCW). Returns: Depending on the value of `return_dict`, either: - A copy of the airfoil with the new coordinates (default), or - A dictionary with keys "airfoil", "x_translation", "y_translation", "scale_factor", and "rotation_angle". documentation for `return_tuple` for more information. .. py:method:: add_control_surface(deflection = 0.0, hinge_point_x = 0.75, modify_coordinates = True, modify_polars = True) Returns a version of the airfoil with a trailing-edge control surface added at a given point. Implicitly repanels the airfoil as part of this operation. :param deflection: Deflection angle [degrees]. Downwards-positive. :param hinge_point_x: Chordwise location of the hinge, as a fraction of chord (x/c) [float] Returns: an Airfoil object with the new control deflection. .. py:method:: set_TE_thickness(thickness = 0.0) Creates a modified copy of the Airfoil that has a specified trailing-edge thickness. Note that the trailing-edge thickness is given nondimensionally (e.g., as a fraction of chord). :param thickness: The target trailing-edge thickness, given nondimensionally (e.g., as a fraction of chord). Returns: The modified airfoil. .. py:method:: scale(scale_x = 1.0, scale_y = 1.0) Scales an Airfoil about the origin. :param scale_x: Amount to scale in the x-direction. :param scale_y: Amount to scale in the y-direction. Scaling by a negative y-value will result in coordinates being re-ordered such that the order of the coordinates is still correct (i.e., starts from the upper-surface trailing edge, continues along the upper surface to the nose, then continues along the lower surface to the trailing edge). Returns: A copy of the Airfoil with appropriate scaling applied. .. py:method:: translate(translate_x = 0.0, translate_y = 0.0) Translates an Airfoil by a given amount. :param translate_x: Amount to translate in the x-direction :param translate_y: Amount to translate in the y-direction Returns: The translated Airfoil. .. py:method:: rotate(angle, x_center = 0.0, y_center = 0.0) Rotates the airfoil clockwise by the specified amount, in radians. Rotates about the point (x_center, y_center), which is (0, 0) by default. :param angle: Angle to rotate, counterclockwise, in radians. :param x_center: The x-coordinate of the center of rotation. :param y_center: The y-coordinate of the center of rotation. Returns: The rotated Airfoil. .. py:method:: blend_with_another_airfoil(airfoil, blend_fraction = 0.5, n_points_per_side = 100) Blends this airfoil with another airfoil. Merges both the coordinates and the aerodynamic functions. :param airfoil: The other airfoil to blend with. :param blend_fraction: The fraction of the other airfoil to use when blending. Defaults to 0.5 (50%). * A blend fraction of 0 will return an identical airfoil to this one (self). * A blend fraction of 1 will return an identical airfoil to the other one (`airfoil` parameter). :param n_points_per_side: The number of points per side to use when blending the coordinates of the two airfoils. Returns: A new airfoil that is a blend of this airfoil and another one. .. py:method:: write_dat(filepath = None, include_name = True) Writes a .dat file corresponding to this airfoil to a filepath. :param filepath: filepath (including the filename and .dat extension) [string] If None, this function returns the .dat file as a string. :param include_name: Should the name be included in the .dat file? (In a standard *.dat file, it usually is.) Returns: None .. py:class:: KulfanAirfoil(name = 'Untitled', lower_weights = None, upper_weights = None, leading_edge_weight = 0.0, TE_thickness = 0.0, N1 = 0.5, N2 = 1.0) Bases: :py:obj:`aerosandbox.geometry.airfoil.airfoil.Airfoil` An airfoil. See constructor docstring for usage details. .. py:attribute:: name :value: 'Untitled' .. py:attribute:: lower_weights :value: None .. py:attribute:: upper_weights :value: None .. py:attribute:: leading_edge_weight :value: 0.0 .. py:attribute:: TE_thickness :value: 0.0 Returns the thickness of the trailing edge of the airfoil. .. py:attribute:: N1 :value: 0.5 .. py:attribute:: N2 :value: 1.0 .. py:method:: __repr__() .. py:property:: kulfan_parameters .. py:property:: coordinates :type: aerosandbox.numpy.ndarray .. py:method:: to_airfoil(n_coordinates_per_side=200, spacing_function_per_side=np.cosspace) .. py:method:: repanel(n_points_per_side = 100, spacing_function_per_side=np.cosspace) Returns a repaneled copy of the airfoil with cosine-spaced coordinates on the upper and lower surfaces. :param n_points_per_side: Number of points per side (upper and lower) of the airfoil [int] Notes: The number of points defining the final airfoil will be `n_points_per_side * 2 - 1`, since one point (the leading edge point) is shared by both the upper and lower surfaces. :param spacing_function_per_side: Determines how to space the points on each side of the airfoil. Can be `np.linspace` or `np.cosspace`, or any other function of the call signature `f(a, b, n)` that returns a spaced array of `n` points between `a` and `b`. [function] Returns: A copy of the airfoil with the new coordinates. .. py:method:: normalize(return_dict = False) Returns a copy of the Airfoil with a new set of `coordinates`, such that: - The leading edge (LE) is at (0, 0) - The trailing edge (TE) is at (1, 0) - The chord length is equal to 1 The trailing-edge (TE) point is defined as the midpoint of the line segment connecting the first and last coordinate points (upper and lower surface TE points, respectively). The TE point is not necessarily one of the original points in the airfoil coordinates (`Airfoil.coordinates`); in general, it will not be one of the points if the TE thickness is nonzero. The leading-edge (LE) point is defined as the coordinate point with the largest Euclidian distance from the trailing edge. (In other words, if you were to center a circle on the trailing edge and progressively grow it, what's the last coordinate point that it would intersect?) The LE point is always one of the original points in the airfoil coordinates. The chord is defined as the Euclidian distance between the LE and TE points. Coordinate modifications to achieve the constraints described above (LE @ origin, TE at (1, 0), and chord of 1) are done by means of a translation and rotation. :param return_dict: Determines the output type of the function. - If `False` (default), returns a copy of the Airfoil with the new coordinates. - If `True`, returns a dictionary with keys: - "airfoil": a copy of the Airfoil with the new coordinates - "x_translation": the amount by which the airfoil's LE was translated in the x-direction - "y_translation": the amount by which the airfoil's LE was translated in the y-direction - "scale_factor": the amount by which the airfoil was scaled (if >1, the airfoil had to get bigger) - "rotation_angle": the angle (in degrees) by which the airfoil was rotated about the LE. Sign convention is that positive angles rotate the airfoil counter-clockwise. All of thes values represent the "required change", e.g.: - "x_translation" is the amount by which the airfoil's LE had to be translated in the x-direction to get it to the origin. - "rotation_angle" is the angle (in degrees) by which the airfoil had to be rotated (CCW). Returns: Depending on the value of `return_dict`, either: - A copy of the airfoil with the new coordinates (default), or - A dictionary with keys "airfoil", "x_translation", "y_translation", "scale_factor", and "rotation_angle". documentation for `return_tuple` for more information. .. py:method:: draw(*args, draw_markers=False, **kwargs) 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" :param show: Should we show the plot? [boolean] Returns: None .. py:method:: get_aero_from_neuralfoil(alpha, Re, mach = 0.0, n_crit = 9.0, xtr_upper = 1.0, xtr_lower = 1.0, model_size = 'large', control_surfaces = None, include_360_deg_effects = True) .. py:method:: upper_coordinates(x_over_c = np.linspace(1, 0, 101)) Returns an Nx2 ndarray of [x, y] coordinates that describe the upper surface of the airfoil. Order is from the trailing edge to the leading edge. Includes the leading edge point; be careful about duplicates if using this method in conjunction with Airfoil.lower_coordinates(). .. py:method:: lower_coordinates(x_over_c = np.linspace(0, 1, 101)) Returns an Nx2 ndarray of [x, y] coordinates that describe the lower surface of the airfoil. Order is from the leading edge to the trailing edge. Includes the leading edge point; be careful about duplicates if using this method in conjunction with Airfoil.upper_coordinates(). .. py:method:: local_camber(x_over_c = np.linspace(0, 1, 101)) Returns the local camber of the airfoil at a given point or points. :param x_over_c: The x/c locations to calculate the camber at [1D array, more generally, an iterable of floats] :returns: Local camber of the airfoil (y/c) [1D array]. .. py:method:: local_thickness(x_over_c = np.linspace(0, 1, 101)) Returns the local thickness of the airfoil at a given point or points. :param x_over_c: The x/c locations to calculate the thickness at [1D array, more generally, an iterable of floats] :returns: Local thickness of the airfoil (y/c) [1D array]. .. py:method:: LE_radius(relative_softness = 0.03) .. py:method:: TE_angle() Returns the trailing edge angle of the airfoil, in degrees. .. py:method:: area() Returns the area of the polygon. .. py:method:: set_TE_thickness(thickness = 0.0) Creates a modified copy of the KulfanAirfoil that has a specified trailing-edge thickness. Note that the trailing-edge thickness is given nondimensionally (e.g., as a fraction of chord). :param thickness: The target trailing-edge thickness, given nondimensionally (e.g., as a fraction of chord). Returns: The modified KulfanAirfoil. .. py:method:: scale(scale_x = 1.0, scale_y = 1.0) Scales a KulfanAirfoil about the origin. :param scale_x: Amount to scale in the x-direction. Note: not supported by KulfanAirfoil due to inherent limitations of parameterization; only given here so that argument symmetry to Airfoil.scale() is retained. Raises a ValueError if modified, along with instructions to use `Airfoil` if needed. :param scale_y: Amount to scale in the y-direction. Scaling by a negative y-value will result in `lower_weights` and `upper_weights` being flipped as appropriate. Returns: A copy of the KulfanAirfoil with appropriate scaling applied. .. py:method:: blend_with_another_airfoil(airfoil, blend_fraction = 0.5) Blends this airfoil with another airfoil. Merges both the coordinates and the aerodynamic functions. :param airfoil: The other airfoil to blend with. :param blend_fraction: The fraction of the other airfoil to use when blending. Defaults to 0.5 (50%). * A blend fraction of 0 will return an identical airfoil to this one (self). * A blend fraction of 1 will return an identical airfoil to the other one (`airfoil` parameter). :param n_points_per_side: The number of points per side to use when blending the coordinates of the two airfoils. Returns: A new airfoil that is a blend of this airfoil and another one.