diff --git a/examples/fun3d_examples/README.md b/examples/fun3d_examples/README.md
index 1489070c..26c21179 100644
--- a/examples/fun3d_examples/README.md
+++ b/examples/fun3d_examples/README.md
@@ -1,38 +1,71 @@
-# FUN3D Examples #
-
-* `ate_wedge_optimization` - Optimization of a supersonic panel under supersonic flow using with aerothermoelastic analysis. Uses FUN3D, TACS, MELD, `FuntofemNlbgs` driver.
-* `diamond_unsteady` - Unsteady forward and adjoint analysis of a diamond wedge wing structure under aeroelastic analysis. Uses FUN3D, TACS, MELD.
-* `pyopt_togw_optimization` - Optimization of the CRM aircraft structure under aeroelastic analysis. Uses FUN3D, TACS, MELD, `FuntofemNlbgs` driver.
-* `sst_optimization` - Optimization of a supersonic transport wing under aerothermoelastic analysis using ksfailure and mass. Uses FUN3D, TACS, MELD, `FuntofemNlbgs` driver.
-* `sst_unsteady` - Unsteady forward and adjoint analysis of a simplified supersonic transport wing geometry. Uses FUN3D, TACS, MELD.
-
-### Supersonic Transport Wing ###
-Directory - `sst_optimization`
-The supersonic transport wing was the first demonstration of aerothermoelastic analysis with FUN3D and TACS on a realistic aircraft structure, included in the following paper.
-```r
-Engelstad, S. P., Burke, B. J., Patel, R. N., Sahu, S., and Kennedy, G. J., “High-Fidelity Aerothermoelastic Optimization with
-Differentiable CAD Geometry,” AIAA Scitech 2023 Forum, National Harbor, MD, 2023. doi:10.2514/6.2023-0329.
+# FUN3D Examples
+
+Examples are organized by geometry group. Each group collects all FUN3D-based examples for a single aircraft or wing geometry.
+
+---
+
+## Super Simple Wing (SSW)
+
+The SSW is a simple rectangular wing geometry used for aeroelastic and aerothermal optimization studies.
+
+- **`ssw/aeroelastic_optimization/`** — Inviscid aeroelastic optimization of the Super Simple Wing. Uses FUN3D, TACS, and CAPS/ESP. Scripts 1–4 cover panel thickness optimization, shape optimization, and derivative testing.
+
+- **`ssw/ssw_meshdef_optimization/`** — Mesh deformation-based aeroelastic optimization of the Super Simple Wing. Uses FUN3D, TACS, and CAPS/ESP.
+
+---
+
+## Supersonic Transport Wing (SST)
+
+The SST is a realistic supersonic aircraft wing geometry used for aerothermoelastic optimization.
+
+The SST results images and README live directly in `sst/`. See `sst/sst_optimization/` for the complete runnable example.
+
+```
+Engelstad, S. P., Burke, B. J., Patel, R. N., Sahu, S., and Kennedy, G. J.,
+"High-Fidelity Aerothermoelastic Optimization with Differentiable CAD Geometry,"
+AIAA Scitech 2023 Forum, National Harbor, MD, 2023. doi:10.2514/6.2023-0329.
```
+
-
+
Optimal thicknesses for the supersonic transport wing.
-
+
Pressure contours in the Mach 2.0 flow solved in FUN3D.
-### Computational Research Model ###
-Directory - `pyopt_togw_optimization`
-```r
-Jacobson, K., Kiviaho, J., Smith, M., and Kennedy, G., “An Aeroelastic Coupling Framework for Time-accurate Anal-
-ysis and Optimization,” 2018 AIAA/ASCE/AHS/ASC Structures, Structural Dynamics, and Materials Conference, 2018.
-doi:10.2514/6.2018-0100.
+### `sst/sst_optimization/`
+
+Complete multi-step aerothermoelastic optimization example adapted from the research case. Includes mesh generation, sizing optimization, shape optimization, and fully coupled optimization scripts. Uses FUN3D, TACS, and CAPS/ESP.
+
```
-
-
-
-
-
-
+Engelstad, S. P., Burke, B. J., Patel, R. N., Sahu, S., and Kennedy, G. J.,
+"High-Fidelity Aerothermoelastic Optimization with Differentiable CAD Geometry,"
+AIAA Scitech 2023 Forum, National Harbor, MD, 2023. doi:10.2514/6.2023-0329.
+```
+
+---
+
+## Diamond
+
+- **`diamond/wedge_optimization/`** — Steady aerothermoelastic optimization of a supersonic diamond wedge panel. Minimizes average structural temperature subject to a mass constraint using a fully coupled FUN3D + TACS analysis. Uses a hand-built hexahedral BDF mesh (no CAPS/ESP required).
+
+---
+
+## Archive
+
+The `archive/` subdirectory preserves older examples that are no longer actively maintained. These examples are kept for historical reference but should not be used as templates for new work.
+
+- **`archive/ate_wedge_optimization/`** — Original version using deprecated APIs (`MassoudBody`, old `FUNtoFEMnlbgs` constructor, `PyOptOptimization`). Updated version is at `diamond/wedge_optimization/`.
+
+- **`archive/pyopt_togw_optimization/`** — Archived because it uses deprecated APIs (`MassoudBody`, `PyOptOptimization`). Original reference:
+
+ ```
+ Jacobson, K., Kiviaho, J., Smith, M., and Kennedy, G., "An Aeroelastic Coupling Framework for Time-accurate Analysis and Optimization," 2018 AIAA/ASCE/AHS/ASC Structures, Structural Dynamics, and Materials Conference, 2018. doi:10.2514/6.2018-0100.
+ ```
+
+- **`archive/diamond_unsteady/`** — Archived because it is incomplete (no mesh files included).
+
+- **`archive/sst_unsteady/`** — Archived because it is incomplete (no mesh files included).
diff --git a/examples/fun3d_examples/_ssw-inviscid/struct/.gitignore b/examples/fun3d_examples/archive/.gitkeep
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/struct/.gitignore
rename to examples/fun3d_examples/archive/.gitkeep
diff --git a/examples/fun3d_examples/ate_wedge_optimization/README b/examples/fun3d_examples/archive/ate_wedge_optimization/README
similarity index 100%
rename from examples/fun3d_examples/ate_wedge_optimization/README
rename to examples/fun3d_examples/archive/ate_wedge_optimization/README
diff --git a/examples/fun3d_examples/ate_wedge_optimization/gen_TACS_bdf_aero.py b/examples/fun3d_examples/archive/ate_wedge_optimization/gen_TACS_bdf_aero.py
similarity index 100%
rename from examples/fun3d_examples/ate_wedge_optimization/gen_TACS_bdf_aero.py
rename to examples/fun3d_examples/archive/ate_wedge_optimization/gen_TACS_bdf_aero.py
diff --git a/examples/fun3d_examples/ate_wedge_optimization/py_optimization.py b/examples/fun3d_examples/archive/ate_wedge_optimization/py_optimization.py
similarity index 100%
rename from examples/fun3d_examples/ate_wedge_optimization/py_optimization.py
rename to examples/fun3d_examples/archive/ate_wedge_optimization/py_optimization.py
diff --git a/examples/fun3d_examples/ate_wedge_optimization/steady/Flow/fun3d.nml b/examples/fun3d_examples/archive/ate_wedge_optimization/steady/Flow/fun3d.nml
similarity index 100%
rename from examples/fun3d_examples/ate_wedge_optimization/steady/Flow/fun3d.nml
rename to examples/fun3d_examples/archive/ate_wedge_optimization/steady/Flow/fun3d.nml
diff --git a/examples/fun3d_examples/ate_wedge_optimization/steady/Flow/moving_body.input b/examples/fun3d_examples/archive/ate_wedge_optimization/steady/Flow/moving_body.input
similarity index 100%
rename from examples/fun3d_examples/ate_wedge_optimization/steady/Flow/moving_body.input
rename to examples/fun3d_examples/archive/ate_wedge_optimization/steady/Flow/moving_body.input
diff --git a/examples/fun3d_examples/ate_wedge_optimization/tacs_model.py b/examples/fun3d_examples/archive/ate_wedge_optimization/tacs_model.py
similarity index 100%
rename from examples/fun3d_examples/ate_wedge_optimization/tacs_model.py
rename to examples/fun3d_examples/archive/ate_wedge_optimization/tacs_model.py
diff --git a/examples/fun3d_examples/diamond_unsteady/simple_diamond.csm b/examples/fun3d_examples/archive/diamond_unsteady/simple_diamond.csm
similarity index 100%
rename from examples/fun3d_examples/diamond_unsteady/simple_diamond.csm
rename to examples/fun3d_examples/archive/diamond_unsteady/simple_diamond.csm
diff --git a/examples/fun3d_examples/diamond_unsteady/unsteady_forward.py b/examples/fun3d_examples/archive/diamond_unsteady/unsteady_forward.py
similarity index 100%
rename from examples/fun3d_examples/diamond_unsteady/unsteady_forward.py
rename to examples/fun3d_examples/archive/diamond_unsteady/unsteady_forward.py
diff --git a/examples/fun3d_examples/pyopt_togw_optimization/images/crm_aero_struct_geom.png b/examples/fun3d_examples/archive/pyopt_togw_optimization/images/crm_aero_struct_geom.png
similarity index 100%
rename from examples/fun3d_examples/pyopt_togw_optimization/images/crm_aero_struct_geom.png
rename to examples/fun3d_examples/archive/pyopt_togw_optimization/images/crm_aero_struct_geom.png
diff --git a/examples/fun3d_examples/pyopt_togw_optimization/images/crm_thick_opt.png b/examples/fun3d_examples/archive/pyopt_togw_optimization/images/crm_thick_opt.png
similarity index 100%
rename from examples/fun3d_examples/pyopt_togw_optimization/images/crm_thick_opt.png
rename to examples/fun3d_examples/archive/pyopt_togw_optimization/images/crm_thick_opt.png
diff --git a/examples/fun3d_examples/pyopt_togw_optimization/pyopt_togw.py b/examples/fun3d_examples/archive/pyopt_togw_optimization/pyopt_togw.py
similarity index 100%
rename from examples/fun3d_examples/pyopt_togw_optimization/pyopt_togw.py
rename to examples/fun3d_examples/archive/pyopt_togw_optimization/pyopt_togw.py
diff --git a/examples/fun3d_examples/pyopt_togw_optimization/tacs_model.py b/examples/fun3d_examples/archive/pyopt_togw_optimization/tacs_model.py
similarity index 100%
rename from examples/fun3d_examples/pyopt_togw_optimization/tacs_model.py
rename to examples/fun3d_examples/archive/pyopt_togw_optimization/tacs_model.py
diff --git a/examples/fun3d_examples/sst_unsteady/unsteady_forward.py b/examples/fun3d_examples/archive/sst_unsteady/unsteady_forward.py
similarity index 100%
rename from examples/fun3d_examples/sst_unsteady/unsteady_forward.py
rename to examples/fun3d_examples/archive/sst_unsteady/unsteady_forward.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/struct/build_exploded_mesh.py b/examples/fun3d_examples/diamond/.gitkeep
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/struct/build_exploded_mesh.py
rename to examples/fun3d_examples/diamond/.gitkeep
diff --git a/examples/fun3d_examples/diamond/wedge_optimization/README.md b/examples/fun3d_examples/diamond/wedge_optimization/README.md
new file mode 100644
index 00000000..088d71ea
--- /dev/null
+++ b/examples/fun3d_examples/diamond/wedge_optimization/README.md
@@ -0,0 +1,75 @@
+# Diamond Wedge — Aerothermoelastic Optimization
+
+Steady aerothermoelastic optimization of a supersonic diamond wedge panel. Minimizes average structural temperature subject to a mass constraint using a fully coupled FUN3D + TACS analysis.
+
+## Flow Conditions
+
+| Parameter | Value |
+|-----------|-------|
+| Mach | 0.5 |
+| V∞ | 148.67 m/s |
+| ρ∞ | 0.01037 kg/m³ |
+| q∞ | ~114.7 Pa |
+| T_ref | 300 K |
+| Wedge half-angle | 5° |
+
+## Design Variables
+
+10 panel thickness variables parameterized via a quadratic Bernstein basis (3 optimizer variables mapped to 10 TACS design variables in `gen_TACS_bdf_aero.py`).
+
+## Objective and Constraints
+
+- **Objective**: minimize average structural temperature
+- **Constraint**: structural mass ≤ 40 kg
+
+## Directory Layout
+
+```
+wedge_optimization/
+├── cfd/ # FUN3D mesh and input files
+├── struct/ # TACS BDF mesh (generated by gen_TACS_bdf_aero.py)
+├── design/ # Optimization history and design variable files
+├── gen_TACS_bdf_aero.py # Generates tacs_aero.bdf in struct/
+└── wedge_optimization.py # Main optimization script
+```
+
+## Dependencies
+
+- FUN3D
+- TACS
+- mpi4py
+- pyoptsparse (SNOPT)
+
+## CFD Mesh
+
+No mesh is committed to this repository. The FUN3D mesh must be generated separately and placed in `cfd/`.
+
+**Geometry**: a 5° half-angle diamond wedge. The aerodynamic surface (FUN3D boundary tag 1) is the inclined upper face of the wedge:
+- Chord: x ∈ [1.0, 2.0] m (leading edge at x = 1 m, trailing edge at x = 2 m)
+- Span: y ∈ [−0.5, 0.5] m
+- Surface normal tilted 5° from vertical (matching the structural panel rotation in `gen_TACS_bdf_aero.py`)
+
+**Suggested mesh approach** using a structured mesh tool (e.g., Pointwise, ICEM CFD, or CGNS-based scripting):
+
+1. Define the wedge surface as a planar quad patch rotated 5° about the y-axis, with the leading edge at (1, ±0.5, 0) and trailing edge at (2, ±0.5, 0).
+2. Extrude a C-type or H-type block outward to a farfield boundary roughly 10–20 chord lengths away.
+3. Apply boundary conditions:
+ - `wall` (tag 1): the inclined wedge surface
+ - `farfield`: all remaining outer faces
+ - `symmetry` at y = ±0.5 m if exploiting span periodicity (optional)
+4. Export as a FUN3D-compatible unstructured grid (`.ugrid` or `.lb8.ugrid`) and place it in `cfd/` along with the FUN3D `input.nml`.
+
+For a quick inviscid mesh, a coarse H-block with ~50×20×20 cells is sufficient to demonstrate the coupled analysis. For a viscous case, add wall-normal clustering to achieve y⁺ ≈ 1 at the wall.
+
+## Run Order
+
+```bash
+# 1. Generate the structural mesh
+python gen_TACS_bdf_aero.py
+
+# 2. Generate the FUN3D CFD mesh (see CFD Mesh section above)
+# and place the .ugrid and input.nml in cfd/
+
+# 3. Run the coupled optimization
+mpiexec -n python wedge_optimization.py
+```
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/5_shape_and_struct.py b/examples/fun3d_examples/diamond/wedge_optimization/cfd/steady/.gitkeep
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/5_shape_and_struct.py
rename to examples/fun3d_examples/diamond/wedge_optimization/cfd/steady/.gitkeep
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/6_shape_and_aero.py b/examples/fun3d_examples/diamond/wedge_optimization/design/.gitkeep
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/6_shape_and_aero.py
rename to examples/fun3d_examples/diamond/wedge_optimization/design/.gitkeep
diff --git a/examples/fun3d_examples/diamond/wedge_optimization/gen_TACS_bdf_aero.py b/examples/fun3d_examples/diamond/wedge_optimization/gen_TACS_bdf_aero.py
new file mode 100644
index 00000000..a6b7c69d
--- /dev/null
+++ b/examples/fun3d_examples/diamond/wedge_optimization/gen_TACS_bdf_aero.py
@@ -0,0 +1,151 @@
+"""
+gen_TACS_bdf_aero.py
+
+Generates the TACS BDF mesh for the diamond wedge aerothermoelastic example.
+Writes struct/tacs_aero.bdf relative to this script's directory.
+"""
+
+import numpy as np
+import os
+
+here = os.path.dirname(os.path.abspath(__file__))
+struct_dir = os.path.join(here, "struct")
+os.makedirs(struct_dir, exist_ok=True)
+
+nx = 11
+ny = 5
+nz = 5
+
+x_min = 1.0
+x_max = 2.0 # m
+y_min = -0.5
+y_max = 0.5 # m
+
+z_min = -0.015
+z_max = -0.005
+
+x = np.linspace(x_min, x_max, num=nx)
+y = np.linspace(y_min, y_max, num=ny)
+z = np.linspace(z_min, z_max, num=nz)
+theta = np.radians(5.0)
+nodes = np.arange(1, nx * ny * nz + 1, dtype=np.int64).reshape(nx, ny, nz)
+
+fp = open(os.path.join(struct_dir, "tacs_aero.bdf"), "w")
+fp.write("$ Input file for a rectangular plate\n")
+fp.write("SOL 103\nCEND\nBEGIN BULK\n")
+
+spclist = []
+spclistY = []
+spclistT = []
+# Write the grid points to a file
+for k in range(nz):
+ for j in range(ny):
+ for i in range(nx):
+ # Write the nodal data
+ spc = " "
+ coord_disp = 0
+ coord_id = 0
+ seid = 0
+
+ xpt = np.cos(theta) * x[i] - np.sin(theta) * z[k] + 1.0
+ ypt = y[j]
+ zpt = np.sin(theta) * x[i] + np.cos(theta) * z[k]
+
+ fp.write(
+ "%-8s%16d%16d%16.9e%16.9e* \n"
+ % ("GRID*", nodes[i, j, k], coord_id, xpt, ypt)
+ )
+ fp.write(
+ "* %16.9e%16d%16s%16d \n" % (zpt, coord_disp, spc, seid)
+ )
+
+ # If the node is on one of the bottom outer edges of the plate,
+ # restrain it against displacement in any direction
+ if k == 0 and (i == 0 or i == nx - 1):
+ spclist.append(nodes[i, j, k])
+
+ # If the node is on one of the outer edges of the plate,
+ # restrain it against displacement in y direction
+ if k != 0 and (j == 0 or j == ny - 1):
+ spclistY.append(nodes[i, j, k])
+
+ # Set the temperature along the bottom edge of the plate
+ if k == 0:
+ spclistT.append(nodes[i, j, k])
+
+# Write out the linear hexahedral elements
+elem = 1
+for k in range(0, nodes.shape[2] - 1, 1):
+ for j in range(0, nodes.shape[1] - 1, 1):
+ for i in range(0, nodes.shape[0] - 1, 1):
+ # Set different part numbers for the elements on the
+ # lower and volume mesh
+ part_id = i + 1
+ if k == 0:
+ part_id = i + nodes.shape[0]
+ # Write the connectivity data
+ fp.write(
+ "%-8s%8d%8d%8d%8d%8d%8d%8d%8d\n%-8s%8d%8d\n"
+ % (
+ "CHEXA",
+ elem,
+ part_id,
+ nodes[i, j, k],
+ nodes[i + 1, j, k],
+ nodes[i + 1, j + 1, k],
+ nodes[i, j + 1, k],
+ nodes[i, j, k + 1],
+ nodes[i + 1, j, k + 1],
+ "*",
+ nodes[i + 1, j + 1, k + 1],
+ nodes[i, j + 1, k + 1],
+ )
+ )
+ elem += 1
+
+# # Add an extra layer of hexa elements and add the tractions through the
+# # TACSTraction3D class.
+# part_id = 2
+# k0 = 0
+# for j in range(0, nodes.shape[1]-1, 1):
+# for i in range(0, nodes.shape[0]-1, 1):
+# # Write the connectivity data
+# fp.write('%-8s%8d%8d%8d%8d%8d%8d%8d%8d\n%-8s%8d%8d\n'%
+# ('CHEXA', elem, part_id,
+# nodes[i, j, k0], nodes[i+1, j ,k0],
+# nodes[i+1, j+1, k0], nodes[i, j+1, k0],
+# nodes[i, j, k0+1], nodes[i+1, j, k0+1], '*',
+# nodes[i+1, j+1, k0+1], nodes[i, j+1, k0+1]))
+# elem += 1
+
+# Note: This approach will not work because the CQUAD4 pressure elements are not
+# yet implemented in TACS.
+#
+# # Write out the elements associated with the back-pressure. These are quads and
+# # are only associated with the back side of the structure
+# k0 = 0 # Set the plane of nodes where we will apply the surface traction
+# part_id = 2 # Set a different part id
+# for i in range(0, nodes.shape[0]-1, 1):
+# for j in range(0, nodes.shape[1]-1, 1):
+# n = [nodes[i, j, k0], nodes[i+1, j, k0],
+# nodes[i+1, j+1, k0], nodes[i, j+1, k0]]
+# # Note that the orientation of the element is reversed so that the normal
+# # points in the -z direction
+# fp.write('%-8s%8d%8d%8d%8d%8d%8d\n'%
+# ('CQUAD4', elem, part_id, n[0], n[3], n[2], n[1]))
+# elem += 1
+
+for node in spclist:
+ spc = "123"
+ fp.write("%-8s%8d%8d%8s%8.6f\n" % ("SPC", 1, node, spc, 0.0))
+
+for node in spclistY:
+ spc = "2"
+ fp.write("%-8s%8d%8d%8s%8.6f\n" % ("SPC", 1, node, spc, 0.0))
+
+for node in spclistT:
+ spc = "4"
+ fp.write("%-8s%8d%8d%8s%8.4f\n" % ("SPC", 1, node, spc, 300.0))
+
+fp.write("END BULK")
+fp.close()
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/7_kitchen_sink.py b/examples/fun3d_examples/diamond/wedge_optimization/struct/.gitkeep
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/7_kitchen_sink.py
rename to examples/fun3d_examples/diamond/wedge_optimization/struct/.gitkeep
diff --git a/examples/fun3d_examples/diamond/wedge_optimization/wedge_optimization.py b/examples/fun3d_examples/diamond/wedge_optimization/wedge_optimization.py
new file mode 100644
index 00000000..b7fcfd94
--- /dev/null
+++ b/examples/fun3d_examples/diamond/wedge_optimization/wedge_optimization.py
@@ -0,0 +1,138 @@
+"""
+wedge_optimization.py
+
+Steady aerothermoelastic optimization of a supersonic diamond wedge panel.
+Minimizes average structural temperature subject to a mass constraint,
+using a fully coupled FUN3D + TACS analysis.
+
+Run with:
+ mpiexec -n python wedge_optimization.py
+
+Prerequisites:
+ 1. Generate the TACS BDF mesh: python gen_TACS_bdf_aero.py
+ (writes tacs_aero.bdf into struct/)
+ 2. Place FUN3D mesh and input files in cfd/
+"""
+
+from pyoptsparse import SNOPT, Optimization
+from funtofem import *
+from mpi4py import MPI
+import os
+
+comm = MPI.COMM_WORLD
+base_dir = os.path.dirname(os.path.abspath(__file__))
+
+# Optimization flags
+hot_start = False
+store_history = True
+
+# Freestream / reference quantities
+v_inf = 1962.44 / 6.6 * 0.5 # ~148.67 m/s, Mach 0.5
+rho = 0.01037 # kg/m^3
+q_inf = 0.5 * rho * v_inf**2 # dynamic pressure, N/m^2
+thermal_scale = 0.5 * rho * v_inf**3 # heat flux * area, J/s
+T_ref = 300.0 # reference / freestream temperature, K
+
+maximum_mass = 40.0 # mass constraint upper bound, kg
+
+# FUNTOFEM MODEL
+# -------------------------------------------------------
+
+f2f_model = FUNtoFEMmodel("wedge")
+
+plate = Body.aerothermoelastic("plate", boundary=1)
+
+# 10 panel thickness design variables
+for i in range(10):
+ Variable.structural(f"thickness {i}", value=0.5).set_bounds(
+ lower=1e-5, upper=1e4
+ ).register_to(plate)
+
+plate.register_to(f2f_model)
+
+# SCENARIO
+# -------------------------------------------------------
+
+steady = Scenario.steady("steady", steps=100)
+steady.set_temperature(T_ref=T_ref, T_inf=T_ref)
+steady.set_flow_ref_vals(qinf=q_inf)
+
+Function.temperature().optimize(
+ scale=1.0, objective=True, plot=True, plot_name="temperature"
+).register_to(steady)
+
+Function.mass().optimize(
+ scale=1.0, upper=maximum_mass, objective=False, plot=True, plot_name="mass"
+).register_to(steady)
+
+steady.register_to(f2f_model)
+
+# DISCIPLINE INTERFACES AND DRIVER
+# -------------------------------------------------------
+
+bdf_file = os.path.join(base_dir, "struct", "tacs_aero.bdf")
+
+solvers = SolverManager(comm)
+solvers.flow = Fun3dInterface(
+ comm,
+ f2f_model,
+ fun3d_dir="cfd",
+ forward_tolerance=1e-6,
+ adjoint_tolerance=1e-6,
+)
+solvers.structural = TacsSteadyInterface.create_from_bdf(
+ model=f2f_model,
+ comm=comm,
+ nprocs=1,
+ bdf_file=bdf_file,
+ prefix=os.path.join(base_dir, "struct"),
+)
+
+transfer_settings = TransferSettings(
+ elastic_scheme="meld",
+ thermal_scheme="meld",
+ npts=10,
+ beta=10.0,
+ isym=-1,
+)
+
+f2f_driver = FUNtoFEMnlbgs(
+ solvers=solvers,
+ transfer_settings=transfer_settings,
+ model=f2f_model,
+)
+
+# PYOPTSPARSE OPTIMIZATION
+# -------------------------------------------------------
+
+design_out_file = os.path.join(base_dir, "design", "wedge.txt")
+
+design_folder = os.path.join(base_dir, "design")
+if comm.rank == 0 and not os.path.exists(design_folder):
+ os.mkdir(design_folder)
+
+history_file = os.path.join(design_folder, "wedge.hst")
+store_history_file = history_file if store_history else None
+hot_start_file = history_file if hot_start else None
+
+manager = OptimizationManager(
+ f2f_driver,
+ design_out_file=design_out_file,
+ hot_start=hot_start,
+ hot_start_file=hot_start_file,
+)
+
+opt_problem = Optimization("wedgeOpt", manager.eval_functions)
+manager.register_to_problem(opt_problem)
+
+snoptimizer = SNOPT(options={"Major Optimality tol": 1e-6, "IPRINT": 1})
+
+sol = snoptimizer(
+ opt_problem,
+ sens=manager.eval_gradients,
+ storeHistory=store_history_file,
+ hotStart=hot_start_file,
+)
+
+if comm.rank == 0:
+ print(f"Final solution = {sol.xStar}", flush=True)
diff --git a/examples/fun3d_examples/sst/README.md b/examples/fun3d_examples/sst/README.md
new file mode 100644
index 00000000..98a9046d
--- /dev/null
+++ b/examples/fun3d_examples/sst/README.md
@@ -0,0 +1,24 @@
+# Supersonic Transport Wing (SST)
+
+The Supersonic Transport Wing (SST) is a realistic supersonic aircraft wing geometry (based on the HSCT configuration) used to demonstrate high-fidelity aerothermoelastic optimization with FUN3D, TACS, and CAPS/ESP. The optimization workflow couples aerodynamic heating, structural deformation, and shape design variables to minimize take-off gross weight subject to structural failure constraints at Mach 2.5 cruise conditions.
+
+## Examples
+
+- **[`sst_optimization/`](sst_optimization/)** — Complete multi-step aerothermoelastic optimization workflow. Includes mesh generation, one-way structural sizing, internal structural shape optimization, and fully coupled aeroelastic TOGW minimization with remeshing. See its [README](sst_optimization/README.md) for the full workflow description and run order.
+
+## Reference
+
+Engelstad, S. P., Burke, B. J., Patel, R. N., Sahu, S., and Kennedy, G. J.,
+"High-Fidelity Aerothermoelastic Optimization with Differentiable CAD Geometry,"
+*AIAA Scitech 2023 Forum*, National Harbor, MD, 2023.
+doi:10.2514/6.2023-0329 ()
+
+## Results
+
+Optimized SST wing structural design:
+
+
+
+FUN3D flow solution over the SST wing:
+
+
diff --git a/examples/fun3d_examples/sst_optimization/results/sst_fun3d_flow.png b/examples/fun3d_examples/sst/results/sst_fun3d_flow.png
similarity index 100%
rename from examples/fun3d_examples/sst_optimization/results/sst_fun3d_flow.png
rename to examples/fun3d_examples/sst/results/sst_fun3d_flow.png
diff --git a/examples/fun3d_examples/sst_optimization/results/sst_opt_design.png b/examples/fun3d_examples/sst/results/sst_opt_design.png
similarity index 100%
rename from examples/fun3d_examples/sst_optimization/results/sst_opt_design.png
rename to examples/fun3d_examples/sst/results/sst_opt_design.png
diff --git a/examples/fun3d_examples/sst/sst_optimization/1_sizing_optimization.py b/examples/fun3d_examples/sst/sst_optimization/1_sizing_optimization.py
new file mode 100644
index 00000000..36348811
--- /dev/null
+++ b/examples/fun3d_examples/sst/sst_optimization/1_sizing_optimization.py
@@ -0,0 +1,216 @@
+"""
+Sean P. Engelstad, Georgia Tech 2023
+
+Run a FUN3D analysis with the Fun3dOnewayDriver
+then with those aero loads still in the F2F Body object
+determine the optimal panel thicknesses using oneway-coupled
+structural optimization in TACS.
+
+NOTE: you need to run _mesh_fun3d.py first and move the .ugrid
+FUN3D mesh into the meshes/turbulent folder first.
+"""
+
+from pyoptsparse import SNOPT, Optimization
+
+# script inputs
+hot_start = False
+store_history = True
+
+# import openmdao.api as om
+from funtofem import *
+from mpi4py import MPI
+from tacs import caps2tacs
+import os
+
+comm = MPI.COMM_WORLD
+
+base_dir = os.path.dirname(os.path.abspath(__file__))
+csm_path = os.path.join(base_dir, "geometry", "sst_v2.csm")
+
+# F2F MODEL and SHAPE MODELS
+# ----------------------------------------
+
+f2f_model = FUNtoFEMmodel("sst-sizing")
+tacs_model = caps2tacs.TacsModel.build(
+ csm_file=csm_path, comm=comm, problem_name="capsStruct1"
+)
+tacs_model.mesh_aim.set_mesh( # need a refined-enough mesh for the derivative test to pass
+ edge_pt_min=2,
+ edge_pt_max=20,
+ global_mesh_size=0.3,
+ max_surf_offset=0.2,
+ max_dihedral_angle=15,
+).register_to(
+ tacs_model
+)
+f2f_model.structural = tacs_model
+
+tacs_aim = tacs_model.tacs_aim
+tacs_aim.set_config_parameter("mode:flow", 0)
+tacs_aim.set_config_parameter("mode:struct", 1)
+
+if comm.rank == 0:
+ aim = tacs_model.mesh_aim.aim
+ aim.input.Mesh_Sizing = {
+ "horizX": {"numEdgePoints": 20},
+ "horizY": {"numEdgePoints": 8},
+ "vert": {"numEdgePoints": 4},
+ }
+
+# add tacs constraints in
+caps2tacs.PinConstraint("root").register_to(tacs_model)
+caps2tacs.PinConstraint("wingFuse").register_to(tacs_model)
+caps2tacs.TemperatureConstraint("midplane").register_to(tacs_model)
+
+# BODIES AND STRUCT DVs
+# -------------------------------------------------
+
+wing = Body.aerothermoelastic("wing", boundary=4)
+
+# setup the material and shell properties
+titanium_alloy = caps2tacs.Isotropic.titanium_alloy().register_to(tacs_model)
+
+nribs = int(tacs_model.get_config_parameter("wing:nribs"))
+nspars = int(tacs_model.get_config_parameter("wing:nspars"))
+nOML = int(tacs_aim.get_output_parameter("wing:nOML"))
+
+for irib in range(1, nribs + 1):
+ name = f"rib{irib}"
+ prop = caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.04
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.01).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+for ispar in range(1, nspars + 1):
+ name = f"spar{ispar}"
+ prop = caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.04
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.01).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+for iOML in range(1, nOML + 1):
+ name = f"OML{iOML}"
+ prop = caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.04
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.01).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+for prefix in ["LE", "TE"]:
+ name = f"{prefix}spar"
+ prop = caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.04
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.01).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+# register the wing body to the model
+wing.register_to(f2f_model)
+
+# INITIAL STRUCTURE MESH, SINCE NO STRUCT SHAPE VARS
+# --------------------------------------------------
+
+tacs_aim.setup_aim()
+tacs_aim.pre_analysis()
+
+# SCENARIOS
+# ----------------------------------------------------
+
+# make a funtofem scenario
+climb = Scenario.steady("climb_inviscid", steps=350, uncoupled_steps=200) # 2000
+mass = Function.mass().optimize(
+ scale=1.0e-4, objective=True, plot=True, plot_name="mass"
+)
+ksfailure = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0, upper=1.0, objective=False, plot=True, plot_name="ks-climb"
+)
+climb.include(mass).include(ksfailure)
+climb.set_temperature(T_ref=216, T_inf=216)
+climb.set_flow_ref_vals(qinf=3.16e4)
+climb.register_to(f2f_model)
+
+# COMPOSITE FUNCTIONS
+# -------------------------------------------------------
+
+# skin thickness adjacency constraints
+variables = f2f_model.get_variables()
+section_prefix = ["rib", "OML"]
+section_nums = [nribs, nOML]
+for isection, prefix in enumerate(section_prefix):
+ section_num = section_nums[isection]
+ for iconstr in range(1, section_num):
+ left_var = f2f_model.get_variables(names=f"{prefix}{iconstr}")
+ right_var = f2f_model.get_variables(names=f"{prefix}{iconstr+1}")
+ adj_constr = (left_var - right_var) / left_var
+ adj_ratio = 0.15
+ adj_constr.set_name(f"{prefix}{iconstr}-{iconstr+1}").optimize(
+ lower=-adj_ratio, upper=adj_ratio, scale=1.0, objective=False
+ ).register_to(f2f_model)
+
+# DISCIPLINE INTERFACES AND DRIVERS
+# -----------------------------------------------------
+
+solvers = SolverManager(comm)
+solvers.flow = Fun3dInterface(comm, f2f_model, fun3d_dir="cfd")
+solvers.structural = TacsSteadyInterface.create_from_bdf(
+ model=f2f_model,
+ comm=comm,
+ nprocs=20,
+ bdf_file=tacs_aim.dat_file_path,
+ prefix=tacs_aim.analysis_dir,
+)
+
+my_transfer_settings = TransferSettings(npts=200)
+fun3d_driver = OnewayAeroDriver(
+ solvers, f2f_model, transfer_settings=my_transfer_settings
+)
+
+# build the shape driver from the file
+tacs_driver = OnewayStructDriver.prime_loads(fun3d_driver)
+
+# PYOPTSPARSE OPTMIZATION
+# -------------------------------------------------------------
+
+# create an OptimizationManager object for the pyoptsparse optimization problem
+design_out_file = os.path.join(base_dir, "design", "sizing.txt")
+
+# reload previous design
+f2f_model.read_design_variables_file(comm, design_out_file)
+
+manager = OptimizationManager(
+ tacs_driver, design_out_file=design_out_file, hot_start=hot_start
+)
+
+# create the pyoptsparse optimization problem
+opt_problem = Optimization("hsctOpt", manager.eval_functions)
+
+# add funtofem model variables to pyoptsparse
+manager.register_to_problem(opt_problem)
+
+# run an SNOPT optimization
+snoptimizer = SNOPT(options={"IPRINT": 1})
+
+design_folder = os.path.join(base_dir, "design")
+if not os.path.exists(design_folder):
+ os.mkdir(design_folder)
+history_file = os.path.join(design_folder, "sizing.hst")
+store_history_file = history_file if store_history else None
+hot_start_file = history_file if hot_start else None
+
+
+sol = snoptimizer(
+ opt_problem,
+ sens=manager.eval_gradients,
+ storeHistory=store_history_file,
+ hotStart=hot_start_file,
+)
+
+# print final solution
+sol_xdict = sol.xStar
+print(f"Final solution = {sol_xdict}", flush=True)
diff --git a/examples/fun3d_examples/sst/sst_optimization/2_sizing_shape.py b/examples/fun3d_examples/sst/sst_optimization/2_sizing_shape.py
new file mode 100644
index 00000000..323657e2
--- /dev/null
+++ b/examples/fun3d_examples/sst/sst_optimization/2_sizing_shape.py
@@ -0,0 +1,235 @@
+"""
+Sean P. Engelstad, Georgia Tech 2023
+
+Run a FUN3D analysis with the Fun3dOnewayDriver
+then with those aero loads still in the F2F Body object
+determine the optimal panel thicknesses using oneway-coupled
+structural optimization in TACS.
+
+NOTE: you need to run _mesh_fun3d.py first and move the .ugrid
+FUN3D mesh into the meshes/turbulent folder first.
+"""
+
+from pyoptsparse import SNOPT, Optimization
+
+# script inputs
+hot_start = False
+store_history = True
+
+# import openmdao.api as om
+from funtofem import *
+from mpi4py import MPI
+from tacs import caps2tacs
+import os
+
+comm = MPI.COMM_WORLD
+
+base_dir = os.path.dirname(os.path.abspath(__file__))
+csm_path = os.path.join(base_dir, "geometry", "sst_v2.csm")
+
+# F2F MODEL and SHAPE MODELS
+# ----------------------------------------
+
+f2f_model = FUNtoFEMmodel("sst-sizing-shape")
+tacs_model = caps2tacs.TacsModel.build(
+ csm_file=csm_path,
+ comm=comm,
+ problem_name="capsStruct2",
+ active_procs=[_ for _ in range(4)],
+ verbosity=1,
+)
+tacs_model.mesh_aim.set_mesh( # need a refined-enough mesh for the derivative test to pass
+ edge_pt_min=2,
+ edge_pt_max=20,
+ global_mesh_size=0.3,
+ max_surf_offset=0.2,
+ max_dihedral_angle=15,
+).register_to(
+ tacs_model
+)
+f2f_model.structural = tacs_model
+
+tacs_aim = tacs_model.tacs_aim
+tacs_aim.set_config_parameter("mode:flow", 0)
+tacs_aim.set_config_parameter("mode:struct", 1)
+
+for proc in tacs_aim.active_procs:
+ if comm.rank == proc:
+ aim = tacs_model.mesh_aim.aim
+ aim.input.Mesh_Sizing = {
+ "horizX": {"numEdgePoints": 20},
+ "horizY": {"numEdgePoints": 8},
+ "vert": {"numEdgePoints": 4},
+ }
+
+# add tacs constraints in
+caps2tacs.PinConstraint("root").register_to(tacs_model)
+caps2tacs.PinConstraint("wingFuse").register_to(tacs_model)
+caps2tacs.TemperatureConstraint("midplane").register_to(tacs_model)
+
+# BODIES AND STRUCT DVs
+# -------------------------------------------------
+
+wing = Body.aerothermoelastic("wing", boundary=4)
+
+# setup the material and shell properties
+titanium_alloy = caps2tacs.Isotropic.titanium_alloy().register_to(tacs_model)
+
+nribs = int(tacs_model.get_config_parameter("wing:nribs"))
+nspars = int(tacs_model.get_config_parameter("wing:nspars"))
+nOML = int(tacs_aim.get_output_parameter("wing:nOML"))
+
+for irib in range(1, nribs + 1):
+ name = f"rib{irib}"
+ prop = caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.04
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.01).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+for ispar in range(1, nspars + 1):
+ name = f"spar{ispar}"
+ prop = caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.04
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.01).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+for iOML in range(1, nOML + 1):
+ name = f"OML{iOML}"
+ prop = caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.04
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.01).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+for prefix in ["LE", "TE"]:
+ name = f"{prefix}spar"
+ prop = caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.04
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.01).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+# structural shape variables
+for prefix in ["rib", "spar"]:
+ Variable.shape(f"wing:{prefix}_a1", value=1.0).set_bounds(
+ lower=0.65, upper=1.35
+ ).register_to(wing)
+ Variable.shape(f"wing:{prefix}_a2", value=0.0).set_bounds(
+ lower=-0.3, upper=0.3
+ ).register_to(wing)
+
+# register the wing body to the model
+wing.register_to(f2f_model)
+
+# INITIAL STRUCTURE MESH, SINCE NO STRUCT SHAPE VARS
+# --------------------------------------------------
+
+tacs_aim.setup_aim()
+
+# SCENARIOS
+# ----------------------------------------------------
+
+# make a funtofem scenario
+climb = Scenario.steady("climb_inviscid", steps=350, uncoupled_steps=200) # 2000
+mass = Function.mass().optimize(
+ scale=1.0e-4, objective=True, plot=True, plot_name="mass"
+)
+ksfailure = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0, upper=1.0, objective=False, plot=True, plot_name="ks-climb"
+)
+climb.include(mass).include(ksfailure)
+climb.set_temperature(T_ref=300, T_inf=300)
+climb.set_flow_ref_vals(qinf=3.28e4)
+climb.register_to(f2f_model)
+
+# COMPOSITE FUNCTIONS
+# -------------------------------------------------------
+
+# skin thickness adjacency constraints
+variables = f2f_model.get_variables()
+section_prefix = ["rib", "OML"]
+section_nums = [nribs, nOML]
+for isection, prefix in enumerate(section_prefix):
+ section_num = section_nums[isection]
+ for iconstr in range(1, section_num):
+ left_var = f2f_model.get_variables(names=f"{prefix}{iconstr}")
+ right_var = f2f_model.get_variables(names=f"{prefix}{iconstr+1}")
+ adj_constr = (left_var - right_var) / left_var
+ adj_ratio = 0.15
+ adj_constr.set_name(f"{prefix}{iconstr}-{iconstr+1}").optimize(
+ lower=-adj_ratio, upper=adj_ratio, scale=1.0, objective=False
+ ).register_to(f2f_model)
+
+# DISCIPLINE INTERFACES AND DRIVERS
+# -----------------------------------------------------
+
+solvers = SolverManager(comm)
+solvers.flow = Fun3dInterface(comm, f2f_model, fun3d_dir="cfd")
+# solvers.structural = TacsSteadyInterface.create_from_bdf(
+# model=f2f_model,
+# comm=comm,
+# nprocs=20,
+# bdf_file=tacs_aim.dat_file_path,
+# prefix=tacs_aim.analysis_dir,
+# )
+
+my_transfer_settings = TransferSettings(npts=200)
+fun3d_driver = OnewayAeroDriver(
+ solvers, f2f_model, transfer_settings=my_transfer_settings, external_shape=True
+)
+
+# build the shape driver from the file
+tacs_driver = OnewayStructDriver.prime_loads(fun3d_driver, nprocs=10)
+
+# write an aero loads file
+aero_loads_file = os.path.join(os.getcwd(), "cfd", "uncoupled_loads.txt")
+f2f_model.write_aero_loads(comm, aero_loads_file)
+
+# PYOPTSPARSE OPTMIZATION
+# -------------------------------------------------------------
+
+# create an OptimizationManager object for the pyoptsparse optimization problem
+# design_in_file = os.path.join(base_dir, "design", "sizing.txt")
+design_in_file = os.path.join(base_dir, "design", "internal-struct.txt")
+design_out_file = os.path.join(base_dir, "design", "internal-struct.txt")
+
+# reload previous design
+f2f_model.read_design_variables_file(comm, design_in_file)
+
+manager = OptimizationManager(
+ tacs_driver, design_out_file=design_out_file, hot_start=hot_start
+)
+
+# create the pyoptsparse optimization problem
+opt_problem = Optimization("hsctOpt", manager.eval_functions)
+
+# add funtofem model variables to pyoptsparse
+manager.register_to_problem(opt_problem)
+
+# run an SNOPT optimization
+snoptimizer = SNOPT(options={"IPRINT": 1})
+
+design_folder = os.path.join(base_dir, "design")
+if not os.path.exists(design_folder):
+ os.mkdir(design_folder)
+history_file = os.path.join(design_folder, "sizing.hst")
+store_history_file = history_file if store_history else None
+hot_start_file = history_file if hot_start else None
+
+
+sol = snoptimizer(
+ opt_problem,
+ sens=manager.eval_gradients,
+ storeHistory=store_history_file,
+ hotStart=hot_start_file,
+)
+
+# print final solution
+sol_xdict = sol.xStar
+print(f"Final solution = {sol_xdict}", flush=True)
diff --git a/examples/fun3d_examples/sst/sst_optimization/3_eval_inviscid.py b/examples/fun3d_examples/sst/sst_optimization/3_eval_inviscid.py
new file mode 100644
index 00000000..08782368
--- /dev/null
+++ b/examples/fun3d_examples/sst/sst_optimization/3_eval_inviscid.py
@@ -0,0 +1,389 @@
+"""
+Sean P. Engelstad, Georgia Tech 2023
+
+This runs the fully coupled aeroelastic, inviscid forward analysis.
+It prints out the aerostructural functionals for you to manually improve the design before full
+optimization.
+"""
+
+import os
+from mpi4py import MPI
+from funtofem import *
+from tacs import caps2tacs
+
+comm = MPI.COMM_WORLD
+base_dir = os.path.dirname(os.path.abspath(__file__))
+fun3d_dir = os.path.join(base_dir, "cfd")
+tacs_dir = os.path.join(base_dir, "struct")
+csm_path = os.path.join(base_dir, "geometry", "sst_v2.csm")
+
+# FUNtoFEM and SHAPE MODELS
+# ---------------------------------------------------------
+hsct_model = FUNtoFEMmodel("sst-inviscid")
+
+# design the FUN3D aero shape model
+flow_model = Fun3dModel.build(
+ csm_file=csm_path, comm=comm, project_name="sst-inviscid", problem_name="capsFluid3"
+)
+m_aflr_aim = flow_model.aflr_aim
+
+flow_aim = flow_model.fun3d_aim
+flow_aim.set_config_parameter("mode:flow", 1)
+flow_aim.set_config_parameter("mode:struct", 0)
+
+# min_scale has the greatest effect on mesh size (scale that to affect # mesh elements)
+m_aflr_aim.set_surface_mesh(
+ ff_growth=1.4, mesh_length=6.0, min_scale=0.006, max_scale=0.5, use_quads=True
+)
+Fun3dBC.inviscid(caps_group="wall").register_to(flow_model)
+Fun3dBC.inviscid(caps_group="staticWall").register_to(flow_model)
+Fun3dBC.SymmetryY(caps_group="SymmetryY").register_to(flow_model)
+Fun3dBC.Farfield(caps_group="Farfield").register_to(flow_model)
+
+# refine bottom of fuselage edge otherwise it's too coarse there and causes bad mesh quality + divergence
+if comm.rank == flow_model.root:
+ aim = aflr_aim.surface_aim
+ aim.input.Mesh_Sizing = {"botFuse": {"scaleFactor": 0.1}}
+
+flow_model.setup()
+hsct_model.flow = flow_model
+
+# design the TACS struct shape model
+tacs_model = caps2tacs.TacsModel.build(
+ csm_file=csm_path, comm=comm, problem_name="capsStruct3"
+)
+tacs_model.mesh_aim.set_mesh( # need a refined-enough mesh for the derivative test to pass
+ edge_pt_min=2,
+ edge_pt_max=20,
+ global_mesh_size=0.3,
+ max_surf_offset=0.2,
+ max_dihedral_angle=15,
+).register_to(
+ tacs_model
+)
+tacs_aim = tacs_model.tacs_aim
+tacs_aim.set_config_parameter("mode:flow", 0)
+tacs_aim.set_config_parameter("mode:struct", 1)
+wing_area = tacs_aim.get_output_parameter("wing:area")
+
+for proc in tacs_aim.active_procs:
+ if comm.rank == proc:
+ aim = tacs_model.mesh_aim.aim
+ aim.input.Mesh_Sizing = {
+ "horizX": {"numEdgePoints": 20},
+ "horizY": {"numEdgePoints": 8},
+ "vert": {"numEdgePoints": 4},
+ }
+hsct_model.structural = tacs_model
+
+# tacs model constraints
+caps2tacs.PinConstraint("root").register_to(tacs_model)
+caps2tacs.PinConstraint("wingFuse").register_to(tacs_model)
+caps2tacs.TemperatureConstraint("midplane").register_to(tacs_model)
+
+# BODIES, STRUCT DVs and SHAPE DVs
+# ---------------------------------------------------
+wing = Body.aeroelastic("wing", boundary=4)
+wing.relaxation(AitkenRelaxation())
+
+# setup the material and shell properties
+titanium_alloy = caps2tacs.Isotropic.titanium_alloy().register_to(tacs_model)
+
+nribs = int(tacs_model.get_config_parameter("wing:nribs"))
+nspars = int(tacs_model.get_config_parameter("wing:nspars"))
+nOML = int(tacs_aim.get_output_parameter("wing:nOML"))
+
+for irib in range(1, nribs + 1):
+ name = f"rib{irib}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.05
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for ispar in range(1, nspars + 1):
+ name = f"spar{ispar}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.05
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for iOML in range(1, nOML + 1):
+ name = f"OML{iOML}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.03
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for name in ["LEspar", "TEspar"]:
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.03
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+# structural shape variables
+for prefix in ["rib", "spar"]:
+ Variable.shape(f"wing:{prefix}_a1", value=1.0).set_bounds(
+ lower=0.6, upper=1.4
+ ).register_to(wing)
+ Variable.shape(f"wing:{prefix}_a2", value=0.0).set_bounds(
+ lower=-0.3, upper=0.3
+ ).register_to(wing)
+
+# wing size and shape variables
+c1 = Variable.shape("wing:c1", value=41.2).set_bounds(lower=30.0, upper=50.0)
+c2 = Variable.shape("wing:c2", value=28.862).set_bounds(lower=20.0, upper=40.0)
+c3 = Variable.shape("wing:c3", value=18.554).set_bounds(lower=13.0, upper=25.0)
+c4 = Variable.shape("wing:c4", value=7.422).set_bounds(lower=5.0, upper=15.0)
+cbars = [c1, c2, c3, c4]
+
+coffset1 = Variable.shape("wing:cbar_offset1", value=0.0).set_bounds(
+ lower=-0.3, upper=0.3
+)
+coffset2 = Variable.shape("wing:cbar_offset2", value=0.0).set_bounds(
+ lower=-0.3, upper=0.3
+)
+coffset3 = Variable.shape("wing:cbar_offset3", value=0.0).set_bounds(
+ lower=-0.3, upper=0.3
+)
+coffsets = [coffset1, coffset2, coffset3]
+
+dzhat1 = Variable.shape("wing:dzhat1", value=0.05).set_bounds(lower=0.0, upper=0.3)
+dzhat2 = Variable.shape("wing:dzhat2", value=0.2).set_bounds(lower=0.0, upper=0.6)
+dz_dihedral = Variable.shape("wing:dz_dihedral", value=-5.0).set_bounds(
+ lower=-7.0, upper=0.0
+)
+dzs = [dzhat1, dzhat2, dz_dihedral]
+
+wing_aspect = Variable.shape("wing:aspect", value=4.0).set_bounds(lower=2.0, upper=6.0)
+wing_span_fr1 = Variable.shape("wing:span_fr1", value=0.2).set_bounds(
+ lower=0.1, upper=0.3
+)
+wing_span_fr2 = Variable.shape("wing:span_fr2", value=0.3).set_bounds(
+ lower=0.2, upper=0.5
+)
+other_wing_vars = [wing_aspect, wing_span_fr1, wing_span_fr2]
+
+for var in cbars + coffsets + dzs + other_wing_vars:
+ var.register_to(wing)
+
+# TODO : add wing airfoil shape variables
+# TODO : add fuselage and tail shape variables
+
+wing.register_to(hsct_model)
+
+# SCENARIOS, AERO DVs, and remaining SHAPE VARS
+# -----------------------------------------------
+
+# NOTE: shape variables can be assigned to the body or scenario
+# when using ESP/CAPS, it doesn't really matter
+_climb_qinf = 3.28e4
+
+climb = Scenario.steady("climb_inviscid", steps=650, uncoupled_steps=150)
+climb.set_stop_criterion(early_stopping=True)
+climb.fun3d_project(flow_aim.project_name)
+climb.set_temperature(T_ref=300.0, T_inf=300.0) # modify this
+climb.set_flow_ref_vals(qinf=_climb_qinf, flow_dt=1.0)
+ksfailure_climb = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0, upper=1.0, objective=False, plot=True
+)
+cl_climb = Function.lift(body=0)
+cd_climb = Function.drag(body=0)
+aoa_climb = climb.get_variable("AOA").set_bounds(lower=0.0, value=4.0, upper=8.0)
+mach_climb = climb.get_variable("Mach").set_bounds(lower=0.3, value=0.55, upper=0.9)
+for func in [ksfailure_climb, cl_climb, cd_climb]:
+ func.register_to(climb)
+climb.register_to(hsct_model)
+
+# cruise flight condition
+# altitude - 60 kft, ,
+_mach_cruise = 2.5
+_ainf_cruise = 295 # m/s
+_rho_inf_cruise = 0.1165 # kg / m^3
+# _mu_cruise = 1.42e-5 # kg/(m-s)
+_aoa_cruise = 2.0
+_Tinf_cruise = 216 # K
+_vinf_cruise = _mach_cruise * _ainf_cruise
+_qinf_cruise = 0.5 * _rho_inf_cruise * _vinf_cruise**2
+
+cruise = Scenario.steady("cruise_inviscid", steps=350, uncoupled_steps=200)
+cruise.set_stop_criterion(early_stopping=True)
+cruise.fun3d_project(flow_aim.project_name)
+cruise.set_temperature(T_ref=_Tinf_cruise, T_inf=_Tinf_cruise)
+cruise.set_flow_ref_vals(qinf=_qinf_cruise, flow_dt=1.0)
+ksfailure_cruise = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0, upper=1.0, objective=False, plot=True
+)
+cl_cruise = Function.lift(body=0)
+cd_cruise = Function.drag(body=0)
+moment = Function.moment(body=0).optimize(
+ lower=0.0, upper=0.0, scale=1.0, objective=False, plot=True
+)
+wing_mass = Function.mass()
+aoa_cruise = cruise.get_variable("AOA").set_bounds(
+ lower=1.0, value=_aoa_cruise, upper=4.0
+)
+mach_cruise = cruise.get_variable("Mach").set_bounds(
+ lower=2.3, value=_mach_cruise, upper=2.7
+)
+for func in [ksfailure_cruise, moment, wing_mass, cl_cruise, cd_cruise]:
+ func.register_to(cruise)
+cruise.register_to(hsct_model)
+
+# COMPOSITE FUNCTIONS
+# -----------------------------------------
+
+# TOGW
+g = 9.81 # m/s^2
+lb_to_N = 4.448 # N/lb
+tsfc = 3.9e-5 # kg/N/s, Rolls Royce Olympus 593 engine
+fuselage_tail_weight = 6e5 # N, 60000 kg
+fuel_reserve_fraction = 0.06
+num_passengers = 250
+passenger_weight = 230 * num_passengers * lb_to_N
+crew_weight = (450 + 5 * num_passengers) * lb_to_N
+# descent_fuel = 6000 * lb_to_N # N, fixed based on NASA report
+
+wing_weight = 2 * wing_mass * g # m/s^2 => N, doubled for sym
+empty_weight = wing_weight + fuselage_tail_weight
+boarded_weight = empty_weight + passenger_weight + crew_weight
+
+# previously cl, cd used area=1 so don't need to multiply this area
+cruise_lift = 2 * cl_cruise * _qinf_cruise
+cruise_drag = 2 * cd_cruise * _qinf_cruise
+
+takeoff_weight_ratio = 0.97
+climb_weight_ratio = 0.985
+land_weight_ratio = 0.995
+rem_weight_ratios = takeoff_weight_ratio * climb_weight_ratio * land_weight_ratio
+
+_range = 12800 # km
+_range *= 1e3 # to m
+cruise_LoverD = cruise_lift / cruise_drag
+cruise_weight_ratio = CompositeFunction.exp(
+ -_range * tsfc / _vinf_cruise / cruise_LoverD
+)
+
+mission_weight_ratio = rem_weight_ratios * cruise_weight_ratio
+fuel_weight_ratio = 1.06 * (1 - mission_weight_ratio) # 6% reserve fuel
+togw = boarded_weight / (1 - fuel_weight_ratio)
+togw.set_name("takeoff-gross-weight").optimize( # kg
+ lower=2e5, upper=3e5, scale=1.0, objective=True
+).register_to(hsct_model)
+
+# steady flight and climb conditions
+cruise_weight = togw * 0.5 * (1 + cruise_weight_ratio)
+steady_cruise = cruise_lift / cruise_weight
+steady_cruise.set_name("steady_cruise").optimize(
+ lower=1.0, upper=1.0, scale=1.0, objective=False
+).register_to(hsct_model)
+
+# previously we used area = 1
+climb_lift = 2 * cl_climb * _climb_qinf
+steady_climb = climb_lift / togw
+steady_climb.set_name("steady_climb").optimize(
+ lower=1.5, upper=2.5, scale=1.0, objective=False
+).register_to(hsct_model)
+
+# adjacency skin thickness constraints (for structures discipline)
+variables = hsct_model.get_variables()
+adj_scale = 10.0
+section_prefix = ["rib", "OML"]
+section_nums = [nribs, nOML]
+for isection, prefix in enumerate(section_prefix):
+ section_num = section_nums[isection]
+ for iconstr in range(1, section_num):
+ left_var = hsct_model.get_variables(names=f"{prefix}{iconstr}")
+ right_var = hsct_model.get_variables(names=f"{prefix}{iconstr+1}")
+ adj_constr = (left_var - right_var) / left_var
+ adj_ratio = 0.15
+ adj_constr.set_name(f"{prefix}{iconstr}-{iconstr+1}").optimize(
+ lower=-adj_ratio, upper=adj_ratio, scale=1.0, objective=False
+ ).register_to(hsct_model)
+
+# measure the actual lift, drag of each scenario (properly normalized)
+half_area = wing_area * 0.5
+(cl_cruise / half_area).set_name("CL_cruise_ND").optimize(objective=False).register_to(
+ hsct_model
+)
+(cd_cruise / half_area).set_name("CD_cruise_ND").optimize(objective=False).register_to(
+ hsct_model
+)
+(cl_climb / half_area).set_name("CL_climb_ND").optimize(objective=False).register_to(
+ hsct_model
+)
+(cd_climb / half_area).set_name("CD_climb_ND").optimize(objective=False).register_to(
+ hsct_model
+)
+
+# measure dimensional lift, drag of each scenario
+
+(2 * cl_cruise * _qinf_cruise).set_name("CL_cruise_Dim").optimize(
+ objective=False
+).register_to(hsct_model)
+(2 * cd_cruise * _qinf_cruise).set_name("CD_cruise_Dim").optimize(
+ objective=False
+).register_to(hsct_model)
+(2 * cl_climb * _climb_qinf).set_name("CL_climb_Dim").optimize(
+ objective=False
+).register_to(hsct_model)
+(2 * cd_climb * _climb_qinf).set_name("CD_climb_Dim").optimize(
+ objective=False
+).register_to(hsct_model)
+
+# BUILD THE DRIVER. NO DISCIPLINE INTERFACES IN DRIVER SCRIPT
+# ------------------------------------------------------
+
+# load in the previous design from the sizing optimization
+# to overwrite the initial values
+sizing_file = os.path.join(base_dir, "design", "internal-struct.txt") # sizing.txt
+hsct_model.read_design_variables_file(comm, sizing_file)
+
+# build the solvers and coupled driver
+solvers = SolverManager(comm)
+solvers.flow = Fun3dInterface(
+ comm,
+ hsct_model,
+ fun3d_dir="cfd",
+ auto_coords=False,
+ forward_tolerance=1e-8,
+ adjoint_tolerance=1e-8,
+)
+# solvers.structural will be built by the shape driver at runtime
+
+# build the driver and run a forward analysis
+# ----------------------------------------------------------------------------
+transfer_settings = TransferSettings(
+ elastic_scheme="meld", thermal_scheme="meld", isym=1, npts=200
+)
+f2f_driver = FuntofemShapeDriver.aero_morph(
+ solvers, hsct_model, transfer_settings=transfer_settings, struct_nprocs=48
+)
+f2f_driver.solve_forward()
+
+# write the coupled aero loads file
+aero_loads_file = os.path.join(os.getcwd(), "cfd", "coupled_loads.txt")
+hsct_model.write_aero_loads(comm, aero_loads_file)
+
+if comm.rank == 0:
+ print(f"Debug func printout")
+ print("------------------------------------\n", flush=True)
+ for func in hsct_model.get_functions():
+ print(f"\t func {func.full_name} = {func.value.real}")
+ print("------------------------------------\n", flush=True)
+
+# eval composite functions and report all function values to the user
+hsct_model.evaluate_composite_functions(compute_grad=False)
+
+if comm.rank == 0:
+ print(
+ f"{hsct_model.name} Model, Inviscid Aeroelastic Forward analysis results...\n"
+ )
+ print("------------------------------------\n", flush=True)
+ for func in hsct_model.get_functions(all=True):
+ print(f"\t func {func.full_name} = {func.value.real}")
+ print("------------------------------------\n", flush=True)
diff --git a/examples/fun3d_examples/sst/sst_optimization/4_eval_turb.py b/examples/fun3d_examples/sst/sst_optimization/4_eval_turb.py
new file mode 100644
index 00000000..558aed3b
--- /dev/null
+++ b/examples/fun3d_examples/sst/sst_optimization/4_eval_turb.py
@@ -0,0 +1,372 @@
+"""
+Sean P. Engelstad, Georgia Tech 2023
+
+This runs the fully coupled aerothermoelastic, turbulent forward analysis.
+It prints out the aerostructural functionals for you to manually improve the design before full
+optimization.
+"""
+
+# TBD this one still needs to be made properly
+import os
+from mpi4py import MPI
+from funtofem import *
+from tacs import caps2tacs
+
+comm = MPI.COMM_WORLD
+base_dir = os.path.dirname(os.path.abspath(__file__))
+fun3d_dir = os.path.join(base_dir, "cfd")
+tacs_dir = os.path.join(base_dir, "struct")
+csm_path = os.path.join(base_dir, "geometry", "sst_v2.csm")
+
+# FUNtoFEM and SHAPE MODELS
+# ---------------------------------------------------------
+hsct_model = FUNtoFEMmodel("sst-turbulent")
+
+# design the FUN3D aero shape model
+flow_model = Fun3dModel.build(csm_file=csm_path, comm=comm, project_name="sst-turb")
+m_aflr_aim = flow_model.aflr_aim
+
+flow_aim = flow_model.fun3d_aim
+flow_aim.set_config_parameter("mode:flow", 1)
+flow_aim.set_config_parameter("mode:struct", 0)
+
+# min_scale has the greatest effect on mesh size (scale that to affect # mesh elements)
+aflr_aim.set_surface_mesh(
+ ff_growth=1.4, mesh_length=8.0, min_scale=0.02, max_scale=0.5, use_quads=True
+)
+Fun3dBC.viscous(caps_group="wall", wall_spacing=1e-4).register_to(flow_model)
+Fun3dBC.viscous(caps_group="staticWall", wall_spacing=1e-4).register_to(flow_model)
+Fun3dBC.SymmetryY(caps_group="SymmetryY").register_to(flow_model)
+Fun3dBC.Farfield(caps_group="Farfield").register_to(flow_model)
+flow_model.setup()
+hsct_model.flow = flow_model
+
+# design the TACS struct shape model
+tacs_model = caps2tacs.TacsModel.build(
+ csm_file=csm_path, comm=comm, problem_name="capsStruct4"
+)
+tacs_model.mesh_aim.set_mesh( # need a refined-enough mesh for the derivative test to pass
+ edge_pt_min=2,
+ edge_pt_max=20,
+ global_mesh_size=0.3,
+ max_surf_offset=0.2,
+ max_dihedral_angle=15,
+).register_to(
+ tacs_model
+)
+tacs_aim = tacs_model.tacs_aim
+tacs_aim.set_config_parameter("mode:flow", 0)
+tacs_aim.set_config_parameter("mode:struct", 1)
+for proc in tacs_aim.active_procs:
+ if comm.rank == proc:
+ aim = tacs_model.mesh_aim.aim
+ aim.input.Mesh_Sizing = {
+ "horizX": {"numEdgePoints": 20},
+ "horizY": {"numEdgePoints": 8},
+ "vert": {"numEdgePoints": 4},
+ }
+hsct_model.structural = tacs_model
+
+# tacs model constraints
+caps2tacs.PinConstraint("root").register_to(tacs_model)
+caps2tacs.PinConstraint("wingFuse").register_to(tacs_model)
+caps2tacs.TemperatureConstraint("midplane").register_to(tacs_model)
+
+# BODIES, STRUCT DVs and SHAPE DVs
+# ---------------------------------------------------
+wing = Body.aerothermoelastic("wing", boundary=4)
+wing.relaxation(AitkenRelaxation())
+
+# setup the material and shell properties
+titanium_alloy = caps2tacs.Isotropic.titanium_alloy().register_to(tacs_model)
+
+nribs = int(tacs_model.get_config_parameter("wing:nribs"))
+nspars = int(tacs_model.get_config_parameter("wing:nspars"))
+nOML = int(tacs_aim.get_output_parameter("wing:nOML"))
+
+for irib in range(1, nribs + 1):
+ name = f"rib{irib}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.05
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for ispar in range(1, nspars + 1):
+ name = f"spar{ispar}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.05
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for iOML in range(1, nOML + 1):
+ name = f"OML{iOML}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.03
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for name in ["LEspar", "TEspar"]:
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.03
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+# structural shape variables
+for prefix in ["rib", "spar"]:
+ Variable.shape(f"wing:{prefix}_a1", value=1.0).set_bounds(
+ lower=0.6, upper=1.4
+ ).register_to(wing)
+ Variable.shape(f"wing:{prefix}_a2", value=0.0).set_bounds(
+ lower=-0.3, upper=0.3
+ ).register_to(wing)
+
+# wing size and shape variables
+cbar1 = Variable.shape("wing:cbar1", value=2.0).set_bounds(lower=1.5, upper=2.5)
+cbar2 = Variable.shape("wing:cbar2", value=1.4).set_bounds(lower=1.0, upper=2.0)
+cbar3 = Variable.shape("wing:cbar3", value=0.9).set_bounds(lower=0.5, upper=1.5)
+cbars = [cbar1, cbar2, cbar3]
+
+coffset1 = Variable.shape("wing:cbar_offset1", value=0.0).set_bounds(
+ lower=-0.3, upper=0.3
+)
+coffset2 = Variable.shape("wing:cbar_offset2", value=0.0).set_bounds(
+ lower=-0.3, upper=0.3
+)
+coffset3 = Variable.shape("wing:cbar_offset3", value=0.0).set_bounds(
+ lower=-0.3, upper=0.3
+)
+coffsets = [coffset1, coffset2, coffset3]
+
+dzhat1 = Variable.shape("wing:dzhat1", value=0.05).set_bounds(lower=0.0, upper=0.3)
+dzhat2 = Variable.shape("wing:dzhat2", value=0.2).set_bounds(lower=0.0, upper=0.6)
+dz_dihedral = Variable.shape("wing:dz_dihedral", value=-5.0).set_bounds(
+ lower=-7.0, upper=0.0
+)
+dzs = [dzhat1, dzhat2, dz_dihedral]
+
+wing_area = Variable.shape("wing:area", value=700).set_bounds(lower=500, upper=800)
+wing_aspect = Variable.shape("wing:aspect", value=7.0).set_bounds(lower=4.0, upper=8.0)
+wing_span_fr1 = Variable.shape("wing:span_fr1", value=0.2).set_bounds(
+ lower=0.1, upper=0.3
+)
+wing_span_fr2 = Variable.shape("wing:span_fr2", value=0.3).set_bounds(
+ lower=0.2, upper=0.5
+)
+other_wing_vars = [wing_area, wing_aspect, wing_span_fr1, wing_span_fr2]
+
+for var in cbars + coffsets + dzs + other_wing_vars:
+ var.register_to(wing)
+
+# TODO : add wing airfoil shape variables
+# TODO : add fuselage and tail shape variables
+
+wing.register_to(hsct_model)
+
+# SCENARIOS, AERO DVs, and remaining SHAPE VARS
+# -----------------------------------------------
+
+# NOTE: shape variables can be assigned to the body or scenario
+# when using ESP/CAPS, it doesn't really matter
+
+# climb flight condition
+_climb_qinf = 3.28e4
+
+climb = Scenario.steady("climb", steps=350, preconditioner_steps=200)
+climb.fun3d_project(flow_aim.project_name)
+climb.set_temperature(T_ref=300.0, T_inf=300.0) # modify this
+climb.set_flow_ref_vals(qinf=_climb_qinf, flow_dt=1.0)
+ksfailure_climb = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0, upper=1.0, objective=False, plot=True
+)
+cl_climb = Function.lift(body=0)
+cd_climb = Function.drag(body=0)
+aoa_climb = climb.get_variable("AOA").set_bounds(lower=0.0, value=3.0, upper=10.0)
+mach_climb = climb.get_variable("Mach").set_bounds(lower=0.5, value=0.7, upper=0.9)
+for func in [ksfailure_climb, cl_climb, cd_climb]:
+ func.register_to(climb)
+climb.register_to(hsct_model)
+
+# cruise flight condition
+# altitude - 60 kft, ,
+_mach_cruise = 2.5
+_ainf_cruise = 295 # m/s
+_rho_inf_cruise = 0.1165 # kg / m^3
+# _mu_cruise = 1.42e-5 # kg/(m-s)
+_aoa_cruise = 2.0
+_Tinf_cruise = 216 # K
+_vinf_cruise = _mach_cruise * _ainf_cruise
+_qinf_cruise = 0.5 * _rho_inf_cruise * _vinf_cruise**2
+
+cruise = Scenario.steady("cruise", steps=350, preconditioner_steps=200)
+cruise.fun3d_project(flow_aim.project_name)
+cruise.set_temperature(T_ref=_Tinf_cruise, T_inf=_Tinf_cruise)
+cruise.set_flow_ref_vals(qinf=_qinf_cruise, flow_dt=1.0)
+ksfailure_cruise = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0, upper=1.0, objective=False, plot=True
+)
+cl_cruise = Function.lift(body=0)
+cd_cruise = Function.drag(body=0)
+moment = Function.moment(body=0).optimize(
+ lower=0.0, upper=0.0, scale=1.0, objective=False, plot=True
+)
+wing_mass = Function.mass()
+aoa_cruise = cruise.get_variable("AOA").set_bounds(
+ lower=1.0, value=_aoa_cruise, upper=4.0
+)
+mach_cruise = cruise.get_variable("Mach").set_bounds(
+ lower=2.3, value=_mach_cruise, upper=2.7
+)
+for func in [ksfailure_cruise, moment, wing_mass, cl_cruise, cd_cruise]:
+ func.register_to(cruise)
+cruise.register_to(hsct_model)
+
+# COMPOSITE FUNCTIONS
+# -----------------------------------------
+
+# TOGW
+g = 9.81 # m/s^2
+lb_to_N = 4.448 # N/lb
+tsfc = 3.9e-5 # kg/N/s, Rolls Royce Olympus 593 engine
+fuselage_tail_weight = 6e5 # N
+fuel_reserve_fraction = 0.06
+num_passengers = 300
+passenger_weight = 230 * num_passengers * lb_to_N
+crew_weight = (450 + 5 * num_passengers) * lb_to_N
+# descent_fuel = 6000 * lb_to_N # N, fixed based on NASA report
+
+wing_weight = 2 * wing_mass * g # m/s^2 => N, doubled for sym
+empty_weight = wing_weight + fuselage_tail_weight
+boarded_weight = empty_weight + passenger_weight + crew_weight
+
+cruise_lift = cl_cruise * _qinf_cruise
+cruise_drag = cd_cruise * _qinf_cruise
+
+takeoff_weight_ratio = 0.97
+climb_weight_ratio = 0.985
+land_weight_ratio = 0.995
+rem_weight_ratios = takeoff_weight_ratio * climb_weight_ratio * land_weight_ratio
+
+_range = 12800 # km
+_range *= 1e3 # to m
+cruise_LoverD = cruise_lift / cruise_drag
+cruise_weight_ratio = CompositeFunction.exp(
+ -_range * tsfc / _vinf_cruise / cruise_LoverD
+)
+
+mission_weight_ratio = rem_weight_ratios * cruise_weight_ratio
+fuel_weight_ratio = 1.06 * (1 - mission_weight_ratio) # 6% reserve fuel
+togw = boarded_weight / (1 - fuel_weight_ratio)
+togw.set_name("takeoff-gross-weight").optimize( # kg
+ lower=2e5, upper=3e5, scale=1.0, objective=True
+).register_to(hsct_model)
+
+# steady flight and climb conditions
+cruise_weight = togw * 0.5 * (1 + cruise_weight_ratio)
+steady_cruise = cruise_lift / cruise_weight
+steady_cruise.set_name("steady_cruise").optimize(
+ lower=1.0, upper=1.0, scale=1.0, objective=False
+).register_to(hsct_model)
+climb_lift = cl_climb * _climb_qinf
+steady_climb = climb_lift / togw
+steady_climb.set_name("steady_climb").optimize(
+ lower=1.5, upper=2.5, scale=1.0, objective=False
+).register_to(hsct_model)
+
+# feasible wing chord constraints => prevent negative sectional chord length
+wing_span_fr3 = 1 - wing_span_fr1 - wing_span_fr2
+# normalized chord at tip = chord_tip / chord_mean
+chord_tip_hat = (
+ 2
+ - (cbar1 + cbar2) * wing_span_fr1
+ - (cbar2 + cbar3) * wing_span_fr2
+ - cbar3 * wing_span_fr3
+) / wing_span_fr3
+chord_tip_hat.set_name("chord_tip*").optimize(
+ lower=0.0, objective=False, scale=1.0
+).register_to(hsct_model)
+
+
+# adjacency skin thickness constraints
+variables = hsct_model.get_variables()
+adj_scale = 10.0
+section_prefix = ["rib", "OML"]
+section_nums = [nribs, nOML]
+for isection, prefix in enumerate(section_prefix):
+ section_num = section_nums[isection]
+ for iconstr in range(1, section_num):
+ left_var = hsct_model.get_variables(names=f"{prefix}{iconstr}")
+ right_var = hsct_model.get_variables(names=f"{prefix}{iconstr+1}")
+ adj_constr = (left_var - right_var) / left_var
+ adj_ratio = 0.15
+ adj_constr.set_name(f"{prefix}{iconstr}-{iconstr+1}").optimize(
+ lower=-adj_ratio, upper=adj_ratio, scale=1.0, objective=False
+ ).register_to(hsct_model)
+
+# measure the actual lift, drag of each scenario (properly normalized)
+half_area = wing_area * 0.5
+(cl_cruise / half_area).set_name("CL_cruise_ND").optimize(objective=False).register_to(
+ hsct_model
+)
+(cd_cruise / half_area).set_name("CD_cruise_ND").optimize(objective=False).register_to(
+ hsct_model
+)
+(cl_climb / half_area).set_name("CL_climb_ND").optimize(objective=False).register_to(
+ hsct_model
+)
+(cd_climb / half_area).set_name("CD_climb_ND").optimize(objective=False).register_to(
+ hsct_model
+)
+
+# measure dimensional lift, drag of each scenario
+
+(cl_cruise * _qinf_cruise).set_name("CL_cruise_Dim").optimize(
+ objective=False
+).register_to(hsct_model)
+(cd_cruise * _qinf_cruise).set_name("CD_cruise_Dim").optimize(
+ objective=False
+).register_to(hsct_model)
+(cl_climb * _climb_qinf).set_name("CL_climb_Dim").optimize(objective=False).register_to(
+ hsct_model
+)
+(cd_climb * _climb_qinf).set_name("CD_climb_Dim").optimize(objective=False).register_to(
+ hsct_model
+)
+
+# BUILD THE DRIVER. NO DISCIPLINE INTERFACES IN DRIVER SCRIPT
+# ------------------------------------------------------
+
+# load in the previous design from the inviscid-AE design
+sizing_file = os.path.join(base_dir, "design", "inviscid-ae.txt")
+hsct_model.read_design_variables_file(comm, sizing_file)
+
+# build the solvers and coupled driver
+solvers = SolverManager(comm)
+solvers.flow = Fun3dInterface(comm, hsct_model, fun3d_dir="cfd", auto_coords=False)
+# solvers.structural will be built by the shape driver at runtime
+
+# build the driver and run a forward analysis
+# ----------------------------------------------------------------------------
+transfer_settings = TransferSettings(
+ elastic_scheme="meld", thermal_scheme="meld", isym=1, npts=200
+)
+f2f_driver = FuntofemShapeDriver.aero_morph(
+ solvers, hsct_model, transfer_settings=transfer_settings, struct_nprocs=48
+)
+f2f_driver.solve_forward()
+
+# eval composite functions and report all function values to the user
+hsct_model.evaluate_composite_functions(compute_grad=False)
+
+if comm.rank == 0:
+ print(
+ f"{hsct_model.name} Model, Turbulent Aerothermoelastic Forward analysis results...\n"
+ )
+ print("------------------------------------\n", flush=True)
+ for func in hsct_model.get_functions(all=True):
+ print(f"\t func {func.full_name} = {func.value.real}")
+ print("------------------------------------\n", flush=True)
diff --git a/examples/fun3d_examples/sst/sst_optimization/5_fc_inviscid_ae_remesh.py b/examples/fun3d_examples/sst/sst_optimization/5_fc_inviscid_ae_remesh.py
new file mode 100644
index 00000000..e95030ab
--- /dev/null
+++ b/examples/fun3d_examples/sst/sst_optimization/5_fc_inviscid_ae_remesh.py
@@ -0,0 +1,352 @@
+"""
+Sean P. Engelstad, Georgia Tech 2023
+
+This is the fully coupled aeroelastic, inviscid optimization of the HSCT.
+NOTE: You need to run the 1_sizing_optimization.py and 2_sizing_shape.py or local version
+first and leave the optimal panel thickness design variables in the meshes folder before running this.
+
+We no longer perform the mesh generation in this script as then we can't use parallel tacsAIMs
+NOTE : don't call this script with mpiexec_mpt, call it with python (otherwise system calls won't work)
+"""
+
+from pyoptsparse import SNOPT, Optimization
+import os
+import numpy as np
+from mpi4py import MPI
+from funtofem import *
+
+# script inputs
+hot_start = True
+store_history = True
+optimize_trim = False
+eval_loads = False
+
+comm = MPI.COMM_WORLD
+base_dir = os.path.dirname(os.path.abspath(__file__))
+fun3d_dir = os.path.join(base_dir, "cfd")
+analysis_file = os.path.join(base_dir, "_run_inviscid_ae.py")
+
+# FUNtoFEM and SHAPE MODELS
+# ---------------------------------------------------------
+hsct_model = FUNtoFEMmodel("sst-inviscid")
+
+# BODIES, STRUCT DVs and SHAPE DVs
+# ---------------------------------------------------
+wing = Body.aeroelastic("wing", boundary=4)
+
+nribs = 25
+nspars = 3
+nOML = nribs - 1
+for irib in range(1, nribs + 1):
+ Variable.structural(f"rib{irib}", value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for ispar in range(1, nspars + 1):
+ Variable.structural(f"spar{ispar}", value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for iOML in range(1, nOML + 1):
+ Variable.structural(f"OML{iOML}", value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for name in ["LEspar", "TEspar"]:
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+# structural shape variables
+# for prefix in ["rib", "spar"]:
+# Variable.shape(f"wing:{prefix}_a1", value=1.0).set_bounds(
+# lower=0.6, upper=1.4
+# ).register_to(wing)
+# Variable.shape(f"wing:{prefix}_a2", value=0.0).set_bounds(
+# lower=-0.3, upper=0.3
+# ).register_to(wing)
+
+# wing size and shape variables
+c1 = Variable.shape("wing:c1", value=41.2).set_bounds(lower=30.0, upper=50.0)
+c2 = Variable.shape("wing:c2", value=28.862).set_bounds(lower=20.0, upper=40.0)
+c3 = Variable.shape("wing:c3", value=18.554).set_bounds(lower=13.0, upper=25.0)
+c4 = Variable.shape("wing:c4", value=7.422).set_bounds(lower=5.0, upper=15.0)
+cbars = [c1, c2, c3, c4]
+
+# coffset1 = Variable.shape("wing:cbar_offset1", value=0.0).set_bounds(
+# lower=-0.1, upper=0.1
+# )
+# coffset2 = Variable.shape("wing:cbar_offset2", value=0.0).set_bounds(
+# lower=-0.1, upper=0.1
+# )
+# coffset3 = Variable.shape("wing:cbar_offset3", value=0.0).set_bounds(
+# lower=-0.1, upper=0.1
+# )
+# coffsets = [coffset1, coffset2, coffset3]
+
+# dzhat1 = Variable.shape("wing:dzhat1", value=0.05).set_bounds(lower=0.0, upper=0.1)
+dzhat2 = Variable.shape("wing:dzhat2", value=0.2).set_bounds(lower=0.1, upper=0.3)
+dz_dihedral = Variable.shape("wing:dz_dihedral", value=-5.0).set_bounds(
+ lower=-5.1, upper=-2.0
+)
+dzs = [dzhat2, dz_dihedral]
+
+# geometric AOA on wing
+# geomAOA1 = Variable.shape("wing:geomAOA1", value=0.0).set_bounds(lower=-0.5, upper=0.5)
+# geomAOA2 = Variable.shape("wing:geomAOA2", value=0.0).set_bounds(lower=-2.0, upper=2.0)
+# geomAOA3 = Variable.shape("wing:geomAOA3", value=0.0).set_bounds(lower=-2.0, upper=2.0)
+# geomAOA4 = Variable.shape("wing:geomAOA4", value=0.0).set_bounds(lower=-2.0, upper=2.0)
+
+for var in cbars + dzs:
+ var.register_to(wing)
+
+# wing thickness variables
+wing_thick1 = Variable.shape("wing:thick1", value=1.237).set_bounds(
+ lower=1.0, upper=1.4
+)
+wing_thick2 = Variable.shape("wing:thick2", value=1.154).set_bounds(
+ lower=0.8, upper=1.3
+)
+wing_thick3 = Variable.shape("wing:thick3", value=0.742).set_bounds(
+ lower=0.5, upper=0.9
+)
+wing_thick4 = Variable.shape("wing:thick4", value=0.296).set_bounds(
+ lower=0.2, upper=0.5
+)
+for var in [wing_thick1, wing_thick2, wing_thick3, wing_thick4]:
+ var.register_to(wing)
+
+# canard shape variables
+if optimize_trim:
+ canard_area = Variable.shape("canard:area", value=70).set_bounds(
+ lower=40, upper=200
+ )
+ canard_twist = Variable.shape("canard:geomAOA", value=5).set_bounds(
+ lower=-5, upper=8
+ )
+
+ for canard_var in [canard_area, canard_twist]:
+ canard_var.register_to(wing)
+
+wing.register_to(hsct_model)
+# end of wing section
+
+# SCENARIOS, AERO DVs, and remaining SHAPE VARS
+# -----------------------------------------------
+
+# NOTE: shape variables can be assigned to the body or scenario
+# when using ESP/CAPS, it doesn't really matter
+_climb_qinf = 3.28e4
+
+climb = Scenario.steady("climb_inviscid", steps=2, uncoupled_steps=1)
+climb.set_temperature(T_ref=300.0, T_inf=300.0) # modify this
+climb.set_flow_ref_vals(qinf=_climb_qinf, flow_dt=1.0)
+ksfailure_climb = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0,
+ upper=1.0,
+ objective=False,
+ plot=True,
+ plot_name="ks-climb",
+)
+cl_climb = Function.lift(body=0)
+cd_climb = Function.drag(body=0)
+aoa_climb = climb.get_variable("AOA").set_bounds(lower=2.0, value=3.0, upper=8.0)
+mach_climb = climb.get_variable("Mach").set_bounds(lower=0.3, value=0.55, upper=0.9)
+for func in [ksfailure_climb, cl_climb, cd_climb]:
+ func.register_to(climb)
+climb.register_to(hsct_model)
+
+# cruise flight condition
+# altitude - 60 kft, ,
+_mach_cruise = 2.5
+_ainf_cruise = 295 # m/s
+_rho_inf_cruise = 0.1165 # kg / m^3
+# _mu_cruise = 1.42e-5 # kg/(m-s)
+_aoa_cruise = 2.0
+_Tinf_cruise = 216 # K
+_vinf_cruise = _mach_cruise * _ainf_cruise
+_qinf_cruise = 0.5 * _rho_inf_cruise * _vinf_cruise**2
+
+cruise = Scenario.steady("cruise_inviscid", steps=2)
+cruise.set_temperature(T_ref=_Tinf_cruise, T_inf=_Tinf_cruise)
+cruise.set_flow_ref_vals(qinf=_qinf_cruise, flow_dt=1.0)
+ksfailure_cruise = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0,
+ upper=1.0,
+ objective=False,
+ plot=True,
+ plot_name="ks-cruise",
+)
+cl_cruise = Function.lift(body=0)
+cd_cruise = Function.drag(body=0)
+if optimize_trim:
+ moment = Function.moment(body=0).optimize(
+ lower=0.0,
+ upper=0.0,
+ scale=1.0e-3,
+ objective=False,
+ plot=True,
+ plot_name="cm-cruise",
+ )
+wing_mass = Function.mass()
+aoa_cruise = cruise.get_variable("AOA").set_bounds(
+ lower=1.0, value=_aoa_cruise, upper=4.0
+)
+mach_cruise = cruise.get_variable("Mach").set_bounds(
+ lower=2.3, value=_mach_cruise, upper=2.7
+)
+for func in [ksfailure_cruise, wing_mass, cl_cruise, cd_cruise]:
+ func.register_to(cruise)
+if optimize_trim:
+ moment.register_to(cruise)
+cruise.register_to(hsct_model)
+
+# COMPOSITE FUNCTIONS
+# -----------------------------------------
+
+# TOGW
+g = 9.81 # m/s^2
+lb_to_N = 4.448 # N/lb
+tsfc = 3.9e-5 # kg/N/s, Rolls Royce Olympus 593 engine
+fuselage_tail_weight = 5.8e5 # N
+fuel_reserve_fraction = 0.06
+num_passengers = 250
+passenger_weight = 230 * num_passengers * lb_to_N
+crew_weight = (450 + 5 * num_passengers) * lb_to_N
+# descent_fuel = 6000 * lb_to_N # N, fixed based on NASA report
+
+wing_weight = 2 * wing_mass * g # m/s^2 => N, doubled for sym
+empty_weight = wing_weight + fuselage_tail_weight
+boarded_weight = empty_weight + passenger_weight + crew_weight
+
+cruise_lift = 2 * cl_cruise * _qinf_cruise
+cruise_drag = 2 * cd_cruise * _qinf_cruise
+
+takeoff_weight_ratio = 0.97
+climb_weight_ratio = 0.985
+land_weight_ratio = 0.995
+rem_weight_ratios = takeoff_weight_ratio * climb_weight_ratio * land_weight_ratio
+
+_range = 12800 # km
+_range *= 1e3 # to m
+cruise_LoverD = cruise_lift / cruise_drag
+cruise_weight_ratio = CompositeFunction.exp(
+ -_range * tsfc / _vinf_cruise / cruise_LoverD
+)
+
+mission_weight_ratio = rem_weight_ratios * cruise_weight_ratio
+fuel_weight_ratio = 1.06 * (1 - mission_weight_ratio) # 6% reserve fuel
+togw = boarded_weight / (1 - fuel_weight_ratio)
+togw.set_name("takeoff-gross-weight").optimize( # kg
+ scale=1.0e-5, objective=True, plot=True, plot_name="togw"
+).register_to(hsct_model)
+
+# steady flight and climb conditions
+cruise_weight = togw * 0.5 * (1 + cruise_weight_ratio)
+steady_cruise = cruise_lift / cruise_weight
+steady_cruise.set_name("steady_cruise").optimize(
+ lower=1.0,
+ upper=1.0,
+ scale=1.0,
+ objective=False,
+ plot=True,
+ plot_name="steady-cruise",
+).register_to(hsct_model)
+
+# steady climb, prob should add some thrust contribution here
+climb_lift = 2 * cl_climb * _climb_qinf
+steady_climb = climb_lift / togw
+steady_climb.set_name("steady_climb").optimize(
+ lower=1.5,
+ upper=2.5,
+ scale=1.0,
+ objective=False,
+ plot=True,
+ plot_name="steady-climb",
+).register_to(hsct_model)
+
+# adjacency skin thickness constraints (for structures discipline)
+variables = hsct_model.get_variables()
+adj_scale = 10.0
+section_prefix = ["rib", "OML"]
+section_nums = [nribs, nOML]
+for isection, prefix in enumerate(section_prefix):
+ section_num = section_nums[isection]
+ for iconstr in range(1, section_num):
+ left_var = hsct_model.get_variables(names=f"{prefix}{iconstr}")
+ right_var = hsct_model.get_variables(names=f"{prefix}{iconstr+1}")
+ adj_constr = (left_var - right_var) / left_var
+ adj_ratio = 0.15
+ adj_constr.set_name(f"{prefix}{iconstr}-{iconstr+1}").optimize(
+ lower=-adj_ratio, upper=adj_ratio, scale=1.0, objective=False
+ ).register_to(hsct_model)
+
+# BUILD THE DRIVER. NO DISCIPLINE INTERFACES IN DRIVER SCRIPT
+# ------------------------------------------------------
+
+# load in the previous design from the sizing optimization
+# to overwrite the initial values
+# change this to "internal-struct.txt" and add internal struct
+# shape variables in once that is ready
+design_in_file = os.path.join(base_dir, "design", "internal-struct.txt") # sizing.txt
+hsct_model.read_design_variables_file(comm, design_in_file)
+
+# change structural design var lb and ubs to be closer to sizing design
+# want to use improved internal struct design after this
+margin = 0.3
+for var in hsct_model.get_variables():
+ if var.analysis_type == "structural":
+ var.lower = np.max([(1 - margin) * var.value, var.lower])
+ var.upper = np.min([(1 + margin) * var.value, var.upper])
+
+
+# build the solvers and coupled driver
+solvers = SolverManager(comm)
+remote = Remote(comm, analysis_file, fun3d_dir, nprocs=192)
+f2f_driver = FuntofemShapeDriver.aero_remesh(solvers, hsct_model, remote)
+
+if eval_loads:
+ f2f_driver.solve_forward()
+
+ # write the coupled aero loads file
+ aero_loads_file = os.path.join(os.getcwd(), "cfd", "coupled_loads.txt")
+ hsct_model.write_aero_loads(comm, aero_loads_file)
+
+ exit()
+
+# PYOPTSPARSE Optimization
+# ------------------------------------------------------
+
+design_folder = os.path.join(base_dir, "design")
+if not os.path.exists(design_folder):
+ os.mkdir(design_folder)
+history_file = os.path.join(design_folder, "inviscid-ae.hst")
+store_history_file = history_file if store_history else None
+hot_start_file = history_file if hot_start else None
+
+
+# create an OptimizationManager object for the pyoptsparse optimization problem
+design_out_file = os.path.join(base_dir, "design", "inviscid-ae.txt")
+manager = OptimizationManager(
+ f2f_driver,
+ design_out_file=design_out_file,
+ hot_start=hot_start,
+ hot_start_file=hot_start_file,
+)
+
+# create the pyoptsparse optimization problem
+opt_problem = Optimization("sst5", manager.eval_functions)
+
+# add funtofem model variables to pyoptsparse
+manager.register_to_problem(opt_problem)
+
+# run an SNOPT optimization
+snoptimizer = SNOPT(options={"IPRINT": 1})
+
+sol = snoptimizer(
+ opt_problem,
+ sens=manager.eval_gradients,
+ storeHistory=store_history_file,
+ hotStart=hot_start_file,
+)
+
+# print final solution
+sol_xdict = sol.xStar
+print(f"Final solution = {sol_xdict}", flush=True)
diff --git a/examples/fun3d_examples/sst/sst_optimization/6_fc_inviscid_der_test.py b/examples/fun3d_examples/sst/sst_optimization/6_fc_inviscid_der_test.py
new file mode 100644
index 00000000..9291f988
--- /dev/null
+++ b/examples/fun3d_examples/sst/sst_optimization/6_fc_inviscid_der_test.py
@@ -0,0 +1,367 @@
+"""
+Sean P. Engelstad, Georgia Tech 2023
+
+This is the fully coupled aeroelastic, inviscid optimization of the HSCT.
+NOTE: You need to run the 1_sizing_optimization.py and 2_sizing_shape.py or local version
+first and leave the optimal panel thickness design variables in the meshes folder before running this.
+
+We no longer perform the mesh generation in this script as then we can't use parallel tacsAIMs
+NOTE : don't call this script with mpiexec_mpt, call it with python (otherwise system calls won't work)
+"""
+
+from pyoptsparse import SNOPT, Optimization
+import os
+import numpy as np
+from mpi4py import MPI
+from funtofem import *
+
+# script inputs
+hot_start = True
+store_history = True
+optimize_trim = False
+
+comm = MPI.COMM_WORLD
+base_dir = os.path.dirname(os.path.abspath(__file__))
+fun3d_dir = os.path.join(base_dir, "cfd")
+analysis_file = os.path.join(base_dir, "_run_inviscid_ae.py")
+
+# FUNtoFEM and SHAPE MODELS
+# ---------------------------------------------------------
+hsct_model = FUNtoFEMmodel("sst-inviscid")
+
+# BODIES, STRUCT DVs and SHAPE DVs
+# ---------------------------------------------------
+wing = Body.aeroelastic("wing", boundary=4)
+
+nribs = 25
+nspars = 3
+nOML = nribs - 1
+for irib in range(1, nribs + 1):
+ Variable.structural(f"rib{irib}", value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for ispar in range(1, nspars + 1):
+ Variable.structural(f"spar{ispar}", value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for iOML in range(1, nOML + 1):
+ Variable.structural(f"OML{iOML}", value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for name in ["LEspar", "TEspar"]:
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+# structural shape variables
+# for prefix in ["rib", "spar"]:
+# Variable.shape(f"wing:{prefix}_a1", value=1.0).set_bounds(
+# lower=0.6, upper=1.4
+# ).register_to(wing)
+# Variable.shape(f"wing:{prefix}_a2", value=0.0).set_bounds(
+# lower=-0.3, upper=0.3
+# ).register_to(wing)
+
+# wing size and shape variables
+c1 = Variable.shape("wing:c1", value=41.2).set_bounds(lower=30.0, upper=50.0)
+c2 = Variable.shape("wing:c2", value=28.862).set_bounds(lower=20.0, upper=40.0)
+c3 = Variable.shape("wing:c3", value=18.554).set_bounds(lower=13.0, upper=25.0)
+c4 = Variable.shape("wing:c4", value=7.422).set_bounds(lower=5.0, upper=15.0)
+cbars = [c1, c2, c3, c4]
+
+coffset1 = Variable.shape("wing:cbar_offset1", value=0.0).set_bounds(
+ lower=-0.1, upper=0.1
+)
+coffset2 = Variable.shape("wing:cbar_offset2", value=0.0).set_bounds(
+ lower=-0.1, upper=0.1
+)
+coffset3 = Variable.shape("wing:cbar_offset3", value=0.0).set_bounds(
+ lower=-0.1, upper=0.1
+)
+coffsets = [coffset1, coffset2, coffset3]
+
+dzhat1 = Variable.shape("wing:dzhat1", value=0.05).set_bounds(lower=0.0, upper=0.1)
+dzhat2 = Variable.shape("wing:dzhat2", value=0.2).set_bounds(lower=0.1, upper=0.3)
+dz_dihedral = Variable.shape("wing:dz_dihedral", value=-5.0).set_bounds(
+ lower=-7.0, upper=-2.0
+)
+dzs = [dzhat1, dzhat2, dz_dihedral]
+
+# geometric AOA on wing
+# geomAOA1 = Variable.shape("wing:geomAOA1", value=0.0).set_bounds(lower=-0.5, upper=0.5)
+# geomAOA2 = Variable.shape("wing:geomAOA2", value=0.0).set_bounds(lower=-2.0, upper=2.0)
+# geomAOA3 = Variable.shape("wing:geomAOA3", value=0.0).set_bounds(lower=-2.0, upper=2.0)
+# geomAOA4 = Variable.shape("wing:geomAOA4", value=0.0).set_bounds(lower=-2.0, upper=2.0)
+
+for var in cbars + coffsets + dzs:
+ var.register_to(wing)
+
+# wing thickness variables
+wing_thick1 = Variable.shape("wing:thick1", value=1.237).set_bounds(
+ lower=1.0, upper=1.4
+)
+wing_thick2 = Variable.shape("wing:thick2", value=1.154).set_bounds(
+ lower=0.8, upper=1.3
+)
+wing_thick3 = Variable.shape("wing:thick3", value=0.742).set_bounds(
+ lower=0.5, upper=0.9
+)
+wing_thick4 = Variable.shape("wing:thick4", value=0.296).set_bounds(
+ lower=0.2, upper=0.5
+)
+for var in [wing_thick1, wing_thick2, wing_thick3, wing_thick4]:
+ var.register_to(wing)
+
+# canard shape variables
+if optimize_trim:
+ canard_area = Variable.shape("canard:area", value=70).set_bounds(
+ lower=40, upper=200
+ )
+ canard_twist = Variable.shape("canard:geomAOA", value=5).set_bounds(
+ lower=-5, upper=8
+ )
+
+ for canard_var in [canard_area, canard_twist]:
+ canard_var.register_to(wing)
+
+wing.register_to(hsct_model)
+# end of wing section
+
+# SCENARIOS, AERO DVs, and remaining SHAPE VARS
+# -----------------------------------------------
+
+# NOTE: shape variables can be assigned to the body or scenario
+# when using ESP/CAPS, it doesn't really matter
+_climb_qinf = 3.28e4
+
+climb = Scenario.steady("climb_inviscid", steps=350, uncoupled_steps=200)
+climb.set_temperature(T_ref=300.0, T_inf=300.0) # modify this
+climb.set_flow_ref_vals(qinf=_climb_qinf, flow_dt=1.0)
+ksfailure_climb = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0,
+ upper=1.0,
+ objective=False,
+ plot=True,
+ plot_name="ks-climb",
+)
+cl_climb = Function.lift(body=0)
+cd_climb = Function.drag(body=0)
+aoa_climb = climb.get_variable("AOA").set_bounds(lower=0.0, value=4.0, upper=8.0)
+mach_climb = climb.get_variable("Mach").set_bounds(lower=0.5, value=0.7, upper=0.9)
+for func in [ksfailure_climb, cl_climb, cd_climb]:
+ func.register_to(climb)
+climb.register_to(hsct_model)
+
+# cruise flight condition
+# altitude - 60 kft, ,
+_mach_cruise = 2.5
+_ainf_cruise = 295 # m/s
+_rho_inf_cruise = 0.1165 # kg / m^3
+# _mu_cruise = 1.42e-5 # kg/(m-s)
+_aoa_cruise = 2.0
+_Tinf_cruise = 216 # K
+_vinf_cruise = _mach_cruise * _ainf_cruise
+_qinf_cruise = 0.5 * _rho_inf_cruise * _vinf_cruise**2
+
+cruise = Scenario.steady("cruise_inviscid", steps=500)
+cruise.set_temperature(T_ref=_Tinf_cruise, T_inf=_Tinf_cruise)
+cruise.set_flow_ref_vals(qinf=_qinf_cruise, flow_dt=1.0)
+ksfailure_cruise = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0,
+ upper=1.0,
+ objective=False,
+ plot=True,
+ plot_name="ks-cruise",
+)
+cl_cruise = Function.lift(body=0)
+cd_cruise = Function.drag(body=0)
+if optimize_trim:
+ moment = Function.moment(body=0).optimize(
+ lower=0.0,
+ upper=0.0,
+ scale=1.0e-3,
+ objective=False,
+ plot=True,
+ plot_name="cm-cruise",
+ )
+wing_mass = Function.mass()
+aoa_cruise = cruise.get_variable("AOA").set_bounds(
+ lower=1.0, value=_aoa_cruise, upper=4.0
+)
+mach_cruise = cruise.get_variable("Mach").set_bounds(
+ lower=2.3, value=_mach_cruise, upper=2.7
+)
+for func in [ksfailure_cruise, wing_mass, cl_cruise, cd_cruise]:
+ func.register_to(cruise)
+if optimize_trim:
+ moment.register_to(cruise)
+cruise.register_to(hsct_model)
+
+# COMPOSITE FUNCTIONS
+# -----------------------------------------
+
+# TOGW
+g = 9.81 # m/s^2
+lb_to_N = 4.448 # N/lb
+tsfc = 3.9e-5 # kg/N/s, Rolls Royce Olympus 593 engine
+fuselage_tail_weight = 5.8e5 # N
+fuel_reserve_fraction = 0.06
+num_passengers = 250
+passenger_weight = 230 * num_passengers * lb_to_N
+crew_weight = (450 + 5 * num_passengers) * lb_to_N
+# descent_fuel = 6000 * lb_to_N # N, fixed based on NASA report
+
+wing_weight = 2 * wing_mass * g # m/s^2 => N, doubled for sym
+empty_weight = wing_weight + fuselage_tail_weight
+boarded_weight = empty_weight + passenger_weight + crew_weight
+
+cruise_lift = 2 * cl_cruise * _qinf_cruise
+cruise_drag = 2 * cd_cruise * _qinf_cruise
+
+takeoff_weight_ratio = 0.97
+climb_weight_ratio = 0.985
+land_weight_ratio = 0.995
+rem_weight_ratios = takeoff_weight_ratio * climb_weight_ratio * land_weight_ratio
+
+_range = 12800 # km
+_range *= 1e3 # to m
+cruise_LoverD = cruise_lift / cruise_drag
+cruise_weight_ratio = CompositeFunction.exp(
+ -_range * tsfc / _vinf_cruise / cruise_LoverD
+)
+
+mission_weight_ratio = rem_weight_ratios * cruise_weight_ratio
+fuel_weight_ratio = 1.06 * (1 - mission_weight_ratio) # 6% reserve fuel
+togw = boarded_weight / (1 - fuel_weight_ratio)
+togw.set_name("takeoff-gross-weight").optimize( # kg
+ scale=1.0e-5, objective=True, plot=True, plot_name="togw"
+).register_to(hsct_model)
+
+# steady flight and climb conditions
+cruise_weight = togw * 0.5 * (1 + cruise_weight_ratio)
+steady_cruise = cruise_lift / cruise_weight
+steady_cruise.set_name("steady_cruise").optimize(
+ lower=1.0,
+ upper=1.0,
+ scale=1.0,
+ objective=False,
+ plot=True,
+ plot_name="steady-cruise",
+).register_to(hsct_model)
+
+# steady climb, prob should add some thrust contribution here
+climb_factor = 2.0 # was 2.5 before
+climb_lift = 2 * cl_climb * _climb_qinf
+steady_climb = climb_lift - climb_factor * togw
+steady_climb.set_name("steady_climb").optimize(
+ lower=0.0,
+ upper=0.0,
+ scale=1.0e-5,
+ objective=False,
+ plot=True,
+ plot_name="steady-climb",
+).register_to(hsct_model)
+
+# adjacency skin thickness constraints (for structures discipline)
+variables = hsct_model.get_variables()
+adj_scale = 10.0
+section_prefix = ["rib", "OML"]
+section_nums = [nribs, nOML]
+for isection, prefix in enumerate(section_prefix):
+ section_num = section_nums[isection]
+ for iconstr in range(1, section_num):
+ left_var = hsct_model.get_variables(names=f"{prefix}{iconstr}")
+ right_var = hsct_model.get_variables(names=f"{prefix}{iconstr+1}")
+ adj_constr = (left_var - right_var) / left_var
+ adj_ratio = 0.15
+ adj_constr.set_name(f"{prefix}{iconstr}-{iconstr+1}").optimize(
+ lower=-adj_ratio, upper=adj_ratio, scale=1.0, objective=False
+ ).register_to(hsct_model)
+
+# BUILD THE DRIVER. NO DISCIPLINE INTERFACES IN DRIVER SCRIPT
+# ------------------------------------------------------
+
+# load in the previous design from the sizing optimization
+# to overwrite the initial values
+# change this to "internal-struct.txt" and add internal struct
+# shape variables in once that is ready
+design_in_file = os.path.join(base_dir, "design", "internal-struct.txt") # sizing.txt
+hsct_model.read_design_variables_file(comm, design_in_file)
+
+# change structural design var lb and ubs to be closer to sizing design
+# want to use improved internal struct design after this
+margin = 0.3
+for var in hsct_model.get_variables():
+ if var.analysis_type == "structural":
+ var.lower = np.max([(1 - margin) * var.value, var.lower])
+ var.upper = np.min([(1 + margin) * var.value, var.upper])
+
+
+# build the solvers and coupled driver
+solvers = SolverManager(comm)
+remote = Remote(comm, analysis_file, fun3d_dir, nprocs=192)
+f2f_driver = FuntofemShapeDriver.aero_remesh(
+ solvers, hsct_model, remote, forward_flow_post_analysis=True
+)
+
+# test the derivatives of analysis functions using FD
+h = 1e-2
+orig_values = [var.value for var in hsct_model.get_variables()]
+pert_values = [
+ np.random.rand() if var.analysis_type == "shape" else 0.0
+ for var in hsct_model.get_variables()
+]
+
+f2f_driver.solve_forward()
+f2f_driver.solve_adjoint()
+
+m_funcs = [func.value.real for func in hsct_model.get_functions()]
+adj_grads = [
+ [func.derivatives[var] for var in hsct_model.get_variables()]
+ for func in hsct_model.get_functions()
+]
+adj_dderivs = []
+for ifunc, func in enumerate(hsct_model.get_functions()):
+ adj_dderiv = 0.0
+ for ivar, var in enumerate(hsct_model.get_variables()):
+ adj_dderiv += adj_grads[ifunc][ivar] * pert_values[ivar]
+ adj_dderivs += [adj_dderiv]
+
+# run f(x-h) analysis
+for ivar, var in enumerate(hsct_model.get_variables()):
+ var.value -= pert_values[ivar] * h
+
+f2f_driver.solve_forward()
+i_funcs = [func.value.real for func in hsct_model.get_functions()]
+
+# run f(x+h) analysis
+for ivar, var in enumerate(hsct_model.get_variables()):
+ var.value += 2 * pert_values[ivar] * h
+
+f2f_driver.solve_forward()
+f_funcs = [func.value.real for func in hsct_model.get_functions()]
+
+# get the FD gradients
+fd_derivs = [(f_funcs[ifunc] - i_funcs[ifunc]) / 2 / h for ifunc in range(len(i_funcs))]
+
+# compare the gradients
+rel_errors = [
+ (fd_derivs[ifunc] - adj_dderivs[ifunc]) / fd_derivs[ifunc]
+ for ifunc in range(len(fd_derivs))
+]
+
+if comm.rank == 0:
+ print(f"Results of finite difference test:\n\n")
+ for ifunc, func in enumerate(hsct_model.get_functions()):
+ print(f"\n\nfunc {func.full_name}")
+ print(f"\tx-h val = {i_funcs[ifunc]}")
+ print(f"\tx val = {m_funcs[ifunc]}")
+ print(f"\tx+h val = {f_funcs[ifunc]}")
+ print(f"\tadj dderiv = {adj_dderivs[ifunc]}")
+ print(f"\tfd dderiv = {fd_derivs[ifunc]}")
+ print(f"\trel error = {rel_errors[ifunc]}")
+ for ifunc, func in enumerate(hsct_model.get_functions()):
+ for ivar, var in enumerate(hsct_model.get_variables()):
+ print(
+ f"\t\tadj d{func.full_name}/d{var.full_name} = {adj_grads[ifunc][ivar]}"
+ )
diff --git a/examples/fun3d_examples/sst/sst_optimization/README.md b/examples/fun3d_examples/sst/sst_optimization/README.md
new file mode 100644
index 00000000..0bdbd42d
--- /dev/null
+++ b/examples/fun3d_examples/sst/sst_optimization/README.md
@@ -0,0 +1,144 @@
+# Supersonic Transport Wing (SST) — Full Optimization Example
+
+This directory contains a multi-step aerothermoelastic optimization of the SST (HSCT) aircraft wing, adapted from the research case in Engelstad et al. (2023). The workflow progresses from one-way structural sizing through fully coupled aeroelastic and aerothermoelastic optimization, using FUN3D for CFD, TACS for structural analysis, and CAPS/ESP for geometry parameterization and mesh generation.
+
+## Flight Conditions (Cruise)
+
+| Parameter | Value |
+|-----------|-------|
+| Altitude | 60,000 ft |
+| Mach number | Mach 2.5 |
+| T∞ | 216 K |
+| ρ∞ | 0.1165 kg/m³ |
+| Speed of sound | 295 m/s |
+| V∞ | 737.5 m/s |
+| q∞ | 3.1682×10⁴ Pa |
+| Re | 7.3817×10⁸ |
+| y⁺ | 15 (turbulent wall spacing = 1×10⁻⁴ m) |
+
+## Dependencies
+
+- **FUN3D** — CFD solver
+- **TACS** — structural finite element solver
+- **CAPS/ESP (pyCAPS)** — geometry parameterization and mesh generation
+- **mpi4py** — MPI parallelism
+- **pyoptsparse (SNOPT)** — gradient-based optimization
+
+## Directory Layout
+
+```
+sst_optimization_full/
+├── cfd/ # FUN3D mesh files and CAPS fluid analysis outputs
+├── geometry/ # CSM geometry source file (sst_v2.csm)
+├── struct/ # TACS structural mesh files and CAPS structural analysis outputs
+├── design/ # Optimization history files (.hst) and design variable files (.txt)
+├── _mesh_fun3d.py
+├── _mesh_tacs.py
+├── _run_flow.py
+├── _run_inviscid_ae.py
+├── 1_sizing_optimization.py
+├── 2_sizing_shape.py
+├── 3_eval_inviscid.py
+├── 4_eval_turb.py
+├── 5_fc_inviscid_ae_remesh.py
+└── 6_fc_inviscid_der_test.py
+```
+
+## Helper Scripts
+
+These scripts handle mesh generation and standalone forward analyses that are prerequisites for the numbered optimization scripts.
+
+### `_mesh_fun3d.py`
+
+Generates the FUN3D volume mesh via CAPS/ESP AFLR. Supports both inviscid and turbulent boundary conditions (controlled by a flag at the top of the script). Outputs the mesh files to `cfd/`.
+
+### `_mesh_tacs.py`
+
+Generates the TACS structural mesh via CAPS/ESP EGADS. Outputs the structural mesh files to `struct/`.
+
+### `_run_flow.py`
+
+Runs a one-way aero-only forward analysis (no structural coupling) to produce an aero loads file. The loads file is written to `cfd/` and is used as input to the one-way structural sizing scripts (1 and 2).
+
+### `_run_inviscid_ae.py`
+
+Runs a fully coupled inviscid aeroelastic forward analysis. This script is called as a subprocess by scripts 5 and 6 via the FUNtoFEM `Remote` driver interface.
+
+## Numbered Scripts
+
+The numbered scripts form a progressive optimization workflow, each building on the results of the previous step.
+
+### `1_sizing_optimization.py`
+
+**Purpose**: One-way structural sizing optimization. Minimizes wing structural mass subject to a KS failure constraint (ksfailure ≤ 1).
+
+**Inputs**: Aero loads file produced by `_run_flow.py` (in `cfd/`).
+
+**Outputs**: Optimal panel thicknesses written to `design/sizing.txt`.
+
+---
+
+### `2_sizing_shape.py`
+
+**Purpose**: One-way sizing plus internal structural shape optimization. Extends script 1 by adding rib and spar orientation angles as shape design variables.
+
+**Inputs**: Aero loads file from `cfd/`; initial design from `design/sizing.txt` (optional).
+
+**Outputs**: Optimal design (thicknesses + shape variables) written to `design/internal-struct.txt`.
+
+---
+
+### `3_eval_inviscid.py`
+
+**Purpose**: Fully coupled inviscid aeroelastic forward analysis (evaluation only, no optimization). Useful for verifying the design from script 2 under coupled aero-structural loading.
+
+**Inputs**: Design variables from `design/internal-struct.txt`.
+
+**Outputs**: Prints all functional values (mass, KS failure, lift, drag) to stdout.
+
+---
+
+### `4_eval_turb.py`
+
+**Purpose**: Fully coupled turbulent aerothermoelastic forward analysis (evaluation only). Evaluates the final design under turbulent flow with aerodynamic heating.
+
+**Inputs**: Design variables from `design/inviscid-ae.txt` (produced by script 5).
+
+**Outputs**: Prints all functional values to stdout.
+
+---
+
+### `5_fc_inviscid_ae_remesh.py`
+
+**Purpose**: Fully coupled inviscid aeroelastic TOGW (take-off gross weight) minimization with geometry remeshing at each major iteration. This is the primary coupled optimization script.
+
+**Inputs**: Initial design from `design/internal-struct.txt`. Calls `_run_inviscid_ae.py` as a subprocess via the `Remote` driver.
+
+**Outputs**: Optimized design written to `design/inviscid-ae.txt`.
+
+---
+
+### `6_fc_inviscid_der_test.py`
+
+**Purpose**: Finite-difference derivative test for the fully coupled inviscid driver. Validates that adjoint-computed gradients match finite-difference approximations to the specified tolerance.
+
+**Inputs**: Design variables from `design/internal-struct.txt`. Calls `_run_inviscid_ae.py` as a subprocess.
+
+**Outputs**: Prints gradient comparison results to stdout.
+
+## Recommended Run Order
+
+```
+_mesh_fun3d.py → _mesh_tacs.py → _run_flow.py → 1_sizing_optimization.py
+ → 2_sizing_shape.py → 3_eval_inviscid.py → 5_fc_inviscid_ae_remesh.py → 4_eval_turb.py
+```
+
+Script 6 (`6_fc_inviscid_der_test.py`) can be run after script 2 to validate gradients before launching the full coupled optimization in script 5.
+
+## Citation
+
+```
+Engelstad, S. P., Burke, B. J., Patel, R. N., Sahu, S., and Kennedy, G. J.,
+"High-Fidelity Aerothermoelastic Optimization with Differentiable CAD Geometry,"
+AIAA Scitech 2023 Forum, National Harbor, MD, 2023. doi:10.2514/6.2023-0329.
+```
diff --git a/examples/fun3d_examples/sst/sst_optimization/_mesh_fun3d.py b/examples/fun3d_examples/sst/sst_optimization/_mesh_fun3d.py
new file mode 100644
index 00000000..507b3032
--- /dev/null
+++ b/examples/fun3d_examples/sst/sst_optimization/_mesh_fun3d.py
@@ -0,0 +1,69 @@
+"""
+Sean P. Engelstad, Georgia Tech 2023
+"""
+
+from pathlib import Path
+from funtofem.interface import Fun3dModel, Fun3dBC
+from mpi4py import MPI
+
+here = Path(__file__).parent
+comm = MPI.COMM_WORLD
+csm_file = str(here.joinpath("geometry").joinpath("sst_v2.csm"))
+
+# case = "inviscid" # "turbulent"
+case = "turbulent"
+if case == "inviscid":
+ project_name = "sst-inviscid"
+else: # turbulent
+ project_name = "sst-turb"
+
+fun3d_model = Fun3dModel.build(csm_file=csm_file, comm=comm, project_name=project_name)
+aflr_aim = fun3d_model.aflr_aim
+fun3d_aim = fun3d_model.fun3d_aim
+fun3d_aim.set_config_parameter("mode:flow", 1)
+fun3d_aim.set_config_parameter("mode:struct", 0)
+
+if comm.rank == 0:
+ aim = aflr_aim.surface_aim
+ aim.input.Mesh_Sizing = {"botFuse": {"scaleFactor": 0.1}}
+
+# min_scale has the greatest effect on mesh size (scale that to affect # mesh elements)
+# min_scale = 0.005 => 3.5M cells
+# min_scale = 0.01 => 1.5M cells
+# min_scale = 0.02 => 600k cells
+aflr_aim.set_surface_mesh(
+ ff_growth=1.2, mesh_length=30.0, min_scale=0.005, max_scale=0.5, use_quads=True
+)
+
+if case == "inviscid":
+ Fun3dBC.inviscid(caps_group="wall").register_to(fun3d_model)
+ Fun3dBC.inviscid(caps_group="staticWall").register_to(fun3d_model)
+else:
+ Fun3dBC.viscous(caps_group="wall", wall_spacing=0.0001).register_to(fun3d_model)
+ Fun3dBC.viscous(caps_group="staticWall", wall_spacing=0.0001).register_to(
+ fun3d_model
+ )
+
+refinement = 1
+
+FluidMeshOptions = {"aflr4AIM": {}, "aflr3AIM": {}}
+
+FluidMeshOptions["aflr4AIM"]["Mesh_Sizing"] = {
+ "wingUpMesh": {"edgeWeight": 1.0},
+ "wingDownMesh": {"edgeWeight": 1.0},
+ "fuselageDownMesh": {"edgeWeight": 1.0},
+ "fuselageUpMesh": {"edgeWeight": 1.0},
+ "centerlineUp": {"scaleFactor": 0.5 ** (refinement / 2)},
+ "centerlineDown": {"scaleFactor": 0.1 ** (refinement / 2)},
+ "centerlineCenter": {"scaleFactor": 0.5 ** (refinement / 2)},
+ "wingFuseJunc": {"scaleFactor": 0.5 ** (refinement / 2)},
+ "tipEdgeMesh": {"scaleFactor": 0.5 ** (refinement / 2)},
+ "wingUpMesh": {"cdfr": 1.2},
+}
+
+aflr_aim.saveDictOptions(FluidMeshOptions)
+
+Fun3dBC.SymmetryY(caps_group="SymmetryY").register_to(fun3d_model)
+Fun3dBC.Farfield(caps_group="Farfield").register_to(fun3d_model)
+fun3d_model.setup()
+fun3d_aim.pre_analysis()
diff --git a/examples/fun3d_examples/sst/sst_optimization/_mesh_tacs.py b/examples/fun3d_examples/sst/sst_optimization/_mesh_tacs.py
new file mode 100644
index 00000000..674ba43d
--- /dev/null
+++ b/examples/fun3d_examples/sst/sst_optimization/_mesh_tacs.py
@@ -0,0 +1,123 @@
+"""
+Sean P. Engelstad, Georgia Tech 2023
+"""
+
+from funtofem import *
+from mpi4py import MPI
+from tacs import caps2tacs
+import os
+
+comm = MPI.COMM_WORLD
+
+base_dir = os.path.dirname(os.path.abspath(__file__))
+csm_path = os.path.join(base_dir, "geometry", "sst_v2.csm")
+
+# make the funtofem and tacs model
+tacs_model = caps2tacs.TacsModel.build(
+ csm_file=csm_path, comm=comm, problem_name="struct_mesh"
+)
+tacs_model.mesh_aim.set_mesh( # need a refined-enough mesh for the derivative test to pass
+ edge_pt_min=2,
+ edge_pt_max=20,
+ global_mesh_size=0.3,
+ max_surf_offset=0.2,
+ max_dihedral_angle=15,
+).register_to(
+ tacs_model
+)
+
+egads_aim = tacs_model.mesh_aim
+tacs_aim = tacs_model.tacs_aim
+
+tacs_aim.set_config_parameter("mode:flow", 0)
+tacs_aim.set_config_parameter("mode:struct", 1)
+
+for proc in tacs_aim.active_procs:
+ if comm.rank == proc:
+ aim = tacs_model.mesh_aim.aim
+ aim.input.Mesh_Sizing = {
+ "horizX": {"numEdgePoints": 20},
+ "horizY": {"numEdgePoints": 8},
+ "vert": {"numEdgePoints": 4},
+ }
+
+view_mesh = False
+
+if comm.rank == 0 and view_mesh:
+ egads_aim.aim.runAnalysis()
+ egads_aim.aim.geometry.view()
+if view_mesh:
+ exit()
+
+# # setup the material and shell properties
+titanium_alloy = caps2tacs.Isotropic.titanium_alloy().register_to(tacs_model)
+
+tacs_aim = tacs_model.tacs_aim
+tacs_aim.set_config_parameter("mode:flow", 0)
+tacs_aim.set_config_parameter("mode:struct", 1)
+nribs = int(tacs_model.get_config_parameter("wing:nribs"))
+nspars = int(tacs_model.get_config_parameter("wing:nspars"))
+nOML = int(tacs_aim.get_output_parameter("wing:nOML"))
+
+wing = Body.aeroelastic("wing", boundary=4)
+
+for irib in range(1, nribs + 1):
+ name = f"rib{irib}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.05
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for ispar in range(1, nspars + 1):
+ name = f"spar{ispar}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.05
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for iOML in range(1, nOML + 1):
+ name = f"OML{iOML}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.03
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for name in ["LEspar", "TEspar"]:
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.03
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+# structural shape variables
+for prefix in ["rib", "spar"]:
+ Variable.shape(f"wing:{prefix}_a1", value=1.0).set_bounds(
+ lower=0.6, upper=1.4
+ ).register_to(wing)
+ Variable.shape(f"wing:{prefix}_a2", value=0.0).set_bounds(
+ lower=-0.3, upper=0.3
+ ).register_to(wing)
+
+
+# # add remaining information to tacs model
+caps2tacs.PinConstraint("root").register_to(tacs_model)
+caps2tacs.PinConstraint("wingFuse").register_to(tacs_model)
+caps2tacs.TemperatureConstraint("midplane").register_to(tacs_model)
+
+# # setup the tacs model
+tacs_model.setup(include_aim=True)
+tacs_model.pre_analysis()
+
+# # print out the mesh empty soln (to view mesh)
+tacs_model.createTACSProbs(addFunctions=True)
+SPs = tacs_model.SPs
+for caseID in SPs:
+ SPs[caseID].writeSolution(
+ baseName="tacs_output",
+ outputDir=tacs_aim.analysis_dir,
+ number=0,
+ )
diff --git a/examples/fun3d_examples/sst/sst_optimization/_run_flow.py b/examples/fun3d_examples/sst/sst_optimization/_run_flow.py
new file mode 100644
index 00000000..9f40cd8a
--- /dev/null
+++ b/examples/fun3d_examples/sst/sst_optimization/_run_flow.py
@@ -0,0 +1,37 @@
+"""
+Sean P. Engelstad, Georgia Tech 2023
+
+Run a FUN3D analysis with the Fun3dOnewayDriver
+"""
+
+from funtofem import *
+from mpi4py import MPI
+
+comm = MPI.COMM_WORLD
+
+f2f_model = FUNtoFEMmodel("hsct_flow")
+wing = Body.aerothermoelastic("wing", boundary=4)
+wing.register_to(f2f_model)
+
+# make a funtofem scenario
+cruise = Scenario.steady("cruise_inviscid", steps=500) # 2000
+cruise.set_stop_criterion(early_stopping=True)
+Function.lift().register_to(cruise)
+Function.drag().register_to(cruise)
+cruise.set_temperature(T_ref=216, T_inf=216)
+cruise.set_flow_ref_vals(qinf=3.16e4)
+cruise.register_to(f2f_model)
+
+
+# DISCIPLINE INTERFACES AND DRIVERS
+# -----------------------------------------------------
+
+solvers = SolverManager(comm)
+solvers.flow = Fun3dInterface(
+ comm, f2f_model, fun3d_dir="cfd", forward_tolerance=1e-6, adjoint_tolerance=1e-6
+)
+my_transfer_settings = TransferSettings(npts=200)
+fun3d_driver = OnewayAeroDriver(
+ solvers, f2f_model, transfer_settings=my_transfer_settings
+)
+fun3d_driver.solve_forward()
diff --git a/examples/fun3d_examples/sst/sst_optimization/_run_inviscid_ae.py b/examples/fun3d_examples/sst/sst_optimization/_run_inviscid_ae.py
new file mode 100644
index 00000000..643d2cc9
--- /dev/null
+++ b/examples/fun3d_examples/sst/sst_optimization/_run_inviscid_ae.py
@@ -0,0 +1,408 @@
+"""
+Sean P. Engelstad, Georgia Tech 2023
+
+This is the fully coupled aeroelastic inviscid optimization of the HSCT.
+NOTE : this is the analysis script corresponding to 5_fc_inviscid_ae_remesh.py.
+ Now the mesh generation is performed HERE in the analysis script.
+"""
+
+import os
+from mpi4py import MPI
+from funtofem import *
+from tacs import caps2tacs
+
+# script inputs, NOTE : you need to run with restart=False first to make restart files
+debug = False # if True, CFD only runs one step each
+restart = False # use restart files for CFD
+optimize_trim = False
+
+comm = MPI.COMM_WORLD
+base_dir = os.path.dirname(os.path.abspath(__file__))
+fun3d_dir = os.path.join(base_dir, "cfd")
+tacs_dir = os.path.join(base_dir, "struct")
+csm_path = os.path.join(base_dir, "geometry", "sst_v2.csm")
+
+# FUNtoFEM and SHAPE MODELS
+# ---------------------------------------------------------
+hsct_model = FUNtoFEMmodel("sst-inviscid")
+
+# design the FUN3D aero shape model
+flow_model = Fun3dModel.build(
+ csm_file=csm_path, comm=comm, project_name="sst-inviscid", root=8
+)
+m_aflr_aim = flow_model.aflr_aim
+
+flow_aim = flow_model.fun3d_aim
+flow_aim.set_config_parameter("mode:flow", 1)
+flow_aim.set_config_parameter("mode:struct", 0)
+
+# min_scale has the greatest effect on mesh size (scale that to affect # mesh elements)
+m_aflr_aim.set_surface_mesh(
+ ff_growth=1.4, mesh_length=6.0, min_scale=0.006, max_scale=0.5, use_quads=True
+)
+Fun3dBC.inviscid(caps_group="wall").register_to(flow_model)
+Fun3dBC.inviscid(caps_group="staticWall").register_to(flow_model)
+Fun3dBC.SymmetryY(caps_group="SymmetryY").register_to(flow_model)
+Fun3dBC.Farfield(caps_group="Farfield").register_to(flow_model)
+
+# refine bottom of fuselage edge otherwise it's too coarse there and causes bad mesh quality + divergence
+if comm.rank == flow_model.root:
+ aim = m_aflr_aim.surface_aim
+ aim.input.Mesh_Sizing = {"botFuse": {"scaleFactor": 0.1}}
+
+flow_model.setup()
+hsct_model.flow = flow_model
+
+# design the TACS struct shape model
+tacs_model = caps2tacs.TacsModel.build(
+ csm_file=csm_path,
+ comm=comm,
+ problem_name="capsStruct5",
+ active_procs=[_ for _ in range(12 if optimize_trim else 10)],
+)
+tacs_model.mesh_aim.set_mesh( # need a refined-enough mesh for the derivative test to pass
+ edge_pt_min=2,
+ edge_pt_max=20,
+ global_mesh_size=0.3,
+ max_surf_offset=0.2,
+ max_dihedral_angle=15,
+).register_to(
+ tacs_model
+)
+tacs_aim = tacs_model.tacs_aim
+tacs_aim.set_config_parameter("mode:flow", 0)
+tacs_aim.set_config_parameter("mode:struct", 1)
+for proc in tacs_model.active_procs:
+ if comm.rank == proc:
+ mesh_aim = tacs_model.mesh_aim.aim
+ mesh_aim.input.Mesh_Sizing = {
+ "horizX": {"numEdgePoints": 20},
+ "horizY": {"numEdgePoints": 8},
+ "vert": {"numEdgePoints": 4},
+ }
+
+ # set optimal internal structure values
+ # manually since don't want to compute
+ # these derivatives here. (optimal values from internal structure optimization)
+ shape_var_dict = {
+ "wing:rib_a1": 0.65,
+ "wing:rib_a2": -0.3,
+ "wing:spar_a1": 1.163,
+ "wing:spar_a2": -0.287,
+ }
+ for var_key in shape_var_dict:
+ tacs_aim.geometry.despmtr[var_key].value = shape_var_dict[var_key]
+
+hsct_model.structural = tacs_model
+
+# tacs model constraints
+caps2tacs.PinConstraint("root").register_to(tacs_model)
+caps2tacs.PinConstraint("wingFuse").register_to(tacs_model)
+caps2tacs.TemperatureConstraint("midplane").register_to(tacs_model)
+
+# BODIES, STRUCT DVs and SHAPE DVs
+# ---------------------------------------------------
+wing = Body.aeroelastic("wing", boundary=4)
+wing.relaxation(AitkenRelaxation(theta_init=0.01, theta_min=0.001, theta_max=0.3))
+
+# setup the material and shell properties
+titanium_alloy = caps2tacs.Isotropic.titanium_alloy().register_to(tacs_model)
+
+nribs = int(tacs_model.get_config_parameter("wing:nribs"))
+nspars = int(tacs_model.get_config_parameter("wing:nspars"))
+nOML = int(tacs_aim.get_output_parameter("wing:nOML"))
+
+for irib in range(1, nribs + 1):
+ name = f"rib{irib}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.05
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for ispar in range(1, nspars + 1):
+ name = f"spar{ispar}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.05
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for iOML in range(1, nOML + 1):
+ name = f"OML{iOML}"
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.03
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+for name in ["LEspar", "TEspar"]:
+ caps2tacs.ShellProperty(
+ caps_group=name, material=titanium_alloy, membrane_thickness=0.03
+ ).register_to(tacs_model)
+ Variable.structural(name, value=0.1).set_bounds(
+ lower=0.001, upper=0.15, scale=100.0
+ ).register_to(wing)
+
+# don't want to optimize these => just fix them from optimal
+# internal-structure design
+# structural shape variables
+# for prefix in ["rib", "spar"]:
+# Variable.shape(f"wing:{prefix}_a1", value=1.0).set_bounds(
+# lower=0.6, upper=1.4
+# ).register_to(wing)
+# Variable.shape(f"wing:{prefix}_a2", value=0.0).set_bounds(
+# lower=-0.3, upper=0.3
+# ).register_to(wing)
+
+# wing size and shape variables
+c1 = Variable.shape("wing:c1", value=41.2).set_bounds(lower=30.0, upper=50.0)
+c2 = Variable.shape("wing:c2", value=28.862).set_bounds(lower=20.0, upper=40.0)
+c3 = Variable.shape("wing:c3", value=18.554).set_bounds(lower=13.0, upper=25.0)
+c4 = Variable.shape("wing:c4", value=7.422).set_bounds(lower=5.0, upper=15.0)
+cbars = [c1, c2, c3, c4]
+
+# coffset1 = Variable.shape("wing:cbar_offset1", value=0.0).set_bounds(
+# lower=-0.1, upper=0.1
+# )
+# coffset2 = Variable.shape("wing:cbar_offset2", value=0.0).set_bounds(
+# lower=-0.1, upper=0.1
+# )
+# coffset3 = Variable.shape("wing:cbar_offset3", value=0.0).set_bounds(
+# lower=-0.1, upper=0.1
+# )
+# coffsets = [coffset1, coffset2, coffset3]
+
+# dzhat1 = Variable.shape("wing:dzhat1", value=0.05).set_bounds(lower=0.0, upper=0.1)
+dzhat2 = Variable.shape("wing:dzhat2", value=0.2).set_bounds(lower=0.1, upper=0.3)
+dz_dihedral = Variable.shape("wing:dz_dihedral", value=-5.0).set_bounds(
+ lower=-5.1, upper=-2.0
+)
+dzs = [dzhat2, dz_dihedral]
+
+# geometric AOA on wing
+# geomAOA1 = Variable.shape("wing:geomAOA1", value=0.0).set_bounds(lower=-0.5, upper=0.5)
+# geomAOA2 = Variable.shape("wing:geomAOA2", value=0.0).set_bounds(lower=-2.0, upper=2.0)
+# geomAOA3 = Variable.shape("wing:geomAOA3", value=0.0).set_bounds(lower=-2.0, upper=2.0)
+# geomAOA4 = Variable.shape("wing:geomAOA4", value=0.0).set_bounds(lower=-2.0, upper=2.0)
+# geomAOA1, geomAOA2, geomAOA3, geomAOA4
+for var in cbars + dzs:
+ var.register_to(wing)
+
+# wing thickness variables
+wing_thick1 = Variable.shape("wing:thick1", value=1.237).set_bounds(
+ lower=1.0, upper=1.4
+)
+wing_thick2 = Variable.shape("wing:thick2", value=1.154).set_bounds(
+ lower=0.8, upper=1.3
+)
+wing_thick3 = Variable.shape("wing:thick3", value=0.742).set_bounds(
+ lower=0.5, upper=0.9
+)
+wing_thick4 = Variable.shape("wing:thick4", value=0.296).set_bounds(
+ lower=0.2, upper=0.5
+)
+for var in [wing_thick1, wing_thick2, wing_thick3, wing_thick4]:
+ var.register_to(wing)
+
+# canard shape variables
+if optimize_trim:
+ canard_area = Variable.shape("canard:area", value=70).set_bounds(
+ lower=40, upper=200
+ )
+ canard_twist = Variable.shape("canard:geomAOA", value=5).set_bounds(
+ lower=-5, upper=8
+ )
+
+ for canard_var in [canard_area, canard_twist]:
+ canard_var.register_to(wing)
+
+wing.register_to(hsct_model)
+# end of wing section
+
+# SCENARIOS, AERO DVs, and remaining SHAPE VARS
+# -----------------------------------------------
+
+# NOTE: shape variables can be assigned to the body or scenario
+# when using ESP/CAPS, it doesn't really matter
+_climb_qinf = 3.28e4
+
+if not (debug):
+ climb = Scenario.steady(
+ "climb_inviscid",
+ steps=650,
+ uncoupled_steps=150,
+ )
+ climb.adjoint_steps = 300
+else:
+ climb = Scenario.steady("climb_inviscid", steps=2, uncoupled_steps=1)
+ climb.adjoint_steps = 1
+climb.fun3d_project(flow_aim.project_name)
+climb.set_temperature(T_ref=300.0, T_inf=300.0) # modify this
+climb.set_flow_ref_vals(qinf=_climb_qinf, flow_dt=1.0)
+ksfailure_climb = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0, upper=1.0, objective=False, plot=True, plot_name="ks-climb"
+)
+cl_climb = Function.lift(body=0)
+cd_climb = Function.drag(body=0)
+aoa_climb = climb.get_variable("AOA").set_bounds(lower=0.0, value=4.0, upper=8.0)
+mach_climb = climb.get_variable("Mach").set_bounds(lower=0.3, value=0.55, upper=0.9)
+for func in [ksfailure_climb, cl_climb, cd_climb]:
+ func.register_to(climb)
+climb.register_to(hsct_model)
+
+# cruise flight condition
+# altitude - 60 kft, ,
+_mach_cruise = 2.5
+_ainf_cruise = 295 # m/s
+_rho_inf_cruise = 0.1165 # kg / m^3
+# _mu_cruise = 1.42e-5 # kg/(m-s)
+_aoa_cruise = 2.0
+_Tinf_cruise = 216 # K
+_vinf_cruise = _mach_cruise * _ainf_cruise
+_qinf_cruise = 0.5 * _rho_inf_cruise * _vinf_cruise**2
+
+if not (debug):
+ cruise = Scenario.steady(
+ "cruise_inviscid",
+ steps=400,
+ uncoupled_steps=150,
+ )
+ cruise.adjoint_steps = 100
+else:
+ cruise = Scenario.steady("cruise_inviscid", steps=2, uncoupled_steps=1)
+ cruise.adjoint_steps = 1
+cruise.fun3d_project(flow_aim.project_name)
+cruise.set_temperature(T_ref=_Tinf_cruise, T_inf=_Tinf_cruise)
+cruise.set_flow_ref_vals(qinf=_qinf_cruise, flow_dt=1.0)
+ksfailure_cruise = Function.ksfailure(ks_weight=10.0, safety_factor=1.5).optimize(
+ scale=1.0, upper=1.0, objective=False, plot=True, plot_name="ks-cruise"
+)
+cl_cruise = Function.lift(body=0)
+cd_cruise = Function.drag(body=0)
+if optimize_trim:
+ moment = Function.moment().optimize(
+ lower=0.0,
+ upper=0.0,
+ scale=1.0e-3,
+ objective=False,
+ plot=True,
+ plot_name="cm-cruise",
+ )
+wing_mass = Function.mass()
+aoa_cruise = cruise.get_variable("AOA").set_bounds(
+ lower=1.0, value=_aoa_cruise, upper=4.0
+)
+mach_cruise = cruise.get_variable("Mach").set_bounds(
+ lower=2.3, value=_mach_cruise, upper=2.7
+)
+for func in [ksfailure_cruise, wing_mass, cl_cruise, cd_cruise]:
+ func.register_to(cruise)
+if optimize_trim:
+ moment.register_to(cruise)
+cruise.register_to(hsct_model)
+
+# COMPOSITE FUNCTIONS
+# -----------------------------------------
+
+# TOGW
+g = 9.81 # m/s^2
+lb_to_N = 4.448 # N/lb
+tsfc = 3.9e-5 # kg/N/s, Rolls Royce Olympus 593 engine
+fuselage_tail_weight = 5.8e5 # N
+fuel_reserve_fraction = 0.06
+num_passengers = 250
+passenger_weight = 230 * num_passengers * lb_to_N
+crew_weight = (450 + 5 * num_passengers) * lb_to_N
+# descent_fuel = 6000 * lb_to_N # N, fixed based on NASA report
+
+wing_weight = 2 * wing_mass * g # m/s^2 => N, doubled for sym
+empty_weight = wing_weight + fuselage_tail_weight
+boarded_weight = empty_weight + passenger_weight + crew_weight
+
+cruise_lift = 2 * cl_cruise * _qinf_cruise
+cruise_drag = 2 * cd_cruise * _qinf_cruise
+
+takeoff_weight_ratio = 0.97
+climb_weight_ratio = 0.985
+land_weight_ratio = 0.995
+rem_weight_ratios = takeoff_weight_ratio * climb_weight_ratio * land_weight_ratio
+
+_range = 12800 # km
+_range *= 1e3 # to m
+cruise_LoverD = cruise_lift / cruise_drag
+cruise_weight_ratio = CompositeFunction.exp(
+ -_range * tsfc / _vinf_cruise / cruise_LoverD
+)
+
+mission_weight_ratio = rem_weight_ratios * cruise_weight_ratio
+fuel_weight_ratio = 1.06 * (1 - mission_weight_ratio) # 6% reserve fuel
+togw = boarded_weight / (1 - fuel_weight_ratio)
+togw.set_name("takeoff-gross-weight").optimize( # kg
+ scale=1.0e-5, objective=True, plot=True, plot_name="togw"
+).register_to(hsct_model)
+
+# steady flight and climb conditions
+
+cruise_weight = togw * 0.5 * (1 + cruise_weight_ratio)
+steady_cruise = cruise_lift / cruise_weight
+steady_cruise.set_name("steady_cruise").optimize(
+ lower=1.0,
+ upper=1.0,
+ scale=1.0,
+ objective=False,
+ plot=True,
+ plot_name="steady-cruise",
+).register_to(hsct_model)
+climb_lift = 2 * cl_climb * _climb_qinf
+steady_climb = climb_lift / togw
+steady_climb.set_name("steady_climb").optimize(
+ lower=1.5,
+ upper=2.5,
+ scale=1.0e-5,
+ objective=False,
+ plot=True,
+ plot_name="steady-climb",
+).register_to(hsct_model)
+
+# adjacency skin thickness constraints (for structures discipline)
+variables = hsct_model.get_variables()
+adj_scale = 10.0
+section_prefix = ["rib", "OML"]
+section_nums = [nribs, nOML]
+for isection, prefix in enumerate(section_prefix):
+ section_num = section_nums[isection]
+ for iconstr in range(1, section_num):
+ left_var = hsct_model.get_variables(names=f"{prefix}{iconstr}")
+ right_var = hsct_model.get_variables(names=f"{prefix}{iconstr+1}")
+ adj_constr = (left_var - right_var) / left_var
+ adj_ratio = 0.15
+ adj_constr.set_name(f"{prefix}{iconstr}-{iconstr+1}").optimize(
+ lower=-adj_ratio, upper=adj_ratio, scale=1.0, objective=False
+ ).register_to(hsct_model)
+
+# BUILD THE DRIVER WHICH AUTO RUNS THE ANALYSIS
+# ---------------------------------------------
+
+# build the solvers and coupled driver
+solvers = SolverManager(comm)
+solvers.flow = Fun3dInterface(
+ comm,
+ hsct_model,
+ fun3d_dir=fun3d_dir,
+ auto_coords=False,
+ forward_tolerance=1e-7,
+ adjoint_tolerance=1e-7,
+)
+
+# select transfer settings in this case using MELD for steady-state aeroelastic transfer
+# between structural and aerodynamic meshes
+transfer_settings = TransferSettings(elastic_scheme="meld", npts=200, beta=0.5, isym=1)
+
+f2f_driver = FuntofemShapeDriver.analysis(
+ solvers,
+ hsct_model,
+ transfer_settings=transfer_settings,
+ struct_nprocs=20,
+ auto_run=True,
+)
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Adjoint/.gitignore b/examples/fun3d_examples/sst/sst_optimization/cfd/.gitkeep
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Adjoint/.gitignore
rename to examples/fun3d_examples/sst/sst_optimization/cfd/.gitkeep
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/inv-cruise/Adjoint/.gitignore b/examples/fun3d_examples/sst/sst_optimization/design/.gitkeep
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/inv-cruise/Adjoint/.gitignore
rename to examples/fun3d_examples/sst/sst_optimization/design/.gitkeep
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/inv-cruise/Flow/.gitignore b/examples/fun3d_examples/sst/sst_optimization/geometry/.gitkeep
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/inv-cruise/Flow/.gitignore
rename to examples/fun3d_examples/sst/sst_optimization/geometry/.gitkeep
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Adjoint/.gitignore b/examples/fun3d_examples/sst/sst_optimization/struct/.gitkeep
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Adjoint/.gitignore
rename to examples/fun3d_examples/sst/sst_optimization/struct/.gitkeep
diff --git a/examples/fun3d_examples/ssw/README.md b/examples/fun3d_examples/ssw/README.md
new file mode 100644
index 00000000..322f12db
--- /dev/null
+++ b/examples/fun3d_examples/ssw/README.md
@@ -0,0 +1,60 @@
+# Super Simple Wing (SSW)
+
+The Super Simple Wing is a rectangular wing geometry used to demonstrate and test fully coupled aeroelastic optimization with FUN3D, TACS, and ESP/CAPS. Its simple shape makes it easy to reason about the design variables and expected optimization behavior, while still exercising the full FUNtoFEM coupling framework.
+
+
+
+## Flight Conditions
+
+All SSW examples use a steady cruise at FL 100 (~3000 m). From the 1976 Standard Atmosphere:
+
+| Parameter | Value |
+|-----------|-------|
+| T∞ | 268.338 K |
+| p∞ | 69.68 kPa |
+| ρ∞ | 0.9046 kg/m³ |
+| μ∞ | 1.7115×10⁻⁵ Pa·s |
+| Mach | 0.5 (164.19 m/s) |
+| q∞ | 12.1945 kPa |
+| Re_L | 8.776×10⁶ |
+| AoA | 2° |
+
+## Dependencies
+
+- FUN3D
+- TACS
+- ESP/CAPS (pyCAPS)
+- mpi4py
+- pyoptsparse (SNOPT)
+
+## Examples
+
+### [`aeroelastic_optimization/`](aeroelastic_optimization/)
+
+Inviscid aeroelastic optimization using the remeshing-based shape driver. Geometry shape variables are updated via ESP/CAPS and the CFD mesh is regenerated at each major iteration.
+
+**Design variables**: panel thicknesses, angle of attack, geometric twist at each span station, OML airfoil thickness
+
+**Scripts**:
+- `_run_flow.py` — one-way aero forward analysis to produce an aero loads file
+- `_oneway_sizing.py` — one-way structural sizing optimization using the aero loads file
+- `1_panel_thickness.py` — fully coupled panel thickness optimization (no shape)
+- `2_aero_aoa.py` — fully coupled AoA optimization
+- `3_geom_twist.py` — fully coupled geometric twist shape optimization
+- `4_oml_shape.py` — fully coupled twist + OML airfoil thickness optimization
+
+---
+
+### [`ssw_meshdef_optimization/`](ssw_meshdef_optimization/)
+
+Aeroelastic optimization using mesh deformation (no remeshing). Shape changes are applied by deforming the existing CFD mesh, which is faster per iteration but limited to smaller shape perturbations.
+
+**Design variables**: panel thicknesses, angle of attack, geometric twist, OML airfoil thickness
+
+**Scripts**:
+- `_run_flow.py` — one-way aero forward analysis
+- `_oneway_sizing.py` — one-way structural sizing
+- `1_panel_thickness.py` — fully coupled panel thickness optimization
+- `2_aero_aoa.py` — fully coupled AoA optimization
+- `3_geom_twist.py` — fully coupled geometric twist optimization
+- `4_oml_shape.py` — fully coupled twist + OML thickness optimization
diff --git a/examples/fun3d_examples/_ssw-inviscid/.gitignore b/examples/fun3d_examples/ssw/aeroelastic_optimization/.gitignore
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/.gitignore
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/.gitignore
diff --git a/examples/fun3d_examples/_ssw-inviscid/1_panel_thickness.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/1_panel_thickness.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/1_panel_thickness.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/1_panel_thickness.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/2_aero_aoa.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/2_aero_aoa.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/2_aero_aoa.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/2_aero_aoa.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/3_geom_twist.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/3_geom_twist.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/3_geom_twist.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/3_geom_twist.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/4_oml_shape.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/4_oml_shape.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/4_oml_shape.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/4_oml_shape.py
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/README.md b/examples/fun3d_examples/ssw/aeroelastic_optimization/README.md
similarity index 74%
rename from examples/fun3d_examples/ssw_meshdef_optimization/README.md
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/README.md
index f9232b5f..a72d7c68 100644
--- a/examples/fun3d_examples/ssw_meshdef_optimization/README.md
+++ b/examples/fun3d_examples/ssw/aeroelastic_optimization/README.md
@@ -23,9 +23,3 @@ Stipulate flight speed of Mach 0.5 (164.1935 m/s), which results in a dynamic pr
Twist variable is not used at root station (fixed there):
* 3_geom_twist.py - twist variables at each station
* 4_oml_shape.py - twist + airfoil thickness at each station
-
-## Shape + Discipline DVs, Fully Coupled Optimization
-Putting it all together:
-* 5_shape_and_struct.py - geom twist variables + panel thickness struct variables
-* 6_shape_and_aero.py - geom twist variables + aero aoa variable + airfoil thickness
-* 7_kitchen_sink.py - put all previous variables together : geom twist, airfoil thickness, AOA, panel thickness
\ No newline at end of file
diff --git a/examples/fun3d_examples/_ssw-inviscid/_2_cfd_only.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/_2_cfd_only.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/_2_cfd_only.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/_2_cfd_only.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/_blade_callback.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/_blade_callback.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/_blade_callback.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/_blade_callback.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/_oneway_sizing.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/_oneway_sizing.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/_oneway_sizing.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/_oneway_sizing.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/_oneway_sizing_blade.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/_oneway_sizing_blade.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/_oneway_sizing_blade.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/_oneway_sizing_blade.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/_panel_thickness_inviscid.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/_panel_thickness_inviscid.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/_panel_thickness_inviscid.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/_panel_thickness_inviscid.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/_run_flow.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/_run_flow.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/_run_flow.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/_run_flow.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/_test_1_derivs.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/_test_1_derivs.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/_test_1_derivs.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/_test_1_derivs.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/_test_struct_shape.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/_test_struct_shape.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/_test_struct_shape.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/_test_struct_shape.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Adjoint/.gitignore b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Adjoint/.gitignore
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Adjoint/.gitignore
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Adjoint/.gitignore
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/.gitignore b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Flow/.gitignore
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/.gitignore
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Flow/.gitignore
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/fun3d.nml b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Flow/fun3d.nml
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/fun3d.nml
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Flow/fun3d.nml
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/moving_body.input b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Flow/moving_body.input
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/moving_body.input
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Flow/moving_body.input
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/ssw-inviscid.lb8.ugrid b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Flow/ssw-inviscid.lb8.ugrid
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/ssw-inviscid.lb8.ugrid
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Flow/ssw-inviscid.lb8.ugrid
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/ssw-inviscid.mapbc b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Flow/ssw-inviscid.mapbc
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/ssw-inviscid.mapbc
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_inviscid/Flow/ssw-inviscid.mapbc
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Adjoint/.gitignore b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_turb/Adjoint/.gitignore
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Adjoint/.gitignore
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_turb/Adjoint/.gitignore
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/.gitignore b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_turb/Flow/.gitignore
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/.gitignore
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_turb/Flow/.gitignore
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/fun3d.nml b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_turb/Flow/fun3d.nml
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/fun3d.nml
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_turb/Flow/fun3d.nml
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/moving_body.input b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_turb/Flow/moving_body.input
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/moving_body.input
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_turb/Flow/moving_body.input
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/ssw-turb.mapbc b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_turb/Flow/ssw-turb.mapbc
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/ssw-turb.mapbc
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/cruise_turb/Flow/ssw-turb.mapbc
diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/loads/uncoupled_loads.txt b/examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/loads/uncoupled_loads.txt
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/cfd/loads/uncoupled_loads.txt
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/cfd/loads/uncoupled_loads.txt
diff --git a/examples/fun3d_examples/_ssw-inviscid/design/.gitignore b/examples/fun3d_examples/ssw/aeroelastic_optimization/design/.gitignore
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/design/.gitignore
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/design/.gitignore
diff --git a/examples/fun3d_examples/_ssw-inviscid/design/_plot_sizing.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/design/_plot_sizing.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/design/_plot_sizing.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/design/_plot_sizing.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/design/design-1.txt b/examples/fun3d_examples/ssw/aeroelastic_optimization/design/design-1.txt
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/design/design-1.txt
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/design/design-1.txt
diff --git a/examples/fun3d_examples/_ssw-inviscid/design/design-2.txt b/examples/fun3d_examples/ssw/aeroelastic_optimization/design/design-2.txt
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/design/design-2.txt
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/design/design-2.txt
diff --git a/examples/fun3d_examples/_ssw-inviscid/design/sizing-oneway.txt b/examples/fun3d_examples/ssw/aeroelastic_optimization/design/sizing-oneway.txt
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/design/sizing-oneway.txt
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/design/sizing-oneway.txt
diff --git a/examples/fun3d_examples/_ssw-inviscid/design/sizing.txt b/examples/fun3d_examples/ssw/aeroelastic_optimization/design/sizing.txt
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/design/sizing.txt
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/design/sizing.txt
diff --git a/examples/fun3d_examples/_ssw-inviscid/design/ssw-aoa_design.txt b/examples/fun3d_examples/ssw/aeroelastic_optimization/design/ssw-aoa_design.txt
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/design/ssw-aoa_design.txt
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/design/ssw-aoa_design.txt
diff --git a/examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing-1way_design.txt b/examples/fun3d_examples/ssw/aeroelastic_optimization/design/ssw-sizing-1way_design.txt
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing-1way_design.txt
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/design/ssw-sizing-1way_design.txt
diff --git a/examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing1-turb_design.txt b/examples/fun3d_examples/ssw/aeroelastic_optimization/design/ssw-sizing1-turb_design.txt
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing1-turb_design.txt
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/design/ssw-sizing1-turb_design.txt
diff --git a/examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing1_design.txt b/examples/fun3d_examples/ssw/aeroelastic_optimization/design/ssw-sizing1_design.txt
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing1_design.txt
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/design/ssw-sizing1_design.txt
diff --git a/examples/fun3d_examples/_ssw-inviscid/design/ssw-twist_design.txt b/examples/fun3d_examples/ssw/aeroelastic_optimization/design/ssw-twist_design.txt
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/design/ssw-twist_design.txt
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/design/ssw-twist_design.txt
diff --git a/examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_fun3d.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_mesh_fun3d.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_fun3d.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_mesh_fun3d.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_fun3d_egads.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_mesh_fun3d_egads.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_fun3d_egads.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_mesh_fun3d_egads.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_tacs.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_mesh_tacs.py
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_tacs.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_mesh_tacs.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingAero.csm b/examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_test_wingAero.csm
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingAero.csm
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_test_wingAero.csm
diff --git a/examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingSolid.csm b/examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_test_wingSolid.csm
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingSolid.csm
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_test_wingSolid.csm
diff --git a/examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingStruct.csm b/examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_test_wingStruct.csm
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingStruct.csm
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/_test_wingStruct.csm
diff --git a/examples/fun3d_examples/_ssw-inviscid/geometry/ssw.csm b/examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/ssw.csm
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/geometry/ssw.csm
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/ssw.csm
diff --git a/examples/fun3d_examples/_ssw-inviscid/geometry/wingAero.udc b/examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/wingAero.udc
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/geometry/wingAero.udc
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/wingAero.udc
diff --git a/examples/fun3d_examples/_ssw-inviscid/geometry/wingSolid.udc b/examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/wingSolid.udc
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/geometry/wingSolid.udc
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/wingSolid.udc
diff --git a/examples/fun3d_examples/_ssw-inviscid/geometry/wingStruct.udc b/examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/wingStruct.udc
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/geometry/wingStruct.udc
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/geometry/wingStruct.udc
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/.gitignore b/examples/fun3d_examples/ssw/aeroelastic_optimization/struct/.gitignore
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/.gitignore
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/struct/.gitignore
diff --git a/examples/fun3d_examples/_ssw-inviscid/struct/README.md b/examples/fun3d_examples/ssw/aeroelastic_optimization/struct/README.md
similarity index 100%
rename from examples/fun3d_examples/_ssw-inviscid/struct/README.md
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/struct/README.md
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/struct/build_exploded_mesh.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/struct/build_exploded_mesh.py
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/struct/build_exploded_mesh.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/struct/build_exploded_mesh.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/test_fun3d_tacs.py b/examples/fun3d_examples/ssw/aeroelastic_optimization/test_fun3d_tacs.py
similarity index 88%
rename from examples/fun3d_examples/_ssw-inviscid/test_fun3d_tacs.py
rename to examples/fun3d_examples/ssw/aeroelastic_optimization/test_fun3d_tacs.py
index c9a8f71c..b006d640 100644
--- a/examples/fun3d_examples/_ssw-inviscid/test_fun3d_tacs.py
+++ b/examples/fun3d_examples/ssw/aeroelastic_optimization/test_fun3d_tacs.py
@@ -41,7 +41,7 @@
comm=comm,
problem_name="capsStruct1",
active_procs=[0],
- verbosity=1,
+ verbosity=0,
)
tacs_model.mesh_aim.set_mesh(
edge_pt_min=2,
@@ -73,18 +73,14 @@
# BODIES AND STRUCT DVs
# <----------------------------------------------------
-# wing = Body.aeroelastic("wing", boundary=3).relaxation(
-# AitkenRelaxation(theta_init=0.6, theta_max=0.95)
-# )
+
wing = Body.aeroelastic("wing", boundary=3)
# setup the material and shell properties
aluminum = caps2tacs.Isotropic.aluminum().register_to(tacs_model)
-# nribs = int(tacs_model.get_config_parameter("nribs"))
-# nspars = int(tacs_model.get_config_parameter("nspars"))
-nribs = 7
-nspars = 4
+nribs = int(tacs_model.get_config_parameter("nribs")) # 7
+nspars = int(tacs_model.get_config_parameter("nspars")) # 2
nOML = nribs - 1
for irib in range(1, nribs + 1):
@@ -142,7 +138,7 @@
# <----------------------------------------------------
# make a funtofem scenario
-cruise = Scenario.steady("cruise", steps=100, uncoupled_steps=0)
+cruise = Scenario.steady("cruise_inviscid", steps=100, uncoupled_steps=0)
cruise.adjoint_steps = 150
mass = Function.mass().optimize(
scale=1.0e-4, objective=True, plot=True, plot_name="mass"
@@ -153,8 +149,21 @@
cruise.include(ksfailure).include(Function.lift()).include(mass)
cruise.set_temperature(T_ref=T_inf, T_inf=T_inf)
cruise.set_flow_ref_vals(qinf=q_inf)
+
+cruise.set_stop_criterion(
+ early_stopping=True,
+ min_forward_steps=10,
+ min_adjoint_steps=10,
+ post_tight_forward_steps=0,
+ post_tight_adjoint_steps=0,
+ post_forward_coupling_freq=1,
+ post_adjoint_coupling_freq=1,
+)
+
cruise.register_to(f2f_model)
+cruise.print_summary(comm, "cruise.txt")
+
# ---------------------------------------------------->
# COMPOSITE FUNCTIONS
@@ -181,13 +190,14 @@
# <----------------------------------------------------
solvers = SolverManager(comm)
-solvers.flow = Fun3dInterface(
+solvers.flow = Fun3d14Interface(
comm,
f2f_model,
- fun3d_project_name="ssw-pw1.2",
fun3d_dir="cfd",
- forward_tol=1e-5,
- adjoint_tol=1e-4,
+ forward_stop_tolerance=1e-8,
+ forward_min_tolerance=1e-5,
+ adjoint_stop_tolerance=1e-7,
+ adjoint_min_tolerance=1e-4,
debug=global_debug_flag,
)
solvers.structural = TacsSteadyInterface.create_from_bdf(
@@ -214,17 +224,17 @@
# Derivative test
# <----------------------------------------------------
-if comm.rank == 0:
- f2f_driver.print_summary()
- f2f_model.print_summary()
+f2f_driver.print_summary(
+ comm, print_model=True, print_comm=True, filename="driver_summary.txt"
+)
max_rel_error = TestResult.derivative_test(
"fun3d+tacs-ssw1",
model=f2f_model,
driver=f2f_driver,
status_file=FILEPATH,
- complex_mode=True,
- epsilon=1e-30,
+ complex_mode=False,
+ epsilon=1e-5,
)
# ---------------------------------------------------->
diff --git a/examples/fun3d_examples/ssw/docs/ssw_model_viz.png b/examples/fun3d_examples/ssw/docs/ssw_model_viz.png
new file mode 100644
index 00000000..f6166f32
Binary files /dev/null and b/examples/fun3d_examples/ssw/docs/ssw_model_viz.png differ
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/.gitignore b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/.gitignore
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/.gitignore
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/.gitignore
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/1_panel_thickness.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/1_panel_thickness.py
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/1_panel_thickness.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/1_panel_thickness.py
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/2_aero_aoa.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/2_aero_aoa.py
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/2_aero_aoa.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/2_aero_aoa.py
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/3_geom_twist.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/3_geom_twist.py
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/3_geom_twist.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/3_geom_twist.py
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/4_oml_shape.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/4_oml_shape.py
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/4_oml_shape.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/4_oml_shape.py
diff --git a/examples/fun3d_examples/_ssw-inviscid/README.md b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/README.md
similarity index 65%
rename from examples/fun3d_examples/_ssw-inviscid/README.md
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/README.md
index dab4b080..10c9e3f1 100644
--- a/examples/fun3d_examples/_ssw-inviscid/README.md
+++ b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/README.md
@@ -1,6 +1,5 @@
# "Super Simple Wing"
Testing out fully-coupled aeroelastic optimizations on a simple wing geometry.
-This directory is only meant to run the inviscid cases. Script 1 panel thickness should be panel thickness inviscid.py
## Flight Conditions
Here we consider a steady cruise at FL 100 (approximately 3000 meters).
@@ -23,10 +22,4 @@ Stipulate flight speed of Mach 0.5 (164.1935 m/s), which results in a dynamic pr
## Pure Shape, Fully Coupled Optimizations
Twist variable is not used at root station (fixed there):
* 3_geom_twist.py - twist variables at each station
-* 4_oml_shape.py - twist + airfoil thickness at each station
-
-## Shape + Discipline DVs, Fully Coupled Optimization
-Putting it all together:
-* 5_shape_and_struct.py - geom twist variables + panel thickness struct variables
-* 6_shape_and_aero.py - geom twist variables + aero aoa variable + airfoil thickness
-* 7_kitchen_sink.py - put all previous variables together : geom twist, airfoil thickness, AOA, panel thickness
+* 4_oml_shape.py - twist + airfoil thickness at each station
\ No newline at end of file
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/_2_cfd_only.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/_2_cfd_only.py
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/_2_cfd_only.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/_2_cfd_only.py
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/_oneway_sizing.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/_oneway_sizing.py
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/_oneway_sizing.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/_oneway_sizing.py
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/_run_flow.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/_run_flow.py
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/_run_flow.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/_run_flow.py
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/struct/.gitignore b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/cruise/Adjoint/.gitignore
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/struct/.gitignore
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/cruise/Adjoint/.gitignore
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/.gitignore b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/cruise/Flow/.gitignore
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/.gitignore
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/cruise/Flow/.gitignore
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/fun3d.nml b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/cruise/Flow/fun3d.nml
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/fun3d.nml
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/cruise/Flow/fun3d.nml
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/moving_body.input b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/cruise/Flow/moving_body.input
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/moving_body.input
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/cruise/Flow/moving_body.input
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/ssw-turb.mapbc b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/cruise/Flow/ssw-turb.mapbc
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/ssw-turb.mapbc
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/cruise/Flow/ssw-turb.mapbc
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/cfd/cruise/Adjoint/.gitignore b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/inv-cruise/Adjoint/.gitignore
similarity index 100%
rename from examples/fun3d_examples/ssw_remesh_optimization/cfd/cruise/Adjoint/.gitignore
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/inv-cruise/Adjoint/.gitignore
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/cfd/cruise/Flow/.gitignore b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/inv-cruise/Flow/.gitignore
similarity index 100%
rename from examples/fun3d_examples/ssw_remesh_optimization/cfd/cruise/Flow/.gitignore
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/inv-cruise/Flow/.gitignore
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/inv-cruise/Flow/fun3d.nml b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/inv-cruise/Flow/fun3d.nml
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/inv-cruise/Flow/fun3d.nml
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/inv-cruise/Flow/fun3d.nml
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/inv-cruise/Flow/moving_body.input b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/inv-cruise/Flow/moving_body.input
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/inv-cruise/Flow/moving_body.input
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/inv-cruise/Flow/moving_body.input
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/inv-cruise/Flow/ssw-turb.mapbc b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/inv-cruise/Flow/ssw-turb.mapbc
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/inv-cruise/Flow/ssw-turb.mapbc
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/inv-cruise/Flow/ssw-turb.mapbc
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/loads/uncoupled_loads.txt b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/loads/uncoupled_loads.txt
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/loads/uncoupled_loads.txt
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/loads/uncoupled_loads.txt
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/struct/.gitignore b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Adjoint/.gitignore
similarity index 100%
rename from examples/fun3d_examples/ssw_remesh_optimization/struct/.gitignore
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Adjoint/.gitignore
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/1_geom_twist.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/.gitignore
similarity index 100%
rename from examples/fun3d_examples/ssw_remesh_optimization/1_geom_twist.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/.gitignore
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/fun3d.nml b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/fun3d.nml
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/fun3d.nml
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/fun3d.nml
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/moving_body.input b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/moving_body.input
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/moving_body.input
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/moving_body.input
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/perturb.input b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/perturb.input
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/perturb.input
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/perturb.input
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/ssw-pw1.2.b8.ugrid b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/ssw-pw1.2.b8.ugrid
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/ssw-pw1.2.b8.ugrid
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/ssw-pw1.2.b8.ugrid
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/ssw-pw1.2.mapbc b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/ssw-pw1.2.mapbc
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/cfd/pw-cruise/Flow/ssw-pw1.2.mapbc
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/cfd/pw-cruise/Flow/ssw-pw1.2.mapbc
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/design/.gitignore b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/design/.gitignore
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/design/.gitignore
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/design/.gitignore
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/design/sizing-oneway.txt b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/design/sizing-oneway.txt
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/design/sizing-oneway.txt
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/design/sizing-oneway.txt
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/design/sizing.txt b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/design/sizing.txt
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/design/sizing.txt
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/design/sizing.txt
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/geometry/_mesh_fun3d.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_mesh_fun3d.py
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/geometry/_mesh_fun3d.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_mesh_fun3d.py
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/geometry/_mesh_fun3d_egads.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_mesh_fun3d_egads.py
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/geometry/_mesh_fun3d_egads.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_mesh_fun3d_egads.py
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/geometry/_mesh_tacs.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_mesh_tacs.py
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/geometry/_mesh_tacs.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_mesh_tacs.py
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/geometry/_test_wingAero.csm b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_test_wingAero.csm
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/geometry/_test_wingAero.csm
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_test_wingAero.csm
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/geometry/_test_wingSolid.csm b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_test_wingSolid.csm
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/geometry/_test_wingSolid.csm
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_test_wingSolid.csm
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/geometry/_test_wingStruct.csm b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_test_wingStruct.csm
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/geometry/_test_wingStruct.csm
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/_test_wingStruct.csm
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/geometry/ssw.csm b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/ssw.csm
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/geometry/ssw.csm
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/ssw.csm
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/geometry/wingAero.udc b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/wingAero.udc
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/geometry/wingAero.udc
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/wingAero.udc
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/geometry/wingSolid.udc b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/wingSolid.udc
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/geometry/wingSolid.udc
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/wingSolid.udc
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/geometry/wingStruct.udc b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/wingStruct.udc
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/geometry/wingStruct.udc
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/geometry/wingStruct.udc
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/2_kitchen_sink.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/struct/.gitignore
similarity index 100%
rename from examples/fun3d_examples/ssw_remesh_optimization/2_kitchen_sink.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/struct/.gitignore
diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/struct/README.md b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/struct/README.md
similarity index 100%
rename from examples/fun3d_examples/ssw_meshdef_optimization/struct/README.md
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/struct/README.md
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/struct/build_exploded_mesh.py b/examples/fun3d_examples/ssw/ssw_meshdef_optimization/struct/build_exploded_mesh.py
similarity index 100%
rename from examples/fun3d_examples/ssw_remesh_optimization/struct/build_exploded_mesh.py
rename to examples/fun3d_examples/ssw/ssw_meshdef_optimization/struct/build_exploded_mesh.py
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/README.md b/examples/fun3d_examples/ssw_remesh_optimization/README.md
deleted file mode 100644
index 6fc04f91..00000000
--- a/examples/fun3d_examples/ssw_remesh_optimization/README.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# "Super Simple" Wing
-Testing out fully-coupled aeroelastic optimizations on a simple wing geometry.
-
-## Preliminary Sizing
-* Run the _run_flow.py to get the aero loads file
-* Use the aero loads file to do a oneway sizing optimization (for panel thicknesses)
-
-## No Shape, Fully Coupled Optimizations
-* 1_panel_thickness.py
-* 2_aero_aoa.py
-
-## Pure Shape, Fully Coupled Optimizations
-Twist variable is not used at root station (fixed there)
-* 3_geom_twist.py - twist variables at each station
-* 4_oml_shape.py - twist + airfoil thickness at each station
-
-## Shape + Discipline DVs, Fully Coupled Optimization
-Putting it all together
-* 5_shape_and_struct.py - geom twist variables + panel thickness struct variables
-* 6_shape_and_aero.py - geom twist variables + aero aoa variable + airfoil thickness
-* 7_kitchen_sink.py - put all previous variables together : geom twist, airfoil thickness, AOA, panel thickness
\ No newline at end of file
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/geometry/_mesh_fun3d.py b/examples/fun3d_examples/ssw_remesh_optimization/geometry/_mesh_fun3d.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/geometry/_mesh_tacs.py b/examples/fun3d_examples/ssw_remesh_optimization/geometry/_mesh_tacs.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/geometry/naca_wing.csm b/examples/fun3d_examples/ssw_remesh_optimization/geometry/naca_wing.csm
deleted file mode 100644
index 2c268f2e..00000000
--- a/examples/fun3d_examples/ssw_remesh_optimization/geometry/naca_wing.csm
+++ /dev/null
@@ -1,239 +0,0 @@
-# Naca wing example
-# Sean Engelstad August 2023
-# SMDO Lab Georgia Tech, Prof. Graeme Kennedy
-
-cfgpmtr view:flow 1
-cfgpmtr view:struct 0
-
-#--- ifthen view:flow EQ 1
-#--- ifthen view:struct EQ 1
-#--- throw 999
-#--- endif
-#--- endif
-
-# aero design parameters
-
-despmtr ff 20.0
-despmtr sspan 5.0
-despmtr camber 0.0
-despmtr thickness 0.12
-despmtr aoa 0.0
-
-### Design parameters ###
-
-# number of ribs and spars
-cfgpmtr nribs 8
-cfgpmtr nspars 2
-
-# spar fraction despmtrs
-despmtr spar_a1 1.0
-despmtr spar_a2 0.0
-set spar_a3 1.0-spar_a1-spar_a2
-
-# rib fraction despmtrs
-despmtr rib_a1 1.0
-despmtr rib_a2 0.0
-set rib_a3 1.0-rib_a1-rib_a2
-
-
-# Depedendent parameters
-set margin1 5.0
-set margin2 3.0
-set margin3 10.0
-
-# wing solid body
-mark
-udprim naca camber camber thickness thickness
-udprim naca camber camber thickness thickness
-translate 0 0 -sspan
-loft 0
-translate 0 0 0.5*sspan
-store wingSolidBody
-
-ifthen view:struct EQ 1
-
- # make upper and lower bodies
- box -1.0 0.0 0.5*sspan+0.1 3.0 1.0 -sspan-0.2
- store upperBox
- box -1.0 0.0 0.5*sspan+0.1 3.0 -1.0 -sspan-0.2
- store lowerBox
-
- restore wingSolidBody
- restore upperBox
- intersect
- select face 4
- attribute capsGroup $rib1
- attribute _color $green
- select face 2
- attribute capsGroup !$rib+nribs
- attribute _color $green
- select face 1 # main OML
- attribute capsGroup $OML
- attribute _color $blue
- select face 5 # TE OML
- attribute capsGroup $OML
- attribute _color $blue
- store upperWing
-
- restore wingSolidBody
- restore lowerBox
- intersect
- select face 4
- attribute capsGroup $rib1
- attribute _color $green
- select face 2
- attribute capsGroup !$rib+nribs
- attribute _color $green
- select face 1 # main OML
- attribute capsGroup $OML
- attribute _color $blue
- select face 3 # TE OML
- attribute capsGroup $OML
- attribute _color $blue
- store lowerWing
-
- ### Wing OML ###
- restore upperWing
- restore lowerWing
- union
- extract 0
-
- store OML
-
- ### Internal Structure ###
-
- # make the spars
- patbeg ispar nspars
-
- # compute the positions of the spars at root and tip
- set fr ispar/(nspars+1)
- set sparFrac fr*(spar_a1+fr*(spar_a2+fr*spar_a3))
- set xroot sparFrac
- set xtip sparFrac
-
- # make the panel to intersect
- box xroot -margin1 margin2 0.0 2*margin1 -sspan-2*margin2
-
- # add caps attributes
- select face
- attribute capsGroup !$spar+ispar
- attribute _color $green
-
- patend
-
- # make the ribs except for end cap ribs
- set ninnerRibs nribs-2
- patbeg index ninnerRibs
-
- # compute the spanwise portion of the rib
- set irib index+1
- set fr (irib-1)/(nribs-1)
- set ribFrac fr*(rib_a1+fr*(rib_a2+fr*rib_a3))
- set ypos -ribFrac*sspan
-
- # make the panel of the rib
- box -margin3 -margin2 ypos 2*margin3 2*margin2 0
-
- # add caps attributes
- select face
- attribute capsGroup !$rib+irib
- attribute _color $green
-
- # union with previous ribs/spars
- ifthen index eq 1
- patbeg ispar nspars
- union
- patend
- else
- union
- endif
-
- patend
- # center to match wing
- translate 0 0 0.5*sspan
- restore wingSolidBody
- intersect
- store internalStruct
-
- restore internalStruct
- restore upperBox
- intersect
- select face
- attribute thermalFace $upper
-
- restore internalStruct
- restore lowerBox
- intersect
- select face
- attribute thermalFace $lower
-
- union
-
- # join OML and internal structure
- restore OML
- union
-
- ### 4. Final Caps Attributes ###
-
- # add constraint attributes to root airfoil
- select face $capsGroup $rib1
- attribute capsConstraint $root
-
- # add the constraint attribute to adjacent edges, nodes
- # otherwise they can pop out of the mesh in the structural analysis
- udprim editAttr filename <<
- EDGE ADJ2FACE capsConstraint=root
- SET capsConstraint=root
-
- NODE ADJ2FACE capsConstraint=root
- SET capsConstraint=root
-
- EDGE ADJ2FACE thermalFace=upper
- AND ADJ2FACE thermalFace=lower
- SET capsConstraint=midplane
- >>
-
- # add load attribute to OML
- select face $capsGroup $OML
- attribute capsLoad $OML
-
- # add AIM attribute to specify the analyses to use
- select body
- attribute capsAIM $egadsTessAIM;tacsAIM
-
- rotatex 90 0 0
- rotatez -aoa 0 0
-
-endif
-
-ifthen view:flow EQ 1
- box -ff -ff -ff 2*ff 2*ff 2*ff
- attribute capsGroup $Farfield
- attribute capsMesh $Farfield
- ATTRIBUTE AFLR_GBC $FARFIELD_UG3_GBC
- ATTRIBUTE AFLR4_CMP_ID 2
- ATTRIBUTE AFLR4_Scale_Factor 10.0
-
- restore wingSolidBody
- select face
- attribute capsGroup $wall
- attribute capsMesh $wall
- attribute AFLR4_Cmp_Id 1
- attribute AFLR4_Edge_Refinement_Weight 0.1
-
- subtract
-
- select body
- attribute capsAIM $fun3dAIM;aflr4AIM;aflr3AIM
- attribute capsMeshLength 1.0
-
- rotatex 90 0 0
- rotatez -aoa 0 0
-endif
-
-
-
-end
-|||||||||||
-||||||||||||||||||||||||||||||
-|||||||||||||||||||||||||||||||
\ No newline at end of file
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/geometry/wing.udc b/examples/fun3d_examples/ssw_remesh_optimization/geometry/wing.udc
deleted file mode 100644
index e69de29b..00000000
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/geometry/wingSolid.udc b/examples/fun3d_examples/ssw_remesh_optimization/geometry/wingSolid.udc
deleted file mode 100644
index e69de29b..00000000
diff --git a/examples/fun3d_examples/ssw_remesh_optimization/struct/README.md b/examples/fun3d_examples/ssw_remesh_optimization/struct/README.md
deleted file mode 100644
index 584f0213..00000000
--- a/examples/fun3d_examples/ssw_remesh_optimization/struct/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# STRUCT MESH FOLDER
-Put the tacs.dat and tacs.bdf file here.
\ No newline at end of file