aerosandbox.geometry.wing ========================= .. py:module:: aerosandbox.geometry.wing Attributes ---------- .. autoapisummary:: aerosandbox.geometry.wing.wing Classes ------- .. autoapisummary:: aerosandbox.geometry.wing.Wing aerosandbox.geometry.wing.WingXSec aerosandbox.geometry.wing.ControlSurface Module Contents --------------- .. py:class:: Wing(name = None, xsecs = None, symmetric = False, color = None, analysis_specific_options = None, **kwargs) Bases: :py:obj:`aerosandbox.common.AeroSandboxObject` Definition for a Wing. Anatomy of a Wing: A wing consists chiefly of a collection of cross-sections, or "xsecs". A cross-section is a 2D "slice" of a wing. These can be accessed with `Wing.xsecs`, which gives a list of xsecs in the Wing. Each xsec is a WingXSec object, a class that is defined separately. You may also see references to wing "sections", which are different than cross-sections (xsecs)! Sections are the portions of the wing that are in between xsecs. In other words, a wing with N cross-sections (xsecs, WingXSec objects) will always have N-1 sections. Sections are never explicitly defined, since you can get all needed information by lofting from the adjacent cross-sections. For example, section 0 (the first one) is a loft between cross-sections 0 and 1. Wings are lofted linearly between cross-sections. If the wing is symmetric across the XZ plane, just define the right half and supply `symmetric=True` in the constructor. If the wing is not symmetric across the XZ plane (e.g., a single vertical stabilizer), just define the wing. .. py:attribute:: name :value: None .. py:attribute:: xsecs :value: None .. py:attribute:: symmetric :value: False .. py:attribute:: color :value: None .. py:attribute:: analysis_specific_options :value: None .. py:method:: __repr__() .. py:method:: translate(xyz) Translates the entire Wing by a certain amount. :param xyz: Returns: The new wing object. .. py:method:: span(type = 'yz', include_centerline_distance=False, _sectional = False) Computes the span, with options for various ways of measuring this (see `type` argument). If the wing is symmetric, both left/right sides are included in order to obtain the full span. In the case where the root cross-section is not coincident with the center plane (e.g., XZ plane), this function's behavior depends on the `include_centerline_distance` argument. :param type: One of the following options, as a string: * "xyz": First, computes the quarter-chord point of each WingXSec. Then, connects these with straight lines. Then, adds up the lengths of these lines. * "xy" or "top": Same as "xyz", except it projects each line segment onto the XY plane before adding up the lengths. * "yz" (default) or "front": Same as "xyz", except it projects each line segment onto the YZ plane (i.e., front view) before adding up the lengths. * "xz" or "side": Same as "xyz", except it projects each line segment onto the XZ plane before adding up the lengths. Rarely needed. * "x": Same as "xyz", except it only counts the x-components of each line segment when adding up the lengths. * "y": Same as "xyz", except it only counts the y-components of each line segment when adding up the lengths. * "z": Same as "xyz", except it only counts the z-components of each line segment when adding up the lengths. :param include_centerline_distance: A boolean flag that tells the function what to do if a wing's root is not :param coincident with the centerline plane: * If True, we first figure out which WingXSec has its quarter-chord point closest to the centerline plane (i.e., XZ plane). Then, we compute the distance from that quarter-chord point directly to the centerline plane (along Y). We then add that distance to the span calculation. In other words, the fictitious span connecting the left and right root cross-sections is included. * If False, this distance is ignored. In other words, the fictitious span connecting the left and right root cross-sections is not included. This is the default behavior. Note: For computation, either the root WingXSec (i.e., index=0) or the tip WingXSec (i.e., index=-1) is used, whichever is closer to the centerline plane. This will almost-always be the root WingXSec, but some weird edge cases (e.g., a half-wing defined on the left-hand-side of the airplane, rather than the conventional right-hand side) will result in the tip WingXSec being used. :type coincident with the centerline plane: i.e., XZ plane :param _sectional: A boolean. If False, returns the total span. If True, returns a list of spans for each of the `n-1` lofted sections (between the `n` wing cross-sections in wing.xsec). .. py:method:: area(type = 'planform', include_centerline_distance=False, _sectional = False) Computes the wing area, with options for various ways of measuring this (see `type` argument): If the wing is symmetric, both left/right sides are included in order to obtain the full area. In the case where the root cross-section is not coincident with the center plane (e.g., XZ plane), this function's behavior depends on the `include_centerline_distance` argument. :param type: One of the following options, as a string: * "planform" (default): First, lofts a quadrilateral mean camber surface between each WingXSec. Then, computes the area of each of these sectional surfaces. Then, sums up all the areas and returns it. When airplane designers refer to "wing area" (in the absence of any other qualifiers), this is typically what they mean. * "wetted": Computes the actual surface area of the wing that is in contact with the air. Will typically be a little more than double the "planform" area above; intuitively, this is because it adds both the "top" and "bottom" surface areas. Accounts for airfoil thickness/shape effects. * "xy" or "projected" or "top": Same as "planform", but each sectional surface is projected onto the XY plane (i.e., top-down view) before computing the areas. Note that if you try to use this method with a vertically-oriented wing, like most vertical stabilizers, you will get an area near zero. * "xz" or "side": Same as "planform", but each sectional surface is projected onto the XZ plane before computing the areas. :param include_centerline_distance: A boolean flag that tells the function what to do if a wing's root chord is :param not coincident with the centerline plane: * If True, we first figure out which WingXSec is closest to the centerline plane (i.e., XZ plane). Then, we imagine that this WingXSec is extruded along the Y axis to the centerline plane (assuming a straight extrusion to produce a rectangular mid-camber surface). In doing so, we use the wing geometric chord as the extrusion width. We then add the area of this fictitious surface to the area calculation. * If False, this function will simply ignore this fictitious wing area. This is the default behavior. :type not coincident with the centerline plane: i.e., XZ plane :param _sectional: A boolean. If False, returns the total area. If True, returns a list of areas for each of the `n-1` lofted sections (between the `n` wing cross-sections in wing.xsec). .. py:method:: aspect_ratio(type = 'geometric') Computes the aspect ratio of the wing, with options for various ways of measuring this. * geometric: geometric aspect ratio, computed in the typical fashion (b^2 / S). * effective: Differs from the geometric aspect ratio only in the case of symmetric wings whose root cross-section is not on the centerline. In these cases, it includes the span and area of the fictitious wing center when computing aspect ratio. :param type: One of the above options, as a string. .. py:method:: is_entirely_symmetric() .. py:method:: mean_geometric_chord() Returns the mean geometric chord of the wing (S/b). :return: .. py:method:: mean_aerodynamic_chord() Computes the length of the mean aerodynamic chord of the wing. Uses the generalized methodology described here: https://core.ac.uk/download/pdf/79175663.pdf Returns: The length of the mean aerodynamic chord. .. py:method:: mean_twist_angle() Returns the mean twist angle (in degrees) of the wing, weighted by area. :return: mean twist angle (in degrees) .. py:method:: mean_sweep_angle(x_nondim=0.25) Returns the mean sweep angle (in degrees) of the wing, relative to the x-axis. Positive sweep is backwards, negative sweep is forward. This is purely measured from root to tip, with no consideration for the sweep of the individual cross-sections in between. :param x_nondim: The nondimensional x-coordinate of the cross-section to use for sweep angle computation. * If you provide 0, it will use the leading edge of the cross-section. * If you provide 0.25, it will use the quarter-chord point of the cross-section. * If you provide 1, it will use the trailing edge of the cross-section. :returns: The mean sweep angle, in degrees. .. py:method:: mean_dihedral_angle(x_nondim=0.25) Returns the mean dihedral angle (in degrees) of the wing, relative to the XY plane. Positive dihedral is bending up, negative dihedral is bending down. This is purely measured from root to tip, with no consideration for the dihedral of the individual cross-sections in between. :param x_nondim: The nondimensional x-coordinate of the cross-section to use for sweep angle computation. * If you provide 0, it will use the leading edge of the cross-section. * If you provide 0.25, it will use the quarter-chord point of the cross-section. * If you provide 1, it will use the trailing edge of the cross-section. :returns: The mean dihedral angle, in degrees .. py:method:: aerodynamic_center(chord_fraction = 0.25, _sectional=False) Computes the location of the aerodynamic center of the wing. Uses the generalized methodology described here: https://core.ac.uk/downloattttd/pdf/79175663.pdf Args: chord_fraction: The position of the aerodynamic center along the MAC, as a fraction of MAC length. Typically, this value (denoted `h_0` in the literature) is 0.25 for a subsonic wing. However, wing-fuselage interactions can cause a forward shift to a value more like 0.1 or less. Citing Cook, Michael V., "Flight Dynamics Principles", 3rd Ed., Sect. 3.5.3 "Controls-fixed static stability". PDF: https://www.sciencedirect.com/science/article/pii/B9780080982427000031 Returns: The (x, y, z) coordinates of the aerodynamic center of the wing. .. py:method:: taper_ratio() Gives the taper ratio of the Wing. Strictly speaking, only valid for trapezoidal wings. :returns: Taper ratio of the Wing. .. py:method:: volume(_sectional = False) Computes the volume of the Wing. :param _sectional: A boolean. If False, returns the total volume. If True, returns a list of volumes for each of :param the `n-1` lofted sections: :type the `n-1` lofted sections: between the `n` wing cross-sections in wing.xsec :returns: The computed volume. .. py:method:: get_control_surface_names() Gets the names of all control surfaces on this wing. :returns: A list of control surface names. .. py:method:: set_control_surface_deflections(control_surface_mappings) Sets the deflection of all control surfaces on this wing, based on the provided mapping. :param control_surface_mappings: A dictionary mapping control surface names to their deflection angles, in degrees. Note: control surface names are set in the asb.ControlSurface constructor. :returns: None. (in-place) .. py:method:: control_surface_area(by_name = None, type = 'planform') Computes the total area of all control surfaces on this wing, optionally filtered by their name. Control surfaces are defined on a section-by-section basis, and are defined in the WingXSec constructor using its `control_surfaces` argument. Note: If redundant control surfaces are defined (e.g., elevons, as defined by separate ailerons + elevator), the area will be duplicated. If the wing is symmetric, control surfaces on both left/right sides are included in order to obtain the full area. :param by_name: If not None, only control surfaces with this name will be included in the area calculation. Note: control surface names are set in the asb.ControlSurface constructor. :param type: One of the following options, as a string: * "planform" (default): First, lofts a quadrilateral mean camber surface between each WingXSec. Then, computes the area of each of these sectional surfaces. Then, computes what fraction of this area is control surface. Then, sums up all the areas and returns it. When airplane designers refer to "control surface area" (in the absence of any other qualifiers), this is typically what they mean. * "wetted": Computes the actual surface area of the control surface that is in contact with the air. Will typically be a little more than double the "planform" area above; intuitively, this is because it adds both the "top" and "bottom" surface areas. Accounts for airfoil thickness/shape effects. * "xy" or "projected" or "top": Same as "planform", but each sectional surface is projected onto the XY plane (i.e., top-down view) before computing the areas. Note that if you try to use this method with a vertically-oriented wing, like most vertical stabilizers, you will get an area near zero. * "xz" or "side": Same as "planform", but each sectional surface is projected onto the XZ plane before computing the areas. .. py:method:: mesh_body(method='quad', chordwise_resolution = 36, chordwise_spacing_function_per_side = np.cosspace, mesh_surface = True, mesh_tips = True, mesh_trailing_edge = True, mesh_symmetric = True) Meshes the outer mold line surface of the wing. Uses the `(points, faces)` standard mesh format. For reference on this format, see the documentation in `aerosandbox.geometry.mesh_utilities`. Order of faces: * On the right wing (or, if `Wing.symmetric` is `False`, just the wing itself): * If `mesh_surface` is `True`: * First face is nearest the top-side trailing edge of the wing root. * Proceeds chordwise, along the upper surface of the wing from back to front. Upon reaching the leading edge, continues along the lower surface of the wing from front to back. * Then, repeats this process for the next spanwise slice of the wing, and so on. * If `mesh_trailing_edge` is `True`: * Continues by meshing the trailing edge of the wing. Meshes the inboard trailing edge first, then proceeds spanwise to the outboard trailing edge. * If `mesh_tips` is `True`: * Continues by meshing the wing tips. Meshes the inboard tip first, then meshes the outboard tip. * Within each tip, meshes from the :param method: One of the following options, as a string: * "tri": Triangular mesh. * "quad": Quadrilateral mesh. :param chordwise_resolution: Number of points to use per wing chord, per wing section. :param chordwise_spacing_function_per_side: A function that determines how to space points in the chordwise :param direction along the top and bottom surfaces. Common values would be `np.linspace` or `np.cosspace`: :param : :param but it can be any function with the call signature `f: :type but it can be any function with the call signature `f: a, b, n :param between `a` and `b`. [function]: :param mesh_surface: If True, includes the actual wing surface in the mesh. :param mesh_tips: If True, includes the wing tips (both on the inboard-most section and on the outboard-most :param section) in the mesh.: :param mesh_trailing_edge: If True, includes the wing trailing edge in the mesh, if the trailing-edge thickness :param is nonzero.: :param mesh_symmetric: Has no effect if the wing is not symmetric. If the wing is symmetric this determines whether :param the generated mesh is also symmetric: :type the generated mesh is also symmetric: right side :param or if if only one side of the wing: :type or if if only one side of the wing: right side Returns: Standard unstructured mesh format: A tuple of `points` and `faces`, where: * `points` is a `n x 3` array of points, where `n` is the number of points in the mesh. * `faces` is a `m x 3` array of faces if `method` is "tri", or a `m x 4` array of faces if `method` is "quad". * Each row of `faces` is a list of indices into `points`, which specifies a face. .. py:method:: mesh_thin_surface(method='tri', chordwise_resolution = 36, chordwise_spacing_function = np.cosspace, add_camber = True) Meshes the mean camber line of the wing as a thin-sheet body. Uses the `(points, faces)` standard mesh format. For reference on this format, see the documentation in `aerosandbox.geometry.mesh_utilities`. Order of faces: * On the right wing (or, if `Wing.symmetric` is `False`, just the wing itself): * First face is the face nearest the leading edge of the wing root. * Proceeds along a chordwise strip to the trailing edge. * Then, goes to the subsequent spanwise location and does another chordwise strip, et cetera until we get to the wing tip. * On the left wing (applicable only if `Wing.symmetric` is `True`): * Same order: Starts at the root leading edge, goes in chordwise strips. Order of vertices within each face: * On the right wing (or, if `Wing.symmetric` is `False`, just the wing itself): * Front-left * Back-left * Back-right * Front-right * On the left wing (applicable only if `Wing.symmetric` is `True`): * Front-left * Back-left * Back-right * Front-right :param method: A string, which determines whether to mesh the fuselage as a series of quadrilaterals or triangles. * "quad" meshes the fuselage as a series of quadrilaterals. * "tri" meshes the fuselage as a series of triangles. :param chordwise_resolution: Determines the number of chordwise panels to use in the meshing. [int] :param chordwise_spacing_function: Determines how to space the chordwise panels. Can be `np.linspace` or :param `np.cosspace`: :type `np.cosspace`: a, b, n :param or any other function of the call signature `f: :type or any other function of the call signature `f: a, b, n :param `n` points between `a` and `b`. [function]: :param add_camber: Controls whether to mesh the thin surface with camber (i.e., mean camber line), or to just :param mesh the flat planform. [bool]: Returns: Standard unstructured mesh format: A tuple of `points` and `faces`, where: * `points` is a `n x 3` array of points, where `n` is the number of points in the mesh. * `faces` is a `m x 3` array of faces if `method` is "tri", or a `m x 4` array of faces if `method` is "quad". * Each row of `faces` is a list of indices into `points`, which specifies a face. .. py:method:: mesh_line(x_nondim = 0.25, z_nondim = 0, add_camber = True) Meshes a line that goes through each of the WingXSec objects in this wing. :param x_nondim: The nondimensional (chord-normalized) x-coordinate that the line should go through. Can either :param be a single value used at all cross-sections: :param or can be an iterable of values to be used at the: :param respective cross-sections.: :param z_nondim: The nondimensional (chord-normalized) y-coordinate that the line should go through. Here, :param y-coordinate means the "vertical" component: :type y-coordinate means the "vertical" component: think standard 2D airfoil axes :param value used at all cross-sections: :param or can be an iterable of values to be used at the respective cross: :param sections.: :param add_camber: Controls whether the camber of each cross-section's airfoil should be added to the line or :param not. Essentially modifies `z_nondim` to be `z_nondim + camber`.: Returns: A list of points, where each point is a 3-element array of the form `[x, y, z]`. Goes from the root to the tip. Ignores any wing symmetry (e.g., only gives one side). .. py:method:: draw(*args, **kwargs) An alias to the more general Airplane.draw() method. See there for documentation. :param \*args: Arguments to pass through to Airplane.draw() :param \*\*kwargs: Keyword arguments to pass through to Airplane.draw() Returns: Same return as Airplane.draw() .. py:method:: draw_wireframe(*args, **kwargs) An alias to the more general Airplane.draw_wireframe() method. See there for documentation. :param \*args: Arguments to pass through to Airplane.draw_wireframe() :param \*\*kwargs: Keyword arguments to pass through to Airplane.draw_wireframe() Returns: Same return as Airplane.draw_wireframe() .. py:method:: draw_three_view(*args, **kwargs) An alias to the more general Airplane.draw_three_view() method. See there for documentation. :param \*args: Arguments to pass through to Airplane.draw_three_view() :param \*\*kwargs: Keyword arguments to pass through to Airplane.draw_three_view() Returns: Same return as Airplane.draw_three_view() .. py:method:: subdivide_sections(ratio, spacing_function = np.linspace) Generates a new Wing that subdivides the existing sections of this Wing into several smaller ones. Splits each section into N=`ratio` smaller sub-sections by inserting new cross-sections (xsecs) as needed. This can allow for finer aerodynamic resolution of sectional properties in certain analyses. :param ratio: The number of new sections to split each old section into. :param spacing_function: A function that takes in three arguments: the start, end, and number of points to generate. The default is `np.linspace`, which generates a linearly-spaced array of points. Other options include `np.cosspace`, which generates a cosine-spaced array of points. Returns: A new Wing object with subdivided sections. .. py:method:: _compute_xyz_le_of_WingXSec(index) .. py:method:: _compute_xyz_te_of_WingXSec(index) .. py:method:: _compute_xyz_of_WingXSec(index, x_nondim, z_nondim) .. py:method:: _compute_frame_of_WingXSec(index) Computes the local reference frame associated with a particular cross-section (XSec) of this wing. :param index: Which cross-section (as indexed in Wing.xsecs) should we get the frame of? :returns: A tuple of (xg_local, yg_local, zg_local), where each entry refers to the respective (normalized) axis of the local reference frame of the WingXSec. Given in geometry axes. .. py:method:: _compute_frame_of_section(index) Computes the local reference frame associated with a particular section. (Note that sections and cross sections are different! cross-sections, or xsecs, are the vertices, and sections are the parts in between. In other words, a wing with N cross-sections (xsecs) will always have N-1 sections. :param index: Which section should we get the frame of? If given `i`, this retrieves the frame of the section :param between xsecs `i` and `i+1`.: :returns: A tuple of (xg_local, yg_local, zg_local), where each entry refers to the respective (normalized) axis of the local reference frame of the section. Given in geometry axes. .. py:class:: WingXSec(xyz_le = None, chord = 1.0, twist = 0.0, airfoil = None, control_surfaces = None, analysis_specific_options = None, **deprecated_kwargs) Bases: :py:obj:`aerosandbox.common.AeroSandboxObject` Definition for a wing cross-section ("X-section"). .. py:attribute:: xyz_le .. py:attribute:: chord :value: 1.0 .. py:attribute:: twist :value: 0.0 .. py:attribute:: airfoil :value: None .. py:attribute:: control_surfaces :value: None .. py:attribute:: analysis_specific_options :value: None .. py:method:: __repr__() .. py:method:: translate(xyz) Returns a copy of this WingXSec that has been translated by `xyz`. :param xyz: The amount to translate the WingXSec. Given as a 3-element NumPy vector. Returns: A new WingXSec object. .. py:method:: xsec_area() Computes the WingXSec's cross-sectional (xsec) area. Returns: The (dimensional) cross-sectional area of the WingXSec. .. py:class:: ControlSurface(name = 'Untitled', symmetric = True, deflection = 0.0, hinge_point = 0.75, trailing_edge = True, analysis_specific_options = None) Bases: :py:obj:`aerosandbox.common.AeroSandboxObject` Definition for a control surface, which is attached to a particular WingXSec via WingXSec's `control_surfaces=[]` parameter. .. py:attribute:: name :value: 'Untitled' .. py:attribute:: symmetric :value: True .. py:attribute:: deflection :value: 0.0 .. py:attribute:: hinge_point :value: 0.75 .. py:attribute:: trailing_edge :value: True .. py:attribute:: analysis_specific_options :value: None .. py:method:: __repr__() .. py:data:: wing