|
2 | 2 | Sean Engelstad, May 2023
|
3 | 3 | GT SMDO Lab, Dr. Graeme Kennedy
|
4 | 4 | OnewayStructDriver example
|
| 5 | +
|
| 6 | +Source for callback: Alasdair Christison Gray |
5 | 7 | """
|
6 | 8 |
|
7 | 9 | from funtofem import *
|
8 | 10 | from tacs import caps2tacs
|
9 | 11 | import openmdao.api as om
|
10 | 12 | from mpi4py import MPI
|
11 | 13 |
|
| 14 | +from _blade_callback import blade_elemCallBack |
| 15 | + |
12 | 16 | # --------------------------------------------------------------#
|
13 | 17 | # Setup CAPS Problem and FUNtoFEM model - NOTE: not complete, needs stringer PR
|
14 | 18 | # --------------------------------------------------------------#
|
|
31 | 35 | )
|
32 | 36 | tacs_aim = tacs_model.tacs_aim
|
33 | 37 |
|
34 |
| -aluminum = caps2tacs.Isotropic.aluminum() |
35 |
| -aluminum_stringer = caps2tacs.Orthotropic.smeared_stringer( |
36 |
| - aluminum, area_ratio=0.5 |
37 |
| -).register_to(tacs_model) |
38 |
| - |
39 | 38 | # setup the thickness design variables + automatic shell properties
|
40 | 39 | # using Composite functions, this part has to go after all funtofem variables are defined...
|
41 | 40 | nribs = int(tacs_model.get_config_parameter("nribs"))
|
42 | 41 | nspars = int(tacs_model.get_config_parameter("nspars"))
|
43 | 42 | nOML = int(tacs_aim.get_output_parameter("nOML"))
|
44 | 43 |
|
| 44 | +null_material = caps2tacs.Orthotropic.null().register_to(tacs_model) |
| 45 | + |
45 | 46 | init_thickness = 0.08
|
46 | 47 | for irib in range(1, nribs + 1):
|
47 | 48 | name = f"rib{irib}"
|
48 |
| - caps2tacs.CompositeProperty.one_ply( |
49 |
| - caps_group=name, |
50 |
| - material=aluminum_stringer, |
51 |
| - thickness=init_thickness, |
52 |
| - ply_angle=0, |
53 |
| - ).register_to(tacs_model) |
| 49 | + caps2tacs.CompositeProperty.null(name, null_material).register_to(tacs_model) |
54 | 50 | Variable.structural(name, value=init_thickness).set_bounds(
|
55 | 51 | lower=0.01, upper=0.2, scale=100.0
|
56 | 52 | ).register_to(wing)
|
57 | 53 |
|
58 | 54 | for ispar in range(1, nspars + 1):
|
59 | 55 | name = f"spar{ispar}"
|
60 |
| - caps2tacs.CompositeProperty.one_ply( |
61 |
| - caps_group=name, |
62 |
| - material=aluminum_stringer, |
63 |
| - thickness=init_thickness, |
64 |
| - ply_angle=0, |
65 |
| - ).register_to(tacs_model) |
| 56 | + caps2tacs.CompositeProperty.null(name, null_material).register_to(tacs_model) |
66 | 57 | Variable.structural(name, value=init_thickness).set_bounds(
|
67 | 58 | lower=0.01, upper=0.2, scale=100.0
|
68 | 59 | ).register_to(wing)
|
69 | 60 |
|
70 | 61 | for iOML in range(1, nOML + 1):
|
71 | 62 | name = f"OML{iOML}"
|
72 |
| - caps2tacs.CompositeProperty.one_ply( |
73 |
| - caps_group=name, |
74 |
| - material=aluminum_stringer, |
75 |
| - thickness=init_thickness, |
76 |
| - ply_angle=0, |
77 |
| - ).register_to(tacs_model) |
| 63 | + caps2tacs.CompositeProperty.null(name, null_material).register_to(tacs_model) |
78 | 64 | Variable.structural(name, value=init_thickness).set_bounds(
|
79 | 65 | lower=0.01, upper=0.2, scale=100.0
|
80 | 66 | ).register_to(wing)
|
|
91 | 77 |
|
92 | 78 | # make the scenario(s)
|
93 | 79 | tacs_scenario = Scenario.steady("tacs", steps=100)
|
94 |
| -Function.mass().optimize(scale=1.0e-2, objective=True, plot=True).register_to( |
95 |
| - tacs_scenario |
96 |
| -) |
97 | 80 | Function.ksfailure(ks_weight=10.0).optimize(
|
98 | 81 | scale=30.0, upper=0.267, objective=False, plot=True
|
99 | 82 | ).register_to(tacs_scenario)
|
| 83 | +Function.mass().optimize(scale=1.0e-2, objective=True, plot=True).register_to( |
| 84 | + tacs_scenario |
| 85 | +) |
100 | 86 | tacs_scenario.register_to(f2f_model)
|
101 | 87 |
|
102 | 88 | # make the composite functions for adjacency constraints
|
103 | 89 | variables = f2f_model.get_variables()
|
104 |
| -adjacency_ratio = 2.0 |
| 90 | +adj_value = 0.002 |
105 | 91 | adjacency_scale = 10.0
|
106 | 92 | for irib in range(
|
107 | 93 | 1, nribs
|
108 | 94 | ): # not (1, nribs+1) bc we want to do one less since we're doing nribs-1 pairs
|
109 | 95 | left_rib = f2f_model.get_variables(names=f"rib{irib}")
|
110 | 96 | right_rib = f2f_model.get_variables(names=f"rib{irib+1}")
|
111 | 97 | # make a composite function for relative diff in rib thicknesses
|
112 |
| - adjacency_rib_constr = (left_rib - right_rib) / left_rib |
| 98 | + adjacency_rib_constr = left_rib - right_rib |
113 | 99 | adjacency_rib_constr.set_name(f"rib{irib}-{irib+1}").optimize(
|
114 |
| - lower=-adjacency_ratio, upper=adjacency_ratio, scale=1.0, objective=False |
| 100 | + lower=-adj_value, upper=adj_value, scale=1.0, objective=False |
115 | 101 | ).register_to(f2f_model)
|
116 | 102 |
|
117 | 103 | for ispar in range(1, nspars):
|
118 | 104 | left_spar = f2f_model.get_variables(names=f"spar{ispar}")
|
119 | 105 | right_spar = f2f_model.get_variables(names=f"spar{ispar+1}")
|
120 | 106 | # make a composite function for relative diff in spar thicknesses
|
121 |
| - adjacency_spar_constr = (left_spar - right_spar) / left_spar |
| 107 | + adjacency_spar_constr = left_spar - right_spar |
122 | 108 | adjacency_spar_constr.set_name(f"spar{ispar}-{ispar+1}").optimize(
|
123 |
| - lower=-adjacency_ratio, upper=adjacency_ratio, scale=1.0, objective=False |
| 109 | + lower=-adj_value, upper=adj_value, scale=1.0, objective=False |
124 | 110 | ).register_to(f2f_model)
|
125 | 111 |
|
126 | 112 | for iOML in range(1, nOML):
|
127 | 113 | left_OML = f2f_model.get_variables(names=f"OML{iOML}")
|
128 | 114 | right_OML = f2f_model.get_variables(names=f"OML{iOML+1}")
|
129 | 115 | # make a composite function for relative diff in OML thicknesses
|
130 |
| - adj_OML_constr = (left_OML - right_OML) / left_OML |
| 116 | + adj_OML_constr = left_OML - right_OML |
131 | 117 | adj_OML_constr.set_name(f"OML{iOML}-{iOML+1}").optimize(
|
132 |
| - lower=-adjacency_ratio, upper=adjacency_ratio, scale=1.0, objective=False |
| 118 | + lower=-adj_value, upper=adj_value, scale=1.0, objective=False |
133 | 119 | ).register_to(f2f_model)
|
134 | 120 |
|
135 | 121 | # make the BDF and DAT file for TACS structural analysis
|
136 | 122 | tacs_aim.setup_aim()
|
137 | 123 | tacs_aim.pre_analysis()
|
138 | 124 |
|
| 125 | + |
139 | 126 | # build the solver manager, no tacs interface since built for each new shape
|
140 | 127 | # in the tacs driver
|
141 | 128 | solvers = SolverManager(comm)
|
|
144 | 131 | model=f2f_model,
|
145 | 132 | comm=comm,
|
146 | 133 | nprocs=1,
|
147 |
| - bdf_file=tacs_aim.dat_file_path, |
148 |
| - prefix=tacs_aim.analysis_dir, |
| 134 | + bdf_file=tacs_aim.root_dat_file, |
| 135 | + prefix=tacs_aim.root_analysis_dir, |
| 136 | + callback=blade_elemCallBack, |
149 | 137 | )
|
150 | 138 | solvers.flow.copy_struct_mesh()
|
151 | 139 | null_driver = NullDriver(solvers, model=f2f_model, transfer_settings=None)
|
152 | 140 |
|
153 | 141 | # build the tacs oneway driver
|
154 | 142 | tacs_driver = OnewayStructDriver.prime_loads(driver=null_driver)
|
155 | 143 |
|
156 |
| -# --------------------------------------------------------------------------# |
157 |
| -# Setup OpenMDAO Problem and Perform Sizing Optimization on the Wing |
158 |
| -# --------------------------------------------------------------------------# |
159 |
| - |
160 |
| -# setup the OpenMDAO Problem object |
161 |
| -prob = om.Problem() |
162 |
| - |
163 |
| -# Create the OpenMDAO component using the built-in Funtofem component |
164 |
| -design_out_file = "design-test.txt" |
165 |
| -f2f_subsystem = FuntofemComponent( |
166 |
| - driver=tacs_driver, write_dir=tacs_aim.analysis_dir, design_out_file=design_out_file |
167 |
| -) |
168 |
| -prob.model.add_subsystem("f2fSystem", f2f_subsystem) |
169 |
| -f2f_subsystem.register_to_model(prob.model, "f2fSystem") |
170 |
| - |
171 |
| -# setup the optimizer settings # COBYLA for auto-FDing |
172 |
| -optimizer = "scipy" |
173 |
| -if optimizer == "scipy": |
174 |
| - prob.driver = om.ScipyOptimizeDriver(optimizer="SLSQP", tol=1.0e-9, disp=True) |
175 |
| -elif optimizer == "pyoptsparse": |
176 |
| - prob.driver = om.pyOptSparseDriver(optimizer="SNOPT") |
177 |
| - # prob.driver.opt_settings['Major feasibility tolerance'] = 1e-5 # lower tolerance to prevent tight oscillations |
178 |
| - |
179 |
| -# Start the optimization |
180 |
| -print("\n==> Starting optimization...") |
181 |
| -prob.setup() |
182 |
| - |
183 |
| -debug = False |
184 |
| -if debug: |
185 |
| - print("Checking partials...", flush=True) |
186 |
| - prob.check_partials(compact_print=True) |
187 |
| - |
188 |
| -else: |
189 |
| - prob.run_driver() |
190 |
| - |
191 |
| - # report the final optimal design |
192 |
| - design_hdl = f2f_subsystem._design_hdl |
193 |
| - for var in f2f_model.get_variables(): |
194 |
| - opt_value = prob.get_val(f"f2fSystem.{var.name}") |
195 |
| - design_hdl.write(f"\t{var.name} = {opt_value}") |
196 |
| - |
197 |
| - prob.cleanup() |
198 |
| - # close the design hdl file |
199 |
| - f2f_subsystem.cleanup() |
| 144 | +tacs_driver.solve_forward() |
| 145 | +# tacs_driver.solve_adjoint() |
0 commit comments