pymead.core.airfoil.Airfoil#

class Airfoil(leading_edge: Point, trailing_edge: Point, upper_surf_end: Point | None = None, lower_surf_end: Point | None = None, name: str | None = None)[source]#

Bases: PymeadObj

This is a primary class in pymead, which defines an airfoil by a leading edge, a trailing edge, and optionally an upper-surface endpoint and a lower-surface endpoint in the case of a blunt airfoil. For the purposes of single-airfoil evaluation method (such as XFOIL or the built-in panel code), instances of this class are sufficient. For multi-element airfoil evaluation (such as MSES), instances of this class are stored in the container class, pymead.core.mea.MEA, which adds some additional and necessary functionality. Coordinates are stored in the coords attribute and can be updated using the update_coords method.

__init__(leading_edge: Point, trailing_edge: Point, upper_surf_end: Point | None = None, lower_surf_end: Point | None = None, name: str | None = None)[source]#
Parameters:
  • leading_edge (Point) – The airfoil’s leading edge point (usually at \((0,0)\) for a typical single airfoil configuration)

  • trailing_edge (Point) – The airfoil’s trailing edge point (usually at \((1,0)\) for a typical single airfoil configuration)

  • upper_surf_end (Point) – Optional specification of the upper surface endpoint (the first point in the Selig file format). If this point is not specified, the trailing edge point is used instead. Default: None

  • lower_surf_end (Point) – Optional specification of the lower surface endpoint (the last point in the Selig file format). If this point is not specified, the trailing edge point is used instead. Default: None

  • name (str) – Optional name for the airfoil. If None, a default name is used. Default: None

Methods

add_relative_points(points)

Marks a list of control points tied to an airfoil as airfoil-relative

check_closed()

This method checks if the airfoil is composed of a closed set of curves.

check_self_intersection()

Determines whether the airfoil intersects itself using the is_simple() function of the shapely library.

compute_area([airfoil_frame_relative])

compute_camber_at_points(x_over_c[, ...])

Calculates the thickness (t/c) at a set of x-locations (x/c)

compute_min_radius([chord_relative])

Computes the minimum radius of curvature for the airfoil.

compute_thickness([airfoil_frame_relative, ...])

Calculates the thickness distribution and maximum thickness of the airfoil.

compute_thickness_at_points(x_arr[, ...])

Calculates the thickness (t/c) at a set of x-locations (x/c)

contains_line_string(points[, ...])

Whether a connected string of points is contained inside the airfoil.

contains_point(point[, airfoil_frame_relative])

Determines whether a point is contained inside the airfoil

downsample(max_airfoil_points[, curvature_exp])

Downsamples the airfoil coordinates based on a curvature exponent.

get_chord_relative_coords([coords, ...])

Gets the chord-relative values of the airfoil coordinates.

get_coords_selig_format([...])

Gets the coordinates of the airfoil in the Selig file format (coordinate array of size \(N \times 2\), where \(N\) is the number of airfoil coordinates, and the columns represent \(x\) and \(y\)).

get_dict_rep()

Gets a dictionary representation of the pymead object.

get_scaled_coords([coords, ...])

Gets the chord-relative values of the airfoil coordinates.

measure_alpha()

Measures the angle of attack in radians

measure_chord()

Measures the chord length

plot([ax, show, save_file])

Plots the airfoil to a matplotlib figure.

remove_relative_points(points)

Removes the relative assignment of a list of points in an airfoil.

save_coords_selig_format(file_name)

Saves this airfoil's coords in Selig file format: \(x\)- and \(y\)-columns in counter-clockwise order, starting at the upper surface trailing edge and ending at the lower surface trailing edge.

update_coords()

Updates the coordinates of the airfoil, and passes this data to the canvas item if it exists.

update_relative_points(...)

Updates the airfoil-coordinate-system-relative points that are part of this airfoil.

visualize_contains_line_string(points[, ...])

Gets the data necessary to visualize the containment of an input line string inside the airfoil.

visualize_max_thickness()

Gets airfoil data necessary to visualize the maximum thickness.

