Skip to content

Commit 7ae28a8

Browse files
authored
Add aerostructural support for multi-section surfaces (#461)
* Add aerostructural support for multi-section * Add support for struct design variables and fixed uni_t_over_c and uni_mesh output naming * Added new test for aerostructural multi-section * Update multisection aerostructural tests * Fix code for latest refactor and clean up tests * Add docs and tutorial * Added support for rotational VLM to the aerostructural groups * Update docs and tests * Fix flake8 * Fix flake 8 again
1 parent efaf675 commit 7ae28a8

7 files changed

Lines changed: 1161 additions & 3 deletions

File tree

openaerostruct/docs/advanced_features/multi_section_surfaces.rst

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
.. _Custom_Mesh:
22

3-
Multi-section Surfaces
3+
Multi-section surfaces for aerodynamic problems
44
==========================
55

66
OpenAeroStruct features the ability to specify surfaces as a series of sequentially connected sections.
77
Rather than controling the geometry of the surface as a whole, the optimizer can control the geometric parameters of each section individually using any of the geometric transformations available in OpenAeroStruct.
88
This feature was developed for the modular wing morphing applications but can be useful in situations where the user wishes to optimize a particular wing section while leaving the others fixed.
99

10-
*Please note that aerostructural optimization with multi-section wings is currently not supported*
11-
1210
This example script demonstrate the usage of the multi-section wing geometry features in OpenAeroStruct.
1311
We first start with the induced drag minimization of a simple two-section symmetrical wing.
1412

@@ -125,4 +123,50 @@ The steps required to setup the multi-section geometry group by constraint featu
125123
:end-before: checkpoint 4
126124

127125

126+
Multi-section surfaces for aerostructural problems
127+
==========================
128+
129+
Both multi-section geometery parameterization approaches in OpenAeroStruct are fully compatible with the aerostructural simulation capability through the multi-section aerostructural geometery class.
130+
Both the tube and wingbox models can be used however note that the structural model itself is not multi-section.
131+
The structural member geometery is not defined for each section but rather the entire surface as it normally is.
132+
The finite-element beam model is therefore coupled with the unified surface mesh which is where the aerodynamic simulation is done.
133+
134+
135+
We start by making the necessary imports for an aerostructural optimization in OpenAeroStruct.
136+
Note that we also import a special geometry group for multi-section aerostructural problems.
137+
138+
.. literalinclude:: /advanced_features/scripts/basic_2_sec_AS.py
139+
:start-after: checkpoint 0
140+
:end-before: checkpoint 1
141+
142+
Next, we define the surface dictionary as we did for the construction-based multi-section geometry parameterization but also include the structural parameters.
143+
144+
145+
.. literalinclude:: /advanced_features/scripts/basic_2_sec_AS.py
146+
:start-after: checkpoint 1
147+
:end-before: checkpoint 2
148+
149+
The independent variables and unified twist spline are setup in the same way as in the construction-based multi-section geometry parameterization.
128150

151+
.. literalinclude:: /advanced_features/scripts/basic_2_sec_AS.py
152+
:start-after: checkpoint 2
153+
:end-before: checkpoint 3
154+
155+
Next, we add the multi-section aerostructural geometery and aerostructural analysis point groups and make the necessary connections.
156+
Note that this step is very similar to the single section aerostructural problem setup with only the geometery group being replaced.
157+
158+
.. literalinclude:: /advanced_features/scripts/basic_2_sec_AS.py
159+
:start-after: checkpoint 3
160+
:end-before: checkpoint 4
161+
162+
We can now setup our optimization problem, configure the optimizer and run the optimization.
163+
164+
.. literalinclude:: /advanced_features/scripts/basic_2_sec_AS.py
165+
:start-after: checkpoint 4
166+
:end-before: checkpoint 5
167+
168+
Lastly, we plot and print the results.
169+
170+
.. literalinclude:: /advanced_features/scripts/basic_2_sec_AS.py
171+
:start-after: checkpoint 5
172+
:end-before: checkpoint 6
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
"""Optimizes the section twist distribution of a two section symmetrical wing using the construction-based approach for section
2+
joining and the aerostructural tube model. This example is referenced as part of the multi-section tutorial."""
3+
4+
# docs checkpoint 0
5+
import numpy as np
6+
import openmdao.api as om
7+
from openaerostruct.integration.aerostruct_groups import MultiSecAerostructGeometry, AerostructPoint
8+
from openaerostruct.utils.constants import grav_constant
9+
from openaerostruct.geometry.utils import build_section_dicts, unify_mesh, build_multi_spline, connect_multi_spline
10+
11+
# docs checkpoint 1
12+
13+
# The geometry parameterization used here is identical to the one in the two section contruction based example. However,
14+
# instead of chord we apply the principle to the twist B-spline.
15+
16+
# Set-up B-splines for each section. Done here since this information will be needed multiple times.
17+
sec_twist_cp = [np.zeros(2), np.zeros(2)]
18+
19+
# Note the additional of structural variables to the surface dictionary
20+
surface = {
21+
# Wing definition
22+
# Basic surface parameters
23+
"name": "surface",
24+
"is_multi_section": True,
25+
"num_sections": 2, # The number of sections in the multi-section surface
26+
"sec_name": ["sec0", "sec1"], # names of the individual sections
27+
"symmetry": True, # if true, model one half of wing. reflected across the midspan of the root section
28+
"S_ref_type": "wetted", # how we compute the wing area, can be 'wetted' or 'projected'
29+
"root_section": 1,
30+
# Geometry Parameters
31+
"taper": [1.0, 1.0], # Wing taper for each section
32+
"span": [10.0, 10.0], # Wing span for each section
33+
"sweep": [0.0, 0.0], # Wing sweep for each section
34+
"twist_cp": sec_twist_cp,
35+
"t_over_c_cp": [np.array([0.15]), np.array([0.15])], # thickness over chord ratio (NACA0015)
36+
"root_chord": 5.0, # Wing root chord for each section
37+
# Mesh Parameters
38+
"meshes": "gen-meshes", # Supply a mesh for each section or "gen-meshes" for automatic mesh generation
39+
"nx": 2, # Number of chordwise points. Same for all sections
40+
"ny": [3, 3], # Number of spanwise points for each section
41+
# Aerodynamic Parameters
42+
"CL0": 0.0, # CL of the surface at alpha=0
43+
"CD0": 0.015, # CD of the surface at alpha=0
44+
# Airfoil properties for viscous drag calculation
45+
"k_lam": 0.05, # percentage of chord with laminar
46+
# flow, used for viscous drag
47+
"c_max_t": 0.303, # chordwise location of maximum (NACA0015)
48+
# thickness
49+
"with_viscous": True, # if true, compute viscous drag
50+
"with_wave": False, # if true, compute wave drag
51+
"groundplane": False,
52+
# Structural
53+
"fem_model_type": "tube",
54+
"thickness_cp": 0.1 * np.ones((2)),
55+
"E": 70.0e9, # [Pa] Young's modulus of the spar
56+
"G": 30.0e9, # [Pa] shear modulus of the spar
57+
"yield": 500.0e6 / 2.5, # [Pa] yield stress divided by 2.5 for limiting case
58+
"mrho": 3.0e3, # [kg/m^3] material density
59+
"fem_origin": 0.35, # normalized chordwise location of the spar
60+
"wing_weight_ratio": 2.0,
61+
"struct_weight_relief": False, # True to add the weight of the structure to the loads on the structure
62+
"distributed_fuel_weight": False,
63+
# Constraints
64+
"exact_failure_constraint": False, # if false, use KS function
65+
}
66+
67+
# docs checkpoint 2
68+
69+
# Create the problem and assign the model group
70+
prob = om.Problem(reports=False)
71+
72+
# Add problem information as an independent variables component
73+
indep_var_comp = om.IndepVarComp()
74+
indep_var_comp.add_output("v", val=248.136, units="m/s")
75+
indep_var_comp.add_output("alpha", val=9.0, units="deg")
76+
indep_var_comp.add_output("Mach_number", val=0.84)
77+
indep_var_comp.add_output("re", val=1.0e6, units="1/m")
78+
indep_var_comp.add_output("rho", val=0.38, units="kg/m**3")
79+
indep_var_comp.add_output("CT", val=grav_constant * 17.0e-6, units="1/s")
80+
indep_var_comp.add_output("R", val=11.165e6, units="m")
81+
indep_var_comp.add_output("W0", val=0.4 * 3e5, units="kg")
82+
indep_var_comp.add_output("speed_of_sound", val=295.4, units="m/s")
83+
indep_var_comp.add_output("load_factor", val=1.0)
84+
indep_var_comp.add_output("empty_cg", val=np.zeros((3)), units="m")
85+
86+
prob.model.add_subsystem("prob_vars", indep_var_comp, promotes=["*"])
87+
88+
89+
# Generate the sections and unified mesh here in addition to adding the components.
90+
# This has to also be done here since AeroPoint has to know the unified mesh size.
91+
section_surfaces = build_section_dicts(surface)
92+
uniMesh = unify_mesh(section_surfaces)
93+
surface["mesh"] = uniMesh
94+
95+
96+
# Build a component with B-spline control points that joins the sections by construction
97+
twist_comp = build_multi_spline("twist_cp", len(section_surfaces), sec_twist_cp)
98+
prob.model.add_subsystem("twist_bspline", twist_comp)
99+
100+
# Connect the B-spline component to the section B-splines
101+
connect_multi_spline(prob, section_surfaces, sec_twist_cp, "twist_cp", "twist_bspline", surface["name"])
102+
103+
# docs checkpoint 3
104+
105+
# Create and add a group that handles the geometry for the
106+
# aerostructual multi-section lifting surface
107+
multi_geom_group = MultiSecAerostructGeometry(surface=surface)
108+
prob.model.add_subsystem(surface["name"], multi_geom_group)
109+
110+
name = surface["name"]
111+
112+
point_name = "AS_point_0"
113+
114+
# Create the aero point group and add it to the model
115+
AS_point = AerostructPoint(surfaces=[surface])
116+
117+
prob.model.add_subsystem(
118+
point_name,
119+
AS_point,
120+
promotes_inputs=[
121+
"v",
122+
"alpha",
123+
"Mach_number",
124+
"re",
125+
"rho",
126+
"CT",
127+
"R",
128+
"W0",
129+
"speed_of_sound",
130+
"empty_cg",
131+
"load_factor",
132+
],
133+
)
134+
135+
com_name = point_name + "." + name + "_perf"
136+
prob.model.connect(name + ".local_stiff_transformed", point_name + ".coupled." + name + ".local_stiff_transformed")
137+
prob.model.connect(name + ".nodes", point_name + ".coupled." + name + ".nodes")
138+
139+
# Connect aerodyamic mesh to coupled group mesh
140+
prob.model.connect(name + ".mesh", point_name + ".coupled." + name + ".mesh")
141+
142+
# Connect performance calculation variables
143+
prob.model.connect(name + ".radius", com_name + ".radius")
144+
prob.model.connect(name + ".thickness", com_name + ".thickness")
145+
prob.model.connect(name + ".nodes", com_name + ".nodes")
146+
prob.model.connect(name + ".cg_location", point_name + "." + "total_perf." + name + "_cg_location")
147+
prob.model.connect(name + ".structural_mass", point_name + "." + "total_perf." + name + "_structural_mass")
148+
prob.model.connect(name + ".t_over_c", com_name + ".t_over_c")
149+
150+
151+
# docs checkpoint 4
152+
153+
prob.driver = om.ScipyOptimizeDriver()
154+
prob.driver.options["optimizer"] = "SLSQP"
155+
prob.driver.options["tol"] = 1e-6
156+
prob.driver.options["disp"] = True
157+
prob.driver.options["maxiter"] = 1000
158+
159+
160+
# Setup problem and add design variables, constraint, and objective
161+
prob.model.add_design_var("twist_bspline.twist_cp_spline", lower=-10.0, upper=15.0)
162+
prob.model.add_design_var("surface.thickness_cp", lower=0.01, upper=0.5, scaler=1e2)
163+
prob.model.add_constraint("AS_point_0.surface_perf.failure", upper=0.0)
164+
prob.model.add_constraint("AS_point_0.surface_perf.thickness_intersects", upper=0.0)
165+
166+
# Add design variables, constraints, and objective on the problem
167+
prob.model.add_design_var("alpha", lower=-10.0, upper=10.0)
168+
prob.model.add_constraint("AS_point_0.L_equals_W", equals=0.0)
169+
prob.model.add_objective("AS_point_0.fuelburn", scaler=1e-5)
170+
171+
172+
# Set up the problem
173+
prob.setup(check=True)
174+
175+
# Run the optimization
176+
optResult = prob.run_driver()
177+
# om.n2(prob, show_browser=False)
178+
179+
180+
# docs checkpoint 5
181+
182+
# Print fuelburn
183+
print(prob.get_val("AS_point_0.fuelburn"))
184+
# docs checkpoint 6

openaerostruct/docs/advanced_features/scripts/basic_2_sec_visc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@
167167
meshUni = prob.get_val(name + "." + unification_name + "." + name + "_uni_mesh")
168168

169169

170+
# Plot the results
170171
def plot_meshes(meshes):
171172
"""this function plots to plot the mesh"""
172173
plt.figure(figsize=(8, 4))

0 commit comments

Comments
 (0)