Skip to content

Commit bfaccce

Browse files
Accept Element Callbacks with TACS and ESP/CAPS + Blade Stiffener Example (#308)
* Blade stiffened callback example with new TACS component write-outs from ESP/CAPS
1 parent 2ef23b5 commit bfaccce

File tree

5 files changed

+222
-84
lines changed

5 files changed

+222
-84
lines changed

examples/framework/tacs_oneway_naca_wing/1_tacs_sizing_opt.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@
132132
model=f2f_model,
133133
comm=comm,
134134
nprocs=1,
135-
bdf_file=tacs_aim.dat_file_path,
136-
prefix=tacs_aim.analysis_dir,
135+
bdf_file=tacs_aim.root_dat_file_path,
136+
prefix=tacs_aim.root_analysis_dir,
137137
)
138138
solvers.flow.copy_struct_mesh()
139139
null_driver = NullDriver(solvers, model=f2f_model, transfer_settings=None)

examples/framework/tacs_oneway_naca_wing/2_tacs_sizing_and_shape_opt.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@
143143
model=f2f_model,
144144
comm=comm,
145145
nprocs=1,
146-
bdf_file=tacs_aim.dat_file_path,
147-
prefix=tacs_aim.analysis_dir,
146+
bdf_file=tacs_aim.root_dat_file_path,
147+
prefix=tacs_aim.root_analysis_dir,
148148
)
149149
solvers.flow.copy_struct_mesh()
150150
null_driver = NullDriver(solvers, model=f2f_model, transfer_settings=None)

examples/framework/tacs_oneway_naca_wing/3_tacs_sizing_opt_stringer.py examples/framework/tacs_oneway_naca_wing/3_tacs_blade_stiffened.py

+25-79
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22
Sean Engelstad, May 2023
33
GT SMDO Lab, Dr. Graeme Kennedy
44
OnewayStructDriver example
5+
6+
Source for callback: Alasdair Christison Gray
57
"""
68

79
from funtofem import *
810
from tacs import caps2tacs
911
import openmdao.api as om
1012
from mpi4py import MPI
1113

14+
from _blade_callback import blade_elemCallBack
15+
1216
# --------------------------------------------------------------#
1317
# Setup CAPS Problem and FUNtoFEM model - NOTE: not complete, needs stringer PR
1418
# --------------------------------------------------------------#
@@ -31,50 +35,32 @@
3135
)
3236
tacs_aim = tacs_model.tacs_aim
3337

34-
aluminum = caps2tacs.Isotropic.aluminum()
35-
aluminum_stringer = caps2tacs.Orthotropic.smeared_stringer(
36-
aluminum, area_ratio=0.5
37-
).register_to(tacs_model)
38-
3938
# setup the thickness design variables + automatic shell properties
4039
# using Composite functions, this part has to go after all funtofem variables are defined...
4140
nribs = int(tacs_model.get_config_parameter("nribs"))
4241
nspars = int(tacs_model.get_config_parameter("nspars"))
4342
nOML = int(tacs_aim.get_output_parameter("nOML"))
4443

44+
null_material = caps2tacs.Orthotropic.null().register_to(tacs_model)
45+
4546
init_thickness = 0.08
4647
for irib in range(1, nribs + 1):
4748
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)
5450
Variable.structural(name, value=init_thickness).set_bounds(
5551
lower=0.01, upper=0.2, scale=100.0
5652
).register_to(wing)
5753

5854
for ispar in range(1, nspars + 1):
5955
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)
6657
Variable.structural(name, value=init_thickness).set_bounds(
6758
lower=0.01, upper=0.2, scale=100.0
6859
).register_to(wing)
6960

7061
for iOML in range(1, nOML + 1):
7162
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)
7864
Variable.structural(name, value=init_thickness).set_bounds(
7965
lower=0.01, upper=0.2, scale=100.0
8066
).register_to(wing)
@@ -91,51 +77,52 @@
9177

9278
# make the scenario(s)
9379
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-
)
9780
Function.ksfailure(ks_weight=10.0).optimize(
9881
scale=30.0, upper=0.267, objective=False, plot=True
9982
).register_to(tacs_scenario)
83+
Function.mass().optimize(scale=1.0e-2, objective=True, plot=True).register_to(
84+
tacs_scenario
85+
)
10086
tacs_scenario.register_to(f2f_model)
10187

10288
# make the composite functions for adjacency constraints
10389
variables = f2f_model.get_variables()
104-
adjacency_ratio = 2.0
90+
adj_value = 0.002
10591
adjacency_scale = 10.0
10692
for irib in range(
10793
1, nribs
10894
): # not (1, nribs+1) bc we want to do one less since we're doing nribs-1 pairs
10995
left_rib = f2f_model.get_variables(names=f"rib{irib}")
11096
right_rib = f2f_model.get_variables(names=f"rib{irib+1}")
11197
# 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
11399
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
115101
).register_to(f2f_model)
116102

117103
for ispar in range(1, nspars):
118104
left_spar = f2f_model.get_variables(names=f"spar{ispar}")
119105
right_spar = f2f_model.get_variables(names=f"spar{ispar+1}")
120106
# 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
122108
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
124110
).register_to(f2f_model)
125111

126112
for iOML in range(1, nOML):
127113
left_OML = f2f_model.get_variables(names=f"OML{iOML}")
128114
right_OML = f2f_model.get_variables(names=f"OML{iOML+1}")
129115
# 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
131117
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
133119
).register_to(f2f_model)
134120

135121
# make the BDF and DAT file for TACS structural analysis
136122
tacs_aim.setup_aim()
137123
tacs_aim.pre_analysis()
138124

125+
139126
# build the solver manager, no tacs interface since built for each new shape
140127
# in the tacs driver
141128
solvers = SolverManager(comm)
@@ -144,56 +131,15 @@
144131
model=f2f_model,
145132
comm=comm,
146133
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,
149137
)
150138
solvers.flow.copy_struct_mesh()
151139
null_driver = NullDriver(solvers, model=f2f_model, transfer_settings=None)
152140

153141
# build the tacs oneway driver
154142
tacs_driver = OnewayStructDriver.prime_loads(driver=null_driver)
155143

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

Comments
 (0)