visualize_min_radius()

Gets airfoil data necessary to visualize the minimum radius of curvature.

visualize_thickness_at_points(x_arr, ...[, ...])

Computes the thickness at a set of x-locations with additional data for visualization purposes.

Attributes

add_relative_points(points: list[Point])[source]#

Marks a list of control points tied to an airfoil as airfoil-relative

Parameters:

points (list[Point]) – List of airfoil points to mark as airfoil-relative

check_closed() None[source]#

This method checks if the airfoil is composed of a closed set of curves. If the airfoil is closed, the method passes, but if the airfoil is not closed, a ClosureError is raised. This method also determines which curves need to be evaluated in the opposite parametric direction of their point sequences.

Returns:

Nothing is returned if the closure check passes, but an error is raised pre-return if the closure check does not pass

Return type:

None

check_self_intersection() bool[source]#

Determines whether the airfoil intersects itself using the is_simple() function of the shapely library.

Returns:

Describes whether the airfoil intersects itself

Return type:

bool

compute_area(airfoil_frame_relative: bool = False) float[source]#

Computes the area of the airfoil as the area of a many-sided polygon enclosed by the airfoil coordinates using the shapely library.

Parameters:

airfoil_frame_relative (bool) – Whether to compute the area in the airfoil-relative frame. If True, the area based on a chord-relative scaling will be returned. Default: False

Returns:

The area of the airfoil

Return type:

float

compute_camber_at_points(x_over_c: ndarray, airfoil_frame_relative: bool = False, start_y_over_c: float = -1.0, end_y_over_c: float = 1.0) ndarray[source]#

Calculates the thickness (t/c) at a set of x-locations (x/c)

Parameters:
  • x_over_c (float or list or np.ndarray) – The \(x/c\) locations at which to evaluate the camber

  • airfoil_frame_relative (bool) – Whether to compute the area in the airfoil-relative frame. If True, the thickness based on a chord-relative scaling will be returned. Default: False

  • start_y_over_c (float) – The \(y/c\) location to draw the first point in a line whose intersection with the airfoil is checked. May need to decrease this value for unusually thick airfoils

  • end_y_over_c (float) – The \(y/c\) location to draw the last point in a line whose intersection with the airfoil is checked. May need to increase this value for unusually thick airfoils

Returns:

An array of thickness (\(t/c\)) values corresponding to the input \(x/c\) values

Return type:

np.ndarray

compute_min_radius(chord_relative: bool = False) float[source]#

Computes the minimum radius of curvature for the airfoil.

Parameters:

chord_relative (bool) – Whether to scale the output value by the chord length of the current airfoil. Default: False.

Returns:

The minimum radius of curvature

Return type:

float

compute_thickness(airfoil_frame_relative: bool = False, n_lines: int = 201) dict[str, float][source]#

Calculates the thickness distribution and maximum thickness of the airfoil.

Parameters:
  • airfoil_frame_relative (bool) – Whether to compute the thickness distribution in the airfoil-relative frame. If True, the thickness based on a chord-relative scaling will be returned. Default: False

  • n_lines (int) – Describes the number of lines evenly spaced along the chordline produced to determine the thickness distribution. Default: 201

Returns:

The list of \(x\)-values used for the thickness distribution calculation, the thickness distribution, the maximum value of the thickness distribution, and, if return_max_thickness_location=True, the \(x/c\)-location of the maximum thickness value.

Return type:

dict

compute_thickness_at_points(x_arr: ndarray, airfoil_frame_relative: bool = False, vertical_check: bool = False) ndarray[source]#

Calculates the thickness (t/c) at a set of x-locations (x/c)

Warning

If the airfoil’s angle of attack is far from zero and vertical_check==True, this method may yield undesirable results.

Parameters:
  • x_arr (float or list or np.ndarray) – The \(x\) (or \(x/c\) if airfoil_frame_relative==True) locations at which to evaluate the thickness

  • airfoil_frame_relative (bool) – Whether to compute the area in the airfoil-relative frame. If True, the thickness based on a chord-relative scaling will be returned. Default: False

  • vertical_check (bool) – Whether to compute the thickness vertically from the chordline (rather than perpendicular). This value is ignored unless``airfoil_frame_relative==False``.

