Skip to content

Commit

Permalink
Extend OpenAeroStruct integration to arbitrary planforms (#47)
Browse files Browse the repository at this point in the history
* Moved OAS mesh generation to own files

* Added OAS mesh visualization function

* Compute method for sectional planform definition done, needs derivatives

* Switched num_x and num_y to be number of panels instead of coordinates

* Fixed my new mesh generation so it doesn't duplicate points and uses num_x and num_y for panels not coordinates

* Working derivatives, but need to clean code and make it more efficient

* Improved derivative efficiency, still more to go

* Ran black

* Further code simplification, time for sparse partials

* Added sparsity for mesh w.r.t. x_LE_sec

* Added tests for new mesh generation

* Preallocated arrays for sparse derivative

* Refactored OAS drag polar to enable more general planforms

* Drag polar VLM tests all pass, need to add more for other meshing options

* Added t over c input and interpolation

* Testing for VLMDragPolar with different mesh generation

* Minor version bump

* Ran black on visualization

* Updated OpenAeroStruct in the docs

* Added option to prevent planform scaling in SectionPlanformMesh

* Updated remaining OAS citations

* Ugh flake8 cmon bro

* More flake8 junk

* Hopefully the last of the flake8 nonsense

* Fixed my beautiful ASCII art

* Realized I made a mistake in the twist spline interpolation, adjusted accordingly

* Edited docstring
  • Loading branch information
eytanadler committed Mar 22, 2023
1 parent 573f1eb commit 48d544d
Show file tree
Hide file tree
Showing 20 changed files with 1,954 additions and 732 deletions.
68 changes: 14 additions & 54 deletions doc/features/aerodynamics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,71 +31,31 @@ In run script, users should set the values for the following aircraft design par
Using OpenAeroStruct
====================
Instead of the simple drag polar model, you can use `OpenAeroStruct <https://github.com/mdolab/OpenAeroStruct>`_ to compute the drag.
This allows you to parametrize the aircraft wing design in more details and explore the effects of wing geometry, including taper, sweep, and twist.
This enables a more detailed parameterization of the aircraft wing.
OpenAeroStruct implements the vortex-lattice method (VLM) for aerodynamics and beam-based finite element method (FEM) for structures (in the case of the aerostructural drag polar).
For more detail, please check the `documentation <https://mdolab-openaerostruct.readthedocs-hosted.com/en/latest/>`_.
For more details, please check the `documentation <https://mdolab-openaerostruct.readthedocs-hosted.com/en/latest/>`_.

The wing is currently limited to simple planform geometries.
Additionally, the wing does not include a tail to trim it because there is no OpenConcept weight position model with which to trim.
The aerodynamic-only model supports three types of mesh generation for planform and geometry flexibility.
The aerostructural model is currently limited to simple planform geometries.
Additionally, the wing does not include a horizontal tail to trim it.

OpenConcept uses a surrogate model trained by OpenAeroStruct analyses to reduce the computational cost.
The data generation and surrogate training is automated; specifying the training grid manually may improve accuracy or decrease computational cost.
The data generation and surrogate training is automated; specifying the training grid manually may improve accuracy and decrease computational cost.

VLM-based aerodynamic model: ``VLMDragPolar``
------------------------------------------------
This model uses the vortex-lattice method (VLM) to compute the drag.
The inputs to this model are the flight conditions (Mach number, altitude, dynamic pressure, lift coefficient) and aircraft design parameters.

Users should set the following design parameters and options in the run script.
The aerodynamic mesh can be defined in one of three ways:

.. list-table:: Aircraft design variables for VLM-based model
:header-rows: 1
1. Use simple planform variables to define a trapezoidal planform. These planform variables are wing area, aspect ratio, taper ratio, and quarter chord sweep angle.

* - Variable name
- Property
- Type
* - ac|geom|wing|S_ref
- Reference wing area
- float
* - ac|geom|wing|AR
- Wing aspect ratio
- float
* - ac|geom|wing|taper
- Taper ratio
- float
* - ac|geom|wing|c4sweep
- Sweep angle at quarter chord
- float
* - ac|geom|wing|twist
- Spanwise distribution of twist, from wint tip to root.
- 1D ndarray, lendth ``num_twist``
* - ac|aero|CD_nonwing
- Drag coefficient of components other than the wing; e.g. fuselage,
tail, interference drag, etc.
- float
* - fltcond|TempIncrement
- Temperature increment for non-standard day
- float

.. list-table:: Options for VLM-based model
:widths: 30 50 20
:header-rows: 1
2. Define multiple spanwise wing sections between which a mesh is linearly interpolated. Each section is defined by its streamwise offset, chord length, and spanwise position. The whole planform is then scaled uniformly to match the desired wing area.

* - Variable name
- Property
- Type
* - ``num_x``
- VLM mesh size (number of vertices) in chordwise direction.
- int
* - ``num_y``
- VLM mesh size (number of vertices) in spanwise direction.
- int
* - ``num_twist``
- Number of spanwise control points for twist distribution.
- int

There are other advanced options, e.g., the surrogate training points in Mach-alpha-altitude space.
The default settings should work fine for these advanced options, but if you want to make changes, please refer to the source docs.
3. Directly provide an OpenAeroStruct-compatible mesh.

More details on the inputs, outputs, and options are available in the source code documentation.

Aerostructural model: ``AeroStructDragPolar``
-----------------------------------------------------
Expand Down Expand Up @@ -133,12 +93,12 @@ OpenConcept uses surrogate models based on OpenAeroStruct analyses to reduce the
The surrogate models are trained in the 3D input space of Mach number, angle of attack, and altitude.
The outputs of the surrogate models are CL and CD (and failure for ``AeroStructDragPolar``).

For more details about the surrogate models, see our `paper <https://mdolab.engin.umich.edu/bibliography/Adler2022a>`_.
For more details about the surrogate models, see our `paper <https://mdolab.engin.umich.edu/bibliography/Adler2022d>`_.

Other models
============

The aerodynamics module also includes a couple components that may be useful to be aware of:
The aerodynamics module also includes a couple components that may be useful:

- ``StallSpeed``, which uses :math:`C_{L, \text{max}}`, aircraft weight, and wing area to compute the stall speed
- ``Lift``, which computes lift force using lift coefficient, wing area, and dynamic pressure
2 changes: 1 addition & 1 deletion openconcept/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.0.2"
__version__ = "1.1.0"
3 changes: 2 additions & 1 deletion openconcept/aerodynamics/openaerostruct/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .drag_polar import VLMDragPolar, VLMDataGen, VLM, PlanformMesh
from .mesh_gen import TrapezoidalPlanformMesh, SectionPlanformMesh, ThicknessChordRatioInterp, SectionLinearInterp
from .drag_polar import VLMDragPolar, VLMDataGen, VLM
from .aerostructural import AerostructDragPolar, OASDataGen, Aerostruct, AerostructDragPolarExact
54 changes: 30 additions & 24 deletions openconcept/aerodynamics/openaerostruct/aerostructural.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from openaerostruct.integration.aerostruct_groups import AerostructPoint
from openaerostruct.structures.spatial_beam_setup import SpatialBeamSetup
from openaerostruct.structures.wingbox_group import WingboxGroup
from openconcept.aerodynamics.openaerostruct.drag_polar import PlanformMesh
from openconcept.aerodynamics.openaerostruct.mesh_gen import TrapezoidalPlanformMesh
except ImportError:
raise ImportError("OpenAeroStruct must be installed to use the AerostructDragPolar component")

Expand Down Expand Up @@ -119,9 +119,9 @@ class AerostructDragPolar(om.Group):
num_nodes : int
Number of analysis points per mission segment (scalar, dimensionless)
num_x : int
Number of points in x (streamwise) direction (scalar, dimensionless)
Number of panels in x (streamwise) direction (scalar, dimensionless)
num_y : int
Number of points in y (spanwise) direction for one wing because
Number of panels in y (spanwise) direction for one wing because
uses symmetry (scalar, dimensionless)
num_twist : int
Number of spline control points for twist (scalar, dimensionless)
Expand Down Expand Up @@ -152,8 +152,8 @@ def __init__(self, **kwargs):

def initialize(self):
self.options.declare("num_nodes", default=1, desc="Number of analysis points to run")
self.options.declare("num_x", default=3, desc="Number of streamwise mesh points")
self.options.declare("num_y", default=7, desc="Number of spanwise (half wing) mesh points")
self.options.declare("num_x", default=2, desc="Number of streamwise mesh panels")
self.options.declare("num_y", default=6, desc="Number of spanwise (half wing) mesh panels")
self.options.declare("num_twist", default=4, desc="Number of twist spline control points")
self.options.declare("num_toverc", default=4, desc="Number of thickness to chord ratio spline control points")
self.options.declare("num_skin", default=4, desc="Number of skin thickness spline control points")
Expand Down Expand Up @@ -307,9 +307,9 @@ class OASDataGen(om.ExplicitComponent):
Options
-------
num_x : int
Number of points in x (streamwise) direction (scalar, dimensionless)
Number of panels in x (streamwise) direction (scalar, dimensionless)
num_y : int
Number of points in y (spanwise) direction for one wing because
Number of panels in y (spanwise) direction for one wing because
uses symmetry (scalar, dimensionless)
num_twist : int
Number of spline control points for twist (scalar, dimensionless)
Expand Down Expand Up @@ -341,8 +341,8 @@ def __init__(self, **kwargs):
self.cite = CITATION

def initialize(self):
self.options.declare("num_x", default=3, desc="Number of streamwise mesh points")
self.options.declare("num_y", default=7, desc="Number of spanwise (half wing) mesh points")
self.options.declare("num_x", default=2, desc="Number of streamwise mesh panels")
self.options.declare("num_y", default=6, desc="Number of spanwise (half wing) mesh panels")
self.options.declare("num_twist", default=4, desc="Number of twist spline control points")
self.options.declare("num_toverc", default=4, desc="Number of thickness to chord ratio spline control points")
self.options.declare("num_skin", default=4, desc="Number of skin thickness spline control points")
Expand Down Expand Up @@ -558,9 +558,9 @@ def compute_partials(self, inputs, partials):
List of spar thicknesses at control points of spline (vector, m)
NOTE: length of vector is num_spar (set in options of OASDataGen)
num_x: int
number of points in x (streamwise) direction (scalar, dimensionless)
number of panels in x (streamwise) direction (scalar, dimensionless)
num_y: int
number of points in y (spanwise) direction for one wing because
number of panels in y (spanwise) direction for one wing because
uses symmetry (scalar, dimensionless)
surf_dict : dict
Dictionary of OpenAeroStruct surface options; any options provided here
Expand Down Expand Up @@ -812,9 +812,9 @@ class Aerostruct(om.Group):
Options
-------
num_x : int
Number of points in x (streamwise) direction (scalar, dimensionless)
Number of panels in x (streamwise) direction (scalar, dimensionless)
num_y : int
Number of points in y (spanwise) direction for one wing because
Number of panels in y (spanwise) direction for one wing because
uses symmetry (scalar, dimensionless)
num_twist : int
Number of spline control points for twist (scalar, dimensionless)
Expand All @@ -838,17 +838,19 @@ def __init__(self, **kwargs):
self.cite = CITATION

def initialize(self):
self.options.declare("num_x", default=3, desc="Number of streamwise mesh points")
self.options.declare("num_y", default=7, desc="Number of spanwise (half wing) mesh points")
self.options.declare("num_x", default=2, desc="Number of streamwise mesh panels")
self.options.declare("num_y", default=6, desc="Number of spanwise (half wing) mesh panels")
self.options.declare("num_twist", default=4, desc="Number of twist spline control points")
self.options.declare("num_toverc", default=4, desc="Number of thickness to chord ratio spline control points")
self.options.declare("num_skin", default=4, desc="Number of skin thickness spline control points")
self.options.declare("num_spar", default=4, desc="Number of spar thickness spline control points")
self.options.declare("surf_options", default=None, desc="Dictionary of OpenAeroStruct surface options")

def setup(self):
nx = int(self.options["num_x"])
ny = int(self.options["num_y"])
# Number of coordinates is one more than the number of panels
nx = int(self.options["num_x"]) + 1
ny = int(self.options["num_y"]) + 1

n_twist = int(self.options["num_twist"])
n_skin = int(self.options["num_skin"])
n_spar = int(self.options["num_spar"])
Expand Down Expand Up @@ -1202,7 +1204,11 @@ def setup(self):
comp.add_spline(y_cp_name="t_over_c_cp", y_interp_name="t_over_c")

# Wing mesh generator
wing_group.add_subsystem("mesh_gen", PlanformMesh(num_x=nx, num_y=ny), promotes_inputs=["*"])
wing_group.add_subsystem(
"mesh_gen",
TrapezoidalPlanformMesh(num_x=self.options["num_x"], num_y=self.options["num_y"]),
promotes_inputs=["*"],
)

# Apply twist spline to mesh
wing_group.add_subsystem(
Expand Down Expand Up @@ -1380,9 +1386,9 @@ class AerostructDragPolarExact(om.Group):
num_nodes : int
Number of analysis points per mission segment (scalar, dimensionless)
num_x : int
Number of points in x (streamwise) direction (scalar, dimensionless)
Number of panels in x (streamwise) direction (scalar, dimensionless)
num_y : int
Number of points in y (spanwise) direction for one wing because
Number of panels in y (spanwise) direction for one wing because
uses symmetry (scalar, dimensionless)
num_twist : int
Number of spline control points for twist (scalar, dimensionless)
Expand All @@ -1407,8 +1413,8 @@ def __init__(self, **kwargs):

def initialize(self):
self.options.declare("num_nodes", default=1, desc="Number of analysis points to run")
self.options.declare("num_x", default=3, desc="Number of streamwise mesh points")
self.options.declare("num_y", default=7, desc="Number of spanwise (half wing) mesh points")
self.options.declare("num_x", default=2, desc="Number of streamwise mesh panels")
self.options.declare("num_y", default=6, desc="Number of spanwise (half wing) mesh panels")
self.options.declare("num_twist", default=4, desc="Number of twist spline control points")
self.options.declare("num_toverc", default=4, desc="Number of thickness to chord ratio spline control points")
self.options.declare("num_skin", default=4, desc="Number of skin thickness spline control points")
Expand Down Expand Up @@ -1502,8 +1508,8 @@ def setup(self):
def example_usage():
# Define parameters
nn = 1
num_x = 3
num_y = 5
num_x = 2
num_y = 4
S = 427.8 # m^2
AR = 9.82
taper = 0.149
Expand Down

0 comments on commit 48d544d

Please sign in to comment.