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: + +![SST optimized design](results/sst_opt_design.png) + +FUN3D flow solution over the SST wing: + +![SST FUN3D flow](results/sst_fun3d_flow.png) 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. + +![SSW model visualization](docs/ssw_model_viz.png) + +## 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