Returns:

An array of thickness (\(t/c\)) values corresponding to the input \(x/c\) values

Return type:

np.ndarray

contains_line_string(points: ndarray | list, airfoil_frame_relative: bool = False, rotate_with_airfoil: bool = True, translate_with_airfoil: bool = True, scale_with_airfoil: bool = True) bool[source]#

Whether a connected string of points is contained inside the airfoil.

Parameters:
  • points (np.ndarray or list) – Should be a 2-D array or list of the form [[<x_val_1>, <y_val_1>], [<x_val_2>, <y_val_2>], ...]

  • airfoil_frame_relative (bool) – Whether to run the enclosure test with the line string defined in the airfoil-relative frame. Default: False

  • rotate_with_airfoil (bool) – Whether to rotate the line string by the opposite of the airfoil’s angle of attack before running the test. Default: True

  • translate_with_airfoil (bool) – Whether to translate the line string by a displacement equal to the airfoil’s leading edge location before running the test. Default: True

  • scale_with_airfoil (bool) – Whether to scale the line string by the airfoil’s chord before running the test. Default: True

Returns:

Whether the line string is contained inside the airfoil

Return type:

bool

contains_point(point: ndarray, airfoil_frame_relative: bool = False) bool[source]#

Determines whether a point is contained inside the airfoil

Parameters:
  • point (np.ndarray or list) – The point to test. Should be either a 1-D ndarray of the format array([<x_val>,<y_val>]) or a list of the format [<x_val>,<y_val>]

  • airfoil_frame_relative (bool) – Whether to check for point containment in the airfoil-relative frame. If True, the airfoil will be scaled by the chord, de-rotated, and the leading edge moved to \((0,0)\) before checking if the point is inside the airfoil. Default: False

Returns:

Whether the point is contained inside the airfoil

Return type:

bool

downsample(max_airfoil_points: int, curvature_exp: float = 2.0) list[ndarray][source]#

Downsamples the airfoil coordinates based on a curvature exponent. This method works by evaluating each Bézier curve using a set number of points (150) and then calculating \(\mathbf{R_e} = \mathbf{R}^{1/e_c}\), where \(\mathbf{R}\) is the radius of curvature vector and \(e_c\) is the curvature exponent (an input to this method). Then, \(\mathbf{R_e}\) is normalized by its maximum value and concatenated to a single array for all curves in a given airfoil. Finally, max_airfoil_points are chosen from this array to create a new set of parameter vectors for the airfoil.

Parameters:
  • max_airfoil_points (int) – Maximum number of points in the airfoil (the actual number in the final airfoil may be slightly less)

  • curvature_exp (float) – Curvature exponent used to scale the radius of curvature. Values close to 0 place high emphasis on curvature, while values close to \(\infty\) place low emphasis on curvature (creating nearly uniform spacing)

Returns:

List of parameter vectors (one for each Bézier curve)

Return type:

list[np.ndarray]

get_chord_relative_coords(coords: ndarray | None = None, max_airfoil_points: int | None = None, curvature_exp: float = 2.0) ndarray[source]#

Gets the chord-relative values of the airfoil coordinates. The airfoil is transformed such that the leading edge is at \((0,0)\) and the trailing edge is at \((1,0)\).

Parameters:
  • coords (np.ndarray or None) – Optional Selig format airfoil coordinates (only specified if computational speed is important). If the coordinates are not specified, they are calculated.

  • max_airfoil_points (int) – Optional value specifying the maximum number of airfoil points. If this value is left as None, no downsampling will be performed. Default: None

  • curvature_exp (float) – Optional value specifying the curvature exponent used in the downsample method. If max_airfoil_points is left as None, this value will be ignored. Default: 2

Returns:

An \(N imes 2\) array of transformed airfoil coordinates

Return type:

np.ndarray

get_coords_selig_format(max_airfoil_points: int | None = None, curvature_exp: float = 2.0, n_points_per_curve: int = 150) ndarray[source]#

Gets the coordinates of the airfoil in the Selig file format (coordinate array of size \(N \times 2\), where \(N\) is the number of airfoil coordinates, and the columns represent \(x\) and \(y\)). The order of the points is counter-clockwise, with the start and end at the upper surface trailing edge point and lower surface trailing edge point, respectively.

Parameters:
  • max_airfoil_points (int) – Optional value specifying the maximum number of airfoil points. If this value is left as None, no downsampling will be performed. Default: None

  • curvature_exp (float) – Optional value specifying the curvature exponent used in the downsample method. If max_airfoil_points is left as None, this value will be ignored. Default: 2

  • n_points_per_curve (int) – Number of points to evaluate for each Bézier curve. Default: 150

Returns:

Coordinate array (size \(N \times 2\))

Return type:

np.ndarray

get_dict_rep() dict[source]#

Gets a dictionary representation of the pymead object. In general, this dictionary should consist of only the required arguments for object instantiation. For example, the dictionary representation of a point looks something like this: {"x": 0.3, "y": 0.5}. If the argument requires a reference to a PymeadObj rather than a string or float value, the name() method should be the value that is stored. For an example, see the overridden value of this method in pymead.core.airfoil.Airfoil. All subclasses of PymeadObj must implement this method, since it is the way pymead objects are stored in saved instances of a GeometryCollection (.jmea files).

get_scaled_coords(coords: ndarray | None = None, max_airfoil_points: int | None = None, curvature_exp: float = 2.0) ndarray[source]#

Gets the chord-relative values of the airfoil coordinates. The airfoil is transformed such that the leading edge is at \((0,0)\) and the trailing edge is at \((1,0)\).

Parameters:
  • coords (np.ndarray or None) – Optional Selig format airfoil coordinates (only specified if computational speed is important). If the coordinates are not specified, they are calculated.

  • max_airfoil_points (int) – Optional value specifying the maximum number of airfoil points. If this value is left as None, no downsampling will be performed. Default: None

  • curvature_exp (float) – Optional value specifying the curvature exponent used in the downsample method. If max_airfoil_points is left as None, this value will be ignored. Default: 2

Returns:

An \(N \times 2\) array of transformed airfoil coordinates

Return type:

np.ndarray

measure_alpha() float[source]#

Measures the angle of attack in radians

Returns:

The airfoil’s current angle of attack

Return type:

float

measure_chord() float[source]#

Measures the chord length

Returns:

The airfoil’s current chord length

Return type:

float

plot(ax: Axes | None = None, show: bool = True, save_file: str | None = None, **plt_kwargs)[source]#

Plots the airfoil to a matplotlib figure.

Parameters:
  • ax (plt.Axes or None) – Matplotlib Axes object on which the airfoil will be plotted. If specified, this method will only plot the airfoil curves and ignore the subsequent functionality. Default: None

  • show (bool) – Whether to immediately show the airfoil plot. Ignored if ax is not None. Default: True

  • save_file (str or None) – Name of the file to save. If None, the airfoil image will not be saved to file. Ignored if ax is not None. Default: None

  • plt_kwargs – Additional keyword arguments to pass to matplotlib.pyplot.plot

remove_relative_points(points: list[Point])[source]#

Removes the relative assignment of a list of points in an airfoil.

Parameters:

points (list[Point]) – List of airfoil points to mark as non-airfoil-relative

save_coords_selig_format(file_name: str) None[source]#

Saves this airfoil’s coords in Selig file format: \(x\)- and \(y\)-columns in counter-clockwise order, starting at the upper surface trailing edge and ending at the lower surface trailing edge. File saves as a text file (usually a .txt or .dat file extension).

Parameters:

file_name (str) – Full path to the coordinate file save location

update_coords()[source]#

Updates the coordinates of the airfoil, and passes this data to the canvas item if it exists.

update_relative_points(original_geo_col_point_values: dict[str, ndarray])[source]#

Updates the airfoil-coordinate-system-relative points that are part of this airfoil.

Parameters:

original_geo_col_point_values (dict[str, np.ndarray]) – Dictionary of all point values in the geometry collection, where each key in the dictionary corresponds to the name of the point and each value is a numpy array of the form np.array([x, y])

visualize_contains_line_string(points: ndarray | list, airfoil_frame_relative: bool = False, rotate_with_airfoil: bool = True, translate_with_airfoil: bool = True, scale_with_airfoil: bool = True) dict[source]#

Gets the data necessary to visualize the containment of an input line string inside the airfoil.

Parameters:
  • points (np.ndarray or list) – Array of points of the form [[x1, y1], [x2, y2], ...] corresponding to the sequence of points defining a line string that must be contained inside the airfoil for the constraint test to pass

  • airfoil_frame_relative (bool) – Whether the line string is defined in the airfoil-relative frame. Default: False

  • rotate_with_airfoil (bool) – Whether the line string should be rotated by the angle corresponding to the opposite of the airfoil angle of attack before performing the line string containment check. Default: True

  • translate_with_airfoil (bool) – Whether the line string should be translated by (x_LE, y_LE), which is the point corresponding to the airfoil leading edge, before performing the line string containment check. Default: True

  • scale_with_airfoil (bool) – Whether the line string should be scaled by the airfoil chord length before performing the line string containment check. Default: True

Returns:

Data with the following fields:

  • "pass": a bool describing whether the line string containment check passed

  • "xy_polyline": a numpy array of the form np.array([[x1, y1], [x2, y2], ...]) matching the points input to the method

Return type:

dict

visualize_max_thickness() dict[source]#

Gets airfoil data necessary to visualize the maximum thickness.

Returns:

Data for maximum thickness with the following fields:

  • "xy": numpy array of the form np.array([[x1, y1], [x2, y2]]) where x1 and y1 are the \(x\)- and \(y\)-locations of the starting point of the maximum thickness line, and x2 and y2 are the \(x\)- and \(y\)-locations of the maximum thickness line

  • "t_max": dimensional maximum thickness value

  • "t/c_max": non-dimensional maximum thickness value (maximum thickness divided by the chord length)

  • "x/c": non-dimensional \(x\)-location corresponding where the maximum thickness was found

Return type:

dict

visualize_min_radius() dict[source]#

Gets airfoil data necessary to visualize the minimum radius of curvature.

Returns:

Data for minimum radius of curvature with the following fields:

  • "R_abs_min": absolute value of the minimum radius of curvature

  • "R_abs_min_over_c": absolute value of the minimum radius of curvature normalized by the chord length

  • "xy": Two-element 1-D numpy array containing the \(x\)- and \(y\)-values of the point on the airfoil surface corresponding to the minimum radius of curvature

Return type:

dict

visualize_thickness_at_points(x_arr: ndarray, thickness_constraints, airfoil_frame_relative: bool = False, vertical_check: bool = False) dict[source]#

Computes the thickness at a set of x-locations with additional data for visualization purposes.

Warning

If the airfoil’s angle of attack is far from zero and vertical_check==True, this method may yield undesirable results.

Parameters:
  • x_arr (float or list or np.ndarray) – The \(x\) (or \(x/c\) if airfoil_frame_relative==True) locations at which to evaluate the thickness

  • airfoil_frame_relative (bool) – Whether to compute the area in the airfoil-relative frame. If True, the thickness based on a chord-relative scaling will be returned. Default: False

  • vertical_check (bool) – Whether to compute the thickness vertically from the chordline (rather than perpendicular). This value is ignored unless``airfoil_frame_relative==False``.

Returns:

Data with the following fields:

  • "slices": List of slices for visualization of the form [np.array([[x1, y1], [x2, y2]]), np.array([[x3, y3], [x4, y4]]), ...], where each pair of points corresponds to a line of length equal to the value of a thickness constraint at an angle corresponding to the thickness evaluation direction (either chord-normal or vertical) and centered about the midpoint between the intersections of a line passing through that \(x\)-value on the airfoil

  • "warning_x_vals": \(x\)-locations of the input set where an individual thickness test does not

    pass

Return type:

dict