diff --git a/examples/fun3d_examples/_ssw-testing/.gitignore b/examples/fun3d_examples/_ssw-inviscid/.gitignore similarity index 100% rename from examples/fun3d_examples/_ssw-testing/.gitignore rename to examples/fun3d_examples/_ssw-inviscid/.gitignore diff --git a/examples/fun3d_examples/_ssw-testing/1_panel_thickness.py b/examples/fun3d_examples/_ssw-inviscid/1_panel_thickness.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/1_panel_thickness.py rename to examples/fun3d_examples/_ssw-inviscid/1_panel_thickness.py diff --git a/examples/fun3d_examples/_ssw-testing/2_aero_aoa.py b/examples/fun3d_examples/_ssw-inviscid/2_aero_aoa.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/2_aero_aoa.py rename to examples/fun3d_examples/_ssw-inviscid/2_aero_aoa.py diff --git a/examples/fun3d_examples/_ssw-testing/3_geom_twist.py b/examples/fun3d_examples/_ssw-inviscid/3_geom_twist.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/3_geom_twist.py rename to examples/fun3d_examples/_ssw-inviscid/3_geom_twist.py diff --git a/examples/fun3d_examples/_ssw-testing/4_oml_shape.py b/examples/fun3d_examples/_ssw-inviscid/4_oml_shape.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/4_oml_shape.py rename to examples/fun3d_examples/_ssw-inviscid/4_oml_shape.py diff --git a/examples/fun3d_examples/_ssw-testing/README.md b/examples/fun3d_examples/_ssw-inviscid/README.md similarity index 89% rename from examples/fun3d_examples/_ssw-testing/README.md rename to examples/fun3d_examples/_ssw-inviscid/README.md index f9232b5f..dab4b080 100644 --- a/examples/fun3d_examples/_ssw-testing/README.md +++ b/examples/fun3d_examples/_ssw-inviscid/README.md @@ -1,5 +1,6 @@ # "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). @@ -28,4 +29,4 @@ Twist variable is not used at root station (fixed there): 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 +* 7_kitchen_sink.py - put all previous variables together : geom twist, airfoil thickness, AOA, panel thickness diff --git a/examples/fun3d_examples/_ssw-testing/_2_cfd_only.py b/examples/fun3d_examples/_ssw-inviscid/_2_cfd_only.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/_2_cfd_only.py rename to examples/fun3d_examples/_ssw-inviscid/_2_cfd_only.py diff --git a/examples/fun3d_examples/_ssw-testing/_oneway_sizing.py b/examples/fun3d_examples/_ssw-inviscid/_oneway_sizing.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/_oneway_sizing.py rename to examples/fun3d_examples/_ssw-inviscid/_oneway_sizing.py diff --git a/examples/fun3d_examples/_ssw-inviscid/_panel_thickness_inviscid.py b/examples/fun3d_examples/_ssw-inviscid/_panel_thickness_inviscid.py new file mode 100644 index 00000000..45a6e7f5 --- /dev/null +++ b/examples/fun3d_examples/_ssw-inviscid/_panel_thickness_inviscid.py @@ -0,0 +1,312 @@ +""" +1_panel_thickness.py + +Run a coupled optimization of the panel thicknesses of the wing structure. +No shape variables are included in this optimization. +This example is finished and converged well in SNOPT +""" + +from pyoptsparse import SNOPT, Optimization +from funtofem import * +from mpi4py import MPI +from tacs import caps2tacs +import os, time + +comm = MPI.COMM_WORLD + +base_dir = os.path.dirname(os.path.abspath(__file__)) +csm_path = os.path.join(base_dir, "geometry", "ssw.csm") + +# Optimization options +hot_start = False +store_history = True + +test_derivatives = False + +nprocs_tacs = 8 + +global_debug_flag = False + +# Derivative test stuff +FILENAME = "complex-step.txt" +FILEPATH = os.path.join(base_dir, FILENAME) + +aitken_file = os.path.join(base_dir, "aitken-hist.txt") + +# FUNTOFEM MODEL +# <---------------------------------------------------- +# Freestream quantities -- see README +T_inf = 268.338 # Freestream temperature +q_inf = 1.21945e4 # Dynamic pressure + +# Construct the FUNtoFEM model +f2f_model = FUNtoFEMmodel("ssw-sizing1") +tacs_model = caps2tacs.TacsModel.build( + csm_file=csm_path, + comm=comm, + problem_name="capsStruct1", + active_procs=[0], + verbosity=1, +) +tacs_model.mesh_aim.set_mesh( + 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("view:flow", 0) +tacs_aim.set_config_parameter("view:struct", 1) + +for proc in tacs_aim.active_procs: + if comm.rank == proc: + aim = tacs_model.mesh_aim.aim + aim.input.Mesh_Sizing = { + "chord": {"numEdgePoints": 20}, + "span": {"numEdgePoints": 8}, + "vert": {"numEdgePoints": 4}, + } + +# add tacs constraints in +caps2tacs.PinConstraint("root").register_to(tacs_model) + +# ----------------------------------------------------> + +# BODIES AND STRUCT DVs +# <---------------------------------------------------- + +# wing = Body.aeroelastic("wing", boundary=3).relaxation( +# AitkenRelaxation( +# theta_init=0.6, theta_max=0.95, history_file=aitken_file, debug=True +# ) +# ) +wing = Body.aeroelastic("wing", boundary=2) + +# 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")) +nOML = nribs - 1 + +for irib in range(1, nribs + 1): + name = f"rib{irib}" + prop = caps2tacs.ShellProperty( + caps_group=name, material=aluminum, 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=aluminum, 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=aluminum, 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=aluminum, 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 +cruise = Scenario.steady( + "cruise_inviscid", steps=300, coupling_frequency=30, uncoupled_steps=0 +) +cruise.adjoint_steps = ( + 100 # outer coupling iterations, total 5000 flow adjoints, 100 grid adjoints +) +cruise.set_stop_criterion(early_stopping=True, min_adjoint_steps=20) + +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-cruise" +) +cruise.include(ksfailure).include(mass) +cruise.set_temperature(T_ref=T_inf, T_inf=T_inf) +cruise.set_flow_ref_vals(qinf=q_inf) +cruise.register_to(f2f_model) + +# ----------------------------------------------------> + +# COMPOSITE FUNCTIONS +# <---------------------------------------------------- + +# skin thickness adjacency constraints +if not test_derivatives: + 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 = left_var - right_var + adj_diff = 0.002 + adj_constr.set_name(f"{prefix}{iconstr}-{iconstr+1}").optimize( + lower=-adj_diff, upper=adj_diff, scale=1.0, objective=False + ).register_to(f2f_model) + + +# ----------------------------------------------------> + +# DISCIPLINE INTERFACES AND DRIVERS +# <---------------------------------------------------- + +solvers = SolverManager(comm) +solvers.flow = Fun3d14Interface( + comm, + f2f_model, + fun3d_dir="cfd", + forward_stop_tolerance=1e-15, + forward_min_tolerance=1e-12, + adjoint_stop_tolerance=4e-16, + adjoint_min_tolerance=1e-12, + debug=global_debug_flag, +) +# fun3d_project_name = "ssw-pw1.2" +solvers.structural = TacsSteadyInterface.create_from_bdf( + model=f2f_model, + comm=comm, + nprocs=nprocs_tacs, + bdf_file=tacs_aim.root_dat_file, + prefix=tacs_aim.root_analysis_dir, + debug=global_debug_flag, +) + +transfer_settings = TransferSettings(npts=200) + +# Build the FUNtoFEM driver +f2f_driver = FUNtoFEMnlbgs( + solvers=solvers, + transfer_settings=transfer_settings, + model=f2f_model, + debug=global_debug_flag, +) + +if test_derivatives: # test using the finite difference test + # load the previous design + # design_in_file = os.path.join(base_dir, "design", "sizing-oneway.txt") + # f2f_model.read_design_variables_file(comm, design_in_file) + + start_time = time.time() + + # run the finite difference test + max_rel_error = TestResult.derivative_test( + "fun3d+tacs-ssw1", + model=f2f_model, + driver=f2f_driver, + status_file="1-derivs.txt", + complex_mode=False, + epsilon=1e-4, + ) + + end_time = time.time() + dt = end_time - start_time + if comm.rank == 0: + print(f"total time for ssw derivative test is {dt} seconds", flush=True) + print(f"max rel error = {max_rel_error}", flush=True) + + # exit before optimization + exit() + + +# PYOPTSPARSE OPTMIZATION +# <---------------------------------------------------- + +# create an OptimizationManager object for the pyoptsparse optimization problem +design_in_file = os.path.join(base_dir, "design", "sizing-oneway.txt") +design_out_file = os.path.join(base_dir, "design", "design-1.txt") + +design_folder = os.path.join(base_dir, "design") +if comm.rank == 0: + if not os.path.exists(design_folder): + os.mkdir(design_folder) +history_file = os.path.join(design_folder, "design-1.hst") +store_history_file = history_file if store_history else None +hot_start_file = history_file if hot_start else None + +# Reload the previous design +f2f_model.read_design_variables_file(comm, design_in_file) + +if comm.rank == 0: + # f2f_driver.print_summary() + f2f_model.print_summary() + +manager = OptimizationManager( + f2f_driver, + design_out_file=design_out_file, + hot_start=hot_start, + hot_start_file=hot_start_file, + debug=True, +) + +# create the pyoptsparse optimization problem +opt_problem = Optimization("sswOpt", manager.eval_functions) + +# add funtofem model variables to pyoptsparse +manager.register_to_problem(opt_problem) + +# run an SNOPT optimization +snoptimizer = SNOPT( + options={ + "Verify level": 0, + "Function precision": 1e-4, + "Major Optimality tol": 1e-4, + } +) + +sol = snoptimizer( + opt_problem, + sens=manager.eval_gradients, + storeHistory=store_history_file, + hotStart=hot_start_file, +) + +# print final solution +sol_xdict = sol.xStar + +if comm.rank == 0: + print(f"Final solution = {sol_xdict}", flush=True) + +# ----------------------------------------------------> diff --git a/examples/fun3d_examples/_ssw-testing/_run_flow.py b/examples/fun3d_examples/_ssw-inviscid/_run_flow.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/_run_flow.py rename to examples/fun3d_examples/_ssw-inviscid/_run_flow.py diff --git a/examples/fun3d_examples/_ssw-inviscid/_test_struct_shape.py b/examples/fun3d_examples/_ssw-inviscid/_test_struct_shape.py new file mode 100644 index 00000000..66065e60 --- /dev/null +++ b/examples/fun3d_examples/_ssw-inviscid/_test_struct_shape.py @@ -0,0 +1,124 @@ +import unittest, importlib, numpy as np, os, sys +from funtofem import * +from mpi4py import MPI + +np.random.seed(1234567) +comm = MPI.COMM_WORLD + +tacs_loader = importlib.util.find_spec("tacs") +caps_loader = importlib.util.find_spec("pyCAPS") + +base_dir = os.path.dirname(os.path.abspath(__file__)) +csm_path = os.path.join(base_dir, "geometry", "ssw.csm") +results_folder, _ = make_test_directories(comm, base_dir) + +if tacs_loader is not None and caps_loader is not None: + from tacs import caps2tacs + +# check if we're in github to run only online vs offline tests +in_github_workflow = bool(os.getenv("GITHUB_ACTIONS")) +# in_github_workflow = True +optional = True # whether to run optional tests + + +@unittest.skipIf( + tacs_loader is None or caps_loader is None, + "skipping test using caps2tacs if caps or tacs are unavailable", +) +class TestTacsSteadyShapeDriver(unittest.TestCase): + N_PROCS = 2 + FILENAME = "tacs_steady_shape_driver.txt" + FILEPATH = os.path.join(results_folder, FILENAME) + + @unittest.skipIf(in_github_workflow, "only run this test offline") + def test_shape_steady_aeroelastic(self): + # make the funtofem and tacs model + f2f_model = FUNtoFEMmodel("wing") + tacs_model = caps2tacs.TacsModel.build(csm_file=csm_path, comm=comm) + tacs_model.mesh_aim.set_mesh( # need a refined-enough mesh for the derivative test to pass + edge_pt_min=5, + edge_pt_max=10, + global_mesh_size=0.1, + max_surf_offset=0.01, + max_dihedral_angle=5, + ).register_to( + tacs_model + ) + f2f_model.structural = tacs_model + + # build a body which we will register variables to + wing = Body.aeroelastic("wing") + + # 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")) + nOML = nribs - 1 + + for irib in range(1, nribs + 1): + caps2tacs.ShellProperty( + caps_group=f"rib{irib}", material=aluminum, membrane_thickness=0.05 + ).register_to(tacs_model) + for ispar in range(1, nspars + 1): + caps2tacs.ShellProperty( + caps_group=f"spar{ispar}", material=aluminum, membrane_thickness=0.05 + ).register_to(tacs_model) + for iOML in range(1, nOML + 1): + caps2tacs.ShellProperty( + caps_group=f"OML{iOML}", material=aluminum, membrane_thickness=0.03 + ).register_to(tacs_model) + for prefix in ["LE", "TE"]: + caps2tacs.ShellProperty( + caps_group=f"{prefix}spar", material=aluminum, membrane_thickness=0.03 + ).register_to(tacs_model) + + # register any shape variables to the wing which are auto-registered to tacs model + for itc in range(2, 4): + Variable.shape(name=f"tc{itc}").set_bounds( + lower=0.4, value=0.2, upper=1.6 + ).register_to(wing) + + # register the wing body to the model + wing.register_to(f2f_model) + + # add remaining information to tacs model + caps2tacs.PinConstraint("root").register_to(tacs_model) + + # make a funtofem scenario + test_scenario = Scenario.steady("test", steps=10) + Function.ksfailure().register_to(test_scenario) + Function.mass().register_to(test_scenario) + test_scenario.register_to(f2f_model) + + solvers = SolverManager(comm) + solvers.flow = TestAerodynamicSolver(comm, f2f_model) + aero_driver = TestAeroOnewayDriver(solvers, f2f_model) + transfer_settings = TransferSettings(npts=200, beta=0.5) + + # setup the tacs model + tacs_model.setup() + + tacs_driver = OnewayStructDriver.prime_loads( + aero_driver, transfer_settings=transfer_settings, nprocs=2 + ) + + max_rel_error = TestResult.derivative_test( + "testaero=>tacs_shape_steady-aeroelastic", + f2f_model, + tacs_driver, + TestTacsSteadyShapeDriver.FILEPATH, + complex_mode=False, + epsilon=1e-4, + ) + rtol = 1e-4 + self.assertTrue(max_rel_error < rtol) + return + + +if __name__ == "__main__": + if tacs_loader is not None and caps_loader is not None: + if comm.rank == 0: + open(TestTacsSteadyShapeDriver.FILEPATH, "w").close() + + unittest.main() diff --git a/examples/fun3d_examples/_ssw-testing/cfd/cruise_inviscid/Flow/fun3d.nml b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/fun3d.nml similarity index 97% rename from examples/fun3d_examples/_ssw-testing/cfd/cruise_inviscid/Flow/fun3d.nml rename to examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/fun3d.nml index 0e60a926..137857ed 100644 --- a/examples/fun3d_examples/_ssw-testing/cfd/cruise_inviscid/Flow/fun3d.nml +++ b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/fun3d.nml @@ -10,9 +10,6 @@ eqn_type = 'compressible' viscous_terms = 'inviscid' / -&turbulent_diffusion_models - turbulence_model = 'sa-neg' -/ &reference_physical_properties mach_number = 0.5 angle_of_attack = 2.0 diff --git a/examples/fun3d_examples/_ssw-testing/cfd/cruise_inviscid/Flow/moving_body.input b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/moving_body.input similarity index 100% rename from examples/fun3d_examples/_ssw-testing/cfd/cruise_inviscid/Flow/moving_body.input rename to examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/moving_body.input diff --git a/examples/fun3d_examples/_ssw-testing/cfd/cruise_inviscid/Flow/ssw-inviscid.lb8.ugrid b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/ssw-inviscid.lb8.ugrid similarity index 100% rename from examples/fun3d_examples/_ssw-testing/cfd/cruise_inviscid/Flow/ssw-inviscid.lb8.ugrid rename to examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/ssw-inviscid.lb8.ugrid diff --git a/examples/fun3d_examples/_ssw-testing/cfd/cruise_inviscid/Flow/ssw-inviscid.mapbc b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/ssw-inviscid.mapbc similarity index 100% rename from examples/fun3d_examples/_ssw-testing/cfd/cruise_inviscid/Flow/ssw-inviscid.mapbc rename to examples/fun3d_examples/_ssw-inviscid/cfd/cruise_inviscid/Flow/ssw-inviscid.mapbc diff --git a/examples/fun3d_examples/_ssw-testing/cfd/cruise/Adjoint/.gitignore b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Adjoint/.gitignore similarity index 100% rename from examples/fun3d_examples/_ssw-testing/cfd/cruise/Adjoint/.gitignore rename to examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Adjoint/.gitignore diff --git a/examples/fun3d_examples/_ssw-testing/cfd/cruise/Flow/fun3d.nml b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/fun3d.nml similarity index 89% rename from examples/fun3d_examples/_ssw-testing/cfd/cruise/Flow/fun3d.nml rename to examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/fun3d.nml index a6618e3d..1cd5101f 100644 --- a/examples/fun3d_examples/_ssw-testing/cfd/cruise/Flow/fun3d.nml +++ b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/fun3d.nml @@ -8,11 +8,11 @@ / &governing_equations eqn_type = 'compressible' - viscous_terms = 'inviscid' -/ -&turbulent_diffusion_models - turbulence_model = 'sa-neg' + viscous_terms = 'turbulent' / +!&turbulent_diffusion_models +! turbulence_model = 'sa' +!/ &reference_physical_properties mach_number = 0.5 angle_of_attack = 2.0 @@ -61,6 +61,9 @@ / &elasticity_gmres elasticity = 2 + preconditioner_iters = 10 + nsearch = 100 ! num GMRES steps + nrestarts = 15 ! leads to 50*15 = 750 grid iterations tol = 1e-22 tol_abs = 1e-30 / diff --git a/examples/fun3d_examples/_ssw-testing/cfd/cruise/Flow/moving_body.input b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/moving_body.input similarity index 86% rename from examples/fun3d_examples/_ssw-testing/cfd/cruise/Flow/moving_body.input rename to examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/moving_body.input index aba9559e..e85aff7b 100644 --- a/examples/fun3d_examples/_ssw-testing/cfd/cruise/Flow/moving_body.input +++ b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/moving_body.input @@ -3,7 +3,7 @@ body_name(1) = 'ssw_wing', parent_name(1) = '', ! '' means motion relative to inertial ref frame n_defining_bndry(1) = 1, ! number of boundaries that define this body - defining_bndry(1,1) = 3, ! index 1: boundary number index 2: body number + defining_bndry(1,1) = 2, ! index 1: boundary number index 2: body number motion_driver(1) = 'funtofem', ! tells fun3d to use motion inputs from python mesh_movement(1) = 'deform', ! can use 'rigid', 'deform', 'rigid+deform' with funtofem interface / diff --git a/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/ssw-turb.mapbc b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/ssw-turb.mapbc new file mode 100644 index 00000000..6f4e85ec --- /dev/null +++ b/examples/fun3d_examples/_ssw-inviscid/cfd/cruise_turb/Flow/ssw-turb.mapbc @@ -0,0 +1,4 @@ +3 +1 6662 SymmetryY +2 4000 wing +3 5000 Farfield diff --git a/examples/fun3d_examples/_ssw-testing/cfd/loads/uncoupled_loads.txt b/examples/fun3d_examples/_ssw-inviscid/cfd/loads/uncoupled_loads.txt similarity index 100% rename from examples/fun3d_examples/_ssw-testing/cfd/loads/uncoupled_loads.txt rename to examples/fun3d_examples/_ssw-inviscid/cfd/loads/uncoupled_loads.txt diff --git a/examples/fun3d_examples/_ssw-testing/design/_plot_sizing.py b/examples/fun3d_examples/_ssw-inviscid/design/_plot_sizing.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/design/_plot_sizing.py rename to examples/fun3d_examples/_ssw-inviscid/design/_plot_sizing.py diff --git a/examples/fun3d_examples/_ssw-testing/design/design-1.txt b/examples/fun3d_examples/_ssw-inviscid/design/design-1.txt similarity index 100% rename from examples/fun3d_examples/_ssw-testing/design/design-1.txt rename to examples/fun3d_examples/_ssw-inviscid/design/design-1.txt diff --git a/examples/fun3d_examples/_ssw-testing/design/design-2.txt b/examples/fun3d_examples/_ssw-inviscid/design/design-2.txt similarity index 100% rename from examples/fun3d_examples/_ssw-testing/design/design-2.txt rename to examples/fun3d_examples/_ssw-inviscid/design/design-2.txt diff --git a/examples/fun3d_examples/_ssw-testing/design/sizing-oneway.txt b/examples/fun3d_examples/_ssw-inviscid/design/sizing-oneway.txt similarity index 100% rename from examples/fun3d_examples/_ssw-testing/design/sizing-oneway.txt rename to examples/fun3d_examples/_ssw-inviscid/design/sizing-oneway.txt diff --git a/examples/fun3d_examples/_ssw-testing/design/sizing.txt b/examples/fun3d_examples/_ssw-inviscid/design/sizing.txt similarity index 100% rename from examples/fun3d_examples/_ssw-testing/design/sizing.txt rename to examples/fun3d_examples/_ssw-inviscid/design/sizing.txt diff --git a/examples/fun3d_examples/_ssw-testing/design/ssw-aoa_design.txt b/examples/fun3d_examples/_ssw-inviscid/design/ssw-aoa_design.txt similarity index 100% rename from examples/fun3d_examples/_ssw-testing/design/ssw-aoa_design.txt rename to examples/fun3d_examples/_ssw-inviscid/design/ssw-aoa_design.txt diff --git a/examples/fun3d_examples/_ssw-testing/design/ssw-sizing-1way_design.txt b/examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing-1way_design.txt similarity index 100% rename from examples/fun3d_examples/_ssw-testing/design/ssw-sizing-1way_design.txt rename to examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing-1way_design.txt diff --git a/examples/fun3d_examples/_ssw-testing/design/ssw-sizing1-turb_design.txt b/examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing1-turb_design.txt similarity index 100% rename from examples/fun3d_examples/_ssw-testing/design/ssw-sizing1-turb_design.txt rename to examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing1-turb_design.txt diff --git a/examples/fun3d_examples/_ssw-testing/design/ssw-sizing1_design.txt b/examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing1_design.txt similarity index 100% rename from examples/fun3d_examples/_ssw-testing/design/ssw-sizing1_design.txt rename to examples/fun3d_examples/_ssw-inviscid/design/ssw-sizing1_design.txt diff --git a/examples/fun3d_examples/_ssw-testing/design/ssw-twist_design.txt b/examples/fun3d_examples/_ssw-inviscid/design/ssw-twist_design.txt similarity index 100% rename from examples/fun3d_examples/_ssw-testing/design/ssw-twist_design.txt rename to examples/fun3d_examples/_ssw-inviscid/design/ssw-twist_design.txt diff --git a/examples/fun3d_examples/_ssw-testing/geometry/_mesh_fun3d.py b/examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_fun3d.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/geometry/_mesh_fun3d.py rename to examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_fun3d.py diff --git a/examples/fun3d_examples/_ssw-testing/geometry/_mesh_fun3d_egads.py b/examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_fun3d_egads.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/geometry/_mesh_fun3d_egads.py rename to examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_fun3d_egads.py diff --git a/examples/fun3d_examples/_ssw-testing/geometry/_mesh_tacs.py b/examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_tacs.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/geometry/_mesh_tacs.py rename to examples/fun3d_examples/_ssw-inviscid/geometry/_mesh_tacs.py diff --git a/examples/fun3d_examples/_ssw-testing/geometry/_test_wingAero.csm b/examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingAero.csm similarity index 100% rename from examples/fun3d_examples/_ssw-testing/geometry/_test_wingAero.csm rename to examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingAero.csm diff --git a/examples/fun3d_examples/_ssw-testing/geometry/_test_wingSolid.csm b/examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingSolid.csm similarity index 100% rename from examples/fun3d_examples/_ssw-testing/geometry/_test_wingSolid.csm rename to examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingSolid.csm diff --git a/examples/fun3d_examples/_ssw-testing/geometry/_test_wingStruct.csm b/examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingStruct.csm similarity index 100% rename from examples/fun3d_examples/_ssw-testing/geometry/_test_wingStruct.csm rename to examples/fun3d_examples/_ssw-inviscid/geometry/_test_wingStruct.csm diff --git a/examples/fun3d_examples/_ssw-testing/geometry/ssw.csm b/examples/fun3d_examples/_ssw-inviscid/geometry/ssw.csm similarity index 100% rename from examples/fun3d_examples/_ssw-testing/geometry/ssw.csm rename to examples/fun3d_examples/_ssw-inviscid/geometry/ssw.csm diff --git a/examples/fun3d_examples/_ssw-testing/geometry/wingAero.udc b/examples/fun3d_examples/_ssw-inviscid/geometry/wingAero.udc similarity index 100% rename from examples/fun3d_examples/_ssw-testing/geometry/wingAero.udc rename to examples/fun3d_examples/_ssw-inviscid/geometry/wingAero.udc diff --git a/examples/fun3d_examples/_ssw-testing/geometry/wingSolid.udc b/examples/fun3d_examples/_ssw-inviscid/geometry/wingSolid.udc similarity index 100% rename from examples/fun3d_examples/_ssw-testing/geometry/wingSolid.udc rename to examples/fun3d_examples/_ssw-inviscid/geometry/wingSolid.udc diff --git a/examples/fun3d_examples/_ssw-testing/geometry/wingStruct.udc b/examples/fun3d_examples/_ssw-inviscid/geometry/wingStruct.udc similarity index 100% rename from examples/fun3d_examples/_ssw-testing/geometry/wingStruct.udc rename to examples/fun3d_examples/_ssw-inviscid/geometry/wingStruct.udc diff --git a/examples/fun3d_examples/_ssw-testing/struct/README.md b/examples/fun3d_examples/_ssw-inviscid/struct/README.md similarity index 100% rename from examples/fun3d_examples/_ssw-testing/struct/README.md rename to examples/fun3d_examples/_ssw-inviscid/struct/README.md diff --git a/examples/fun3d_examples/_ssw-testing/struct/build_exploded_mesh.py b/examples/fun3d_examples/_ssw-inviscid/struct/build_exploded_mesh.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/struct/build_exploded_mesh.py rename to examples/fun3d_examples/_ssw-inviscid/struct/build_exploded_mesh.py diff --git a/examples/fun3d_examples/_ssw-testing/test_fun3d_tacs.py b/examples/fun3d_examples/_ssw-inviscid/test_fun3d_tacs.py similarity index 100% rename from examples/fun3d_examples/_ssw-testing/test_fun3d_tacs.py rename to examples/fun3d_examples/_ssw-inviscid/test_fun3d_tacs.py diff --git a/examples/fun3d_examples/_ssw-testing/cfd/cruise/Flow/ssw-turb.mapbc b/examples/fun3d_examples/_ssw-testing/cfd/cruise/Flow/ssw-turb.mapbc deleted file mode 100644 index c88a30ce..00000000 --- a/examples/fun3d_examples/_ssw-testing/cfd/cruise/Flow/ssw-turb.mapbc +++ /dev/null @@ -1,4 +0,0 @@ -3 -1 6662 symmetry -2 3000 wing -3 5000 farfield diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/1_panel_thickness.py b/examples/fun3d_examples/ssw_meshdef_optimization/1_panel_thickness.py index db033ea8..32421ac1 100644 --- a/examples/fun3d_examples/ssw_meshdef_optimization/1_panel_thickness.py +++ b/examples/fun3d_examples/ssw_meshdef_optimization/1_panel_thickness.py @@ -3,13 +3,14 @@ Run a coupled optimization of the panel thicknesses of the wing structure. No shape variables are included in this optimization. +This example is finished and converged well in SNOPT """ from pyoptsparse import SNOPT, Optimization from funtofem import * from mpi4py import MPI from tacs import caps2tacs -import os +import os, time comm = MPI.COMM_WORLD @@ -20,6 +21,8 @@ hot_start = False store_history = True +test_derivatives = False + nprocs_tacs = 8 global_debug_flag = False @@ -37,7 +40,7 @@ q_inf = 1.21945e4 # Dynamic pressure # Construct the FUNtoFEM model -f2f_model = FUNtoFEMmodel("ssw-sizing1") +f2f_model = FUNtoFEMmodel("ssw-sizing1-turb") tacs_model = caps2tacs.TacsModel.build( csm_file=csm_path, comm=comm, @@ -142,15 +145,20 @@ # <---------------------------------------------------- # make a funtofem scenario -cruise = Scenario.steady("cruise", steps=1000, coupling_frequency=50, uncoupled_steps=0) +cruise = Scenario.steady( + "cruise", steps=1500, coupling_frequency=30, uncoupled_steps=200 +) cruise.adjoint_steps = ( - 100 # outer coupling iterations, total 5000 flow adjoints, 100 grid adjoints + 150 # outer coupling iterations, total 5000 flow adjoints, 100 grid adjoints +) +cruise.set_stop_criterion( + early_stopping=True, min_forward_steps=300, min_adjoint_steps=20 ) -cruise.set_stop_criterion(early_stopping=True, min_adjoint_steps=50) + mass = Function.mass().optimize( scale=1.0e-4, objective=True, plot=True, plot_name="mass" ) -ksfailure = Function.ksfailure(ks_weight=50.0, safety_factor=1.5).optimize( +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-cruise" ) cruise.include(ksfailure).include(mass) @@ -164,21 +172,23 @@ # <---------------------------------------------------- # 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 = left_var - right_var - adj_diff = 0.002 - adj_constr.set_name(f"{prefix}{iconstr}-{iconstr+1}").optimize( - lower=-adj_diff, upper=adj_diff, scale=1.0, objective=False - ).register_to(f2f_model) +if not test_derivatives: + 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 = left_var - right_var + adj_diff = 0.002 + adj_constr.set_name(f"{prefix}{iconstr}-{iconstr+1}").optimize( + lower=-adj_diff, upper=adj_diff, scale=1.0, objective=False + ).register_to(f2f_model) + # ----------------------------------------------------> @@ -190,12 +200,13 @@ comm, f2f_model, fun3d_dir="cfd", - forward_stop_tolerance=1e-12, - forward_min_tolerance=1e-8, - adjoint_stop_tolerance=1e-12, - adjoint_min_tolerance=1e-8, + forward_stop_tolerance=1e-15, + forward_min_tolerance=1e-12, + adjoint_stop_tolerance=1e-13, + adjoint_min_tolerance=1e-10, debug=global_debug_flag, ) +# fun3d_project_name = "ssw-pw1.2" solvers.structural = TacsSteadyInterface.create_from_bdf( model=f2f_model, comm=comm, @@ -216,6 +227,32 @@ reload_funtofem_states=True, ) +if test_derivatives: # test using the finite difference test + # load the previous design + # design_in_file = os.path.join(base_dir, "design", "sizing-oneway.txt") + # f2f_model.read_design_variables_file(comm, design_in_file) + + start_time = time.time() + + # run the finite difference test + max_rel_error = TestResult.derivative_test( + "fun3d+tacs-ssw1", + model=f2f_model, + driver=f2f_driver, + status_file="1-derivs.txt", + complex_mode=False, + epsilon=1e-4, + ) + + end_time = time.time() + dt = end_time - start_time + if comm.rank == 0: + print(f"total time for ssw derivative test is {dt} seconds", flush=True) + print(f"max rel error = {max_rel_error}", flush=True) + + # exit before optimization + exit() + # PYOPTSPARSE OPTMIZATION # <---------------------------------------------------- @@ -233,18 +270,18 @@ hot_start_file = history_file if hot_start else None # Reload the previous design -# f2f_model.read_design_variables_file(comm, design_in_file) +f2f_model.read_design_variables_file(comm, design_in_file) if comm.rank == 0: - f2f_driver.print_summary() + # f2f_driver.print_summary() f2f_model.print_summary() manager = OptimizationManager( f2f_driver, design_out_file=design_out_file, hot_start=hot_start, - debug=global_debug_flag, hot_start_file=hot_start_file, + debug=True, ) # create the pyoptsparse optimization problem @@ -254,8 +291,13 @@ manager.register_to_problem(opt_problem) # run an SNOPT optimization -snoptimizer = SNOPT(options={"Verify level": 3}) -# snoptimizer = SNOPT(options={"Verify level": 0, "Function precision": 1e-8}) +snoptimizer = SNOPT( + options={ + "Verify level": 0, + "Function precision": 1e-4, + "Major Optimality tol": 1e-4, + } +) sol = snoptimizer( opt_problem, diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/fun3d.nml b/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/fun3d.nml index 4bf6a71b..ed725db0 100644 --- a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/fun3d.nml +++ b/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/fun3d.nml @@ -10,9 +10,9 @@ eqn_type = 'compressible' viscous_terms = 'turbulent' / -&turbulent_diffusion_models - turbulence_model = 'sa-neg' -/ +!&turbulent_diffusion_models +! turbulence_model = 'sa' +!/ &reference_physical_properties mach_number = 0.5 angle_of_attack = 2.0 @@ -24,6 +24,7 @@ restart_write_freq = 100 restart_read = 'on' steps = 5000 + stopping_tolerance = 1e-18 / &force_moment_integ_properties area_reference = 5.0 @@ -60,8 +61,11 @@ / &elasticity_gmres elasticity = 2 + preconditioner_iters = 10 + nsearch = 200 ! num GMRES steps + nrestarts = 4 ! leads to 50*15 = 750 grid iterations tol = 1e-20 - tol_abs = 1e-30 + tol_abs = 1e-25 / &slice_data nslices = 1 diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/ssw-turb.mapbc b/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/ssw-turb.mapbc index b5c7df3c..6f4e85ec 100644 --- a/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/ssw-turb.mapbc +++ b/examples/fun3d_examples/ssw_meshdef_optimization/cfd/cruise/Flow/ssw-turb.mapbc @@ -1,4 +1,4 @@ 3 -1 5000 farfield +1 6662 SymmetryY 2 4000 wing -3 6662 symmetry +3 5000 Farfield diff --git a/examples/fun3d_examples/ssw_meshdef_optimization/design/sizing-oneway.txt b/examples/fun3d_examples/ssw_meshdef_optimization/design/sizing-oneway.txt new file mode 100644 index 00000000..9f92c915 --- /dev/null +++ b/examples/fun3d_examples/ssw_meshdef_optimization/design/sizing-oneway.txt @@ -0,0 +1,18 @@ +Discipline structural + var rib1 0.0011764564105997894 + var rib2 0.001 + var rib3 0.001 + var rib4 0.001 + var rib5 0.001 + var rib6 0.001 + var rib7 0.001 + var spar1 0.0012820370166530702 + var spar2 0.001 + var OML1 0.0037882127680944743 + var OML2 0.0032199810198174127 + var OML3 0.0027369839562798397 + var OML4 0.0023264364107546784 + var OML5 0.001977470974636796 + var OML6 0.0016808503416537541 + var LEspar 0.001 + var TEspar 0.001 \ No newline at end of file diff --git a/funtofem/driver/funtofem_shape_driver.py b/funtofem/driver/funtofem_shape_driver.py index 894badac..5b672647 100644 --- a/funtofem/driver/funtofem_shape_driver.py +++ b/funtofem/driver/funtofem_shape_driver.py @@ -414,7 +414,8 @@ def solve_forward(self): # run the pre analysis to generate a new mesh try: - self.flow_aim.pre_analysis() + self.model.flow.pre_analysis() + # self.flow_aim.pre_analysis() local_fail = False except: local_fail = True @@ -642,7 +643,8 @@ def solve_adjoint(self): start_time = time.time() # run the pre analysis to generate a new mesh - self.flow_aim.pre_analysis() + self.model.flow.pre_analysis() + # self.flow_aim.pre_analysis() flow_adjoint_pre_time = (time.time() - start_time) / 60.0 self._write_timing_data( diff --git a/funtofem/driver/oneway_aero_driver.py b/funtofem/driver/oneway_aero_driver.py index 9cec8456..4b59e283 100644 --- a/funtofem/driver/oneway_aero_driver.py +++ b/funtofem/driver/oneway_aero_driver.py @@ -267,7 +267,7 @@ def solve_forward(self): self.flow_aim.set_design_sensitivity(False, include_file=False) # run the pre analysis to generate a new mesh - self.flow_aim.pre_analysis() + self.model.flow.pre_analysis() if not (self.is_paired): if ( diff --git a/funtofem/interface/caps2fun/aflr_aim.py b/funtofem/interface/caps2fun/aflr_aim.py index cf9ae4cf..16da159f 100644 --- a/funtofem/interface/caps2fun/aflr_aim.py +++ b/funtofem/interface/caps2fun/aflr_aim.py @@ -139,10 +139,11 @@ def _set_dict_options(self): return self - def mesh_sizing(self, fun3d_bc): + def mesh_sizing(self, fun3d_bcs: list): if self.root_proc: - self.aim.input.Mesh_Sizing = {fun3d_bc.name: fun3d_bc.BC_dict} - + self.aim.input.Mesh_Sizing = { + fun3d_bc.name: fun3d_bc.BC_dict for fun3d_bc in fun3d_bcs + } return def set_surface_mesh( diff --git a/funtofem/interface/caps2fun/fun3d_model.py b/funtofem/interface/caps2fun/fun3d_model.py index 6a26b4f4..7b00bb9e 100644 --- a/funtofem/interface/caps2fun/fun3d_model.py +++ b/funtofem/interface/caps2fun/fun3d_model.py @@ -7,6 +7,8 @@ import pyCAPS, os from .fun3d_aim import Fun3dAim from .mesh_aim import MeshAim +from .pointwise_aim import PointwiseAIM +from .aflr_aim import Aflr3Aim class Fun3dModel: @@ -132,7 +134,8 @@ def _set_project_names(self): self.fun3d_aim.aim.input.Proj_Name = self.project_name self.fun3d_aim._metadata["project_name"] = self.project_name if self.mesh_aim.root_proc: - self.mesh_aim.surface_aim.aim.input.Proj_Name = self.project_name + if self.mesh_aim.surface_aim is not None: + self.mesh_aim.surface_aim.aim.input.Proj_Name = self.project_name self.mesh_aim.volume_aim.aim.input.Proj_Name = self.project_name return @@ -162,9 +165,13 @@ def setup(self): return def _set_grid_filename(self): - self.fun3d_aim.grid_file = os.path.join( - self.mesh_aim.analysis_dir, "aflr3_0.lb8.ugrid" - ) + if isinstance(self.mesh_aim.volume_aim, Aflr3Aim): + filename = "aflr3_0.lb8.ugrid" + elif isinstance(self.mesh_aim.volume_aim, PointwiseAIM): + filename = "caps.GeomTomesh.lb8.ugrid" + + self.fun3d_aim.grid_file = os.path.join(self.mesh_aim.analysis_dir, filename) + # also set mapbc file self.fun3d_aim.mapbc_file = os.path.join( self.fun3d_aim.analysis_dir, "Flow", self.fun3d_aim.project_name + ".mapbc" @@ -180,6 +187,14 @@ def _link_aims(self): ) return + def pre_analysis(self): + volume_aim = self.mesh_aim.volume_aim + if isinstance(volume_aim, PointwiseAIM): + volume_aim.run_pointwise() + + self.fun3d_aim.pre_analysis() + return + @property def geometry(self): return self.fun3d_aim.geometry diff --git a/funtofem/interface/caps2fun/mesh_aim.py b/funtofem/interface/caps2fun/mesh_aim.py index eaeec31e..63d1e1f2 100644 --- a/funtofem/interface/caps2fun/mesh_aim.py +++ b/funtofem/interface/caps2fun/mesh_aim.py @@ -5,6 +5,7 @@ __all__ = ["MeshAim"] from .aflr_aim import Aflr3Aim, Aflr4Aim +from .pointwise_aim import PointwiseAIM from .egads_aim import EgadsAim @@ -26,6 +27,10 @@ def __init__( # Set volume and surface mesh AIMs if volume_mesh == "aflr3": self._vol_aim = Aflr3Aim(caps_problem=caps_problem, comm=comm, root=root) + elif volume_mesh == "pointwise": + self._vol_aim = PointwiseAIM( + caps_problem=caps_problem, comm=comm, root=root + ) else: raise RuntimeError("Unrecognized volume mesher.") @@ -33,6 +38,8 @@ def __init__( self._surf_aim = Aflr4Aim(caps_problem=caps_problem, comm=comm, root=root) elif surface_mesh == "egads": self._surf_aim = EgadsAim(caps_problem=caps_problem, comm=comm, root=root) + elif surface_mesh is None: + self._surf_aim = None else: raise RuntimeError("Unrecognized surface mesher.") @@ -69,7 +76,7 @@ def analysis_dir(self): def link_surface_mesh(self): """link the surface mesh to volume mesh""" - if self.root_proc: + if self.root_proc and self.surface_aim is not None: self.volume_aim.aim.input["Surface_Mesh"].link( self.surface_aim.aim.output["Surface_Mesh"] ) @@ -79,14 +86,16 @@ def _build_aim(self): # self._vol_aim = self.volume_aim._build_sub_aim() # self._surf_aim = self.surface_aim._build_sub_aim() self.volume_aim._build_sub_aim() - self.surface_aim._build_sub_aim() + if self.surface_aim is not None: + self.surface_aim._build_sub_aim() return def saveDictOptions(self, dictOptions): self._dictOptions = dictOptions self.volume_aim.save_dict_options(dictOptions) - self.surface_aim.save_dict_options(dictOptions) + if self.surface_aim is not None: + self.surface_aim.save_dict_options(dictOptions) return self @@ -98,6 +107,7 @@ def _setDictOptions(self): dictOptions = self._dictOptions self.volume_aim._set_dict_options() - self.surface_aim._set_dict_options() + if self.surface_aim is not None: + self.surface_aim._set_dict_options() return self diff --git a/funtofem/interface/caps2fun/pointwise_aim.py b/funtofem/interface/caps2fun/pointwise_aim.py new file mode 100644 index 00000000..0422649c --- /dev/null +++ b/funtofem/interface/caps2fun/pointwise_aim.py @@ -0,0 +1,156 @@ +""" +Written by Brian Burke and Sean Engelstad, Georgia Tech SMDO Lab, 2024. +""" + +__all__ = ["PointwiseAIM"] + +import os +from .fun3d_aim import Fun3dBC +from typing import List + + +class PointwiseAIM: + def __init__(self, caps_problem, comm, root=0): + """MPI wrapper class for AflrAIM from ESP/CAPS""" + + self.caps_problem = caps_problem + self.comm = comm + self.root = root + + # holds aflr3 AIM + self._aim = None + + self._dictOptions = None + + self.root_dir = os.getcwd() + + return + + @property + def root_proc(self) -> bool: + return self.comm.rank == self.root + + @property + def aim(self): + return self._aim + + def _build_sub_aim(self): + if self.root_proc: + self._aim = self.caps_problem.analysis.create( + aim="pointwiseAIM", name="pointwise" + ) + + return self._aim + + def run_pointwise(self): + if self.comm.rank == 0: + # run AIM pre-analysis + self.aim.preAnalysis() + + # move to test directory + os.chdir(self.aim.analysisDir) + + CAPS_GLYPH = os.environ["CAPS_GLYPH"] + # ranPointwise = False + for i in range(1): # can run extra times if having license issues + os.system( + "pointwise -b " + + CAPS_GLYPH + + "/GeomToMesh.glf caps.egads capsUserDefaults.glf" + ) + # ranPointwise = os.path.isfile('caps.GeomToMesh.gma') and os.path.isfile('caps.GeomToMesh.ugrid') + # if ranPointwise: break + + os.chdir(self.root_dir) + # if (not(ranPointwise)): sys.exit("No pointwise license available") + + # run AIM postanalysis, files in self.pointwiseAim.analysisDir + self.aim.postAnalysis() + + def main_settings( + self, + project_name="Wing", + mesh_format="AFLR3", + connector_turn_angle=6.0, + connector_prox_growth_rate=1.3, + connector_source_spacing=False, + domain_algorithm="AdvancingFront", + domain_max_layers=0, + domain_growth_rate=1.3, + domain_iso_type="TriangleQuad", + domain_trex_type="TriangleQuad", + domain_decay=0.5, + domain_trex_AR_limit=200, + domain_wall_spacing=0, + block_algorithm="AdvancingFront", + block_boundary_decay=0.5, + block_collision_buffer=1.0, + block_max_skew_angle=175, + block_edge_max_growth_rate=1.8, + block_full_layers=0, + block_max_layers=0, + block_trex_type="TetPyramid", + ): + if self.comm.rank == 0: + self.aim.input.Proj_Name = project_name + self.aim.input.Mesh_Format = mesh_format + + # connector level + self.aim.input.Connector_Turn_Angle = connector_turn_angle + self.aim.input.Connector_Prox_Growth_Rate = connector_prox_growth_rate + self.aim.input.Connector_Source_Spacing = connector_source_spacing + + # domain level + self.aim.input.Domain_Algorithm = ( + domain_algorithm # "Delaunay", "AdvancingFront", "AdvancingFrontOrtho" + ) + self.aim.input.Domain_Max_Layers = domain_max_layers + self.aim.input.Domain_Growth_Rate = domain_growth_rate + self.aim.input.Domain_TRex_ARLimit = ( + domain_trex_AR_limit # def 40.0, lower inc mesh size + ) + self.aim.input.Domain_Decay = domain_decay + self.aim.input.Domain_Iso_Type = domain_iso_type # "TriangleQuad" + self.aim.input.Domain_TRex_Type = domain_trex_type # "TriangleQuad" + + self.aim.input.Domain_Wall_Spacing = ( + domain_wall_spacing # e.g. 1e-5 if turbulent + ) + # Defined spacing when geometry attributed with PW:WallSpacing $wall (relative to capsMeshLength) + + # block level + self.aim.input.Block_Algorithm = block_algorithm + self.aim.input.Block_Boundary_Decay = block_boundary_decay + self.aim.input.Block_Collision_Buffer = block_collision_buffer + self.aim.input.Block_Max_Skew_Angle = block_max_skew_angle + self.aim.input.Block_Edge_Max_Growth_Rate = block_edge_max_growth_rate + self.aim.input.Block_Full_Layers = block_full_layers + self.aim.input.Block_Max_Layers = block_max_layers + self.aim.input.Block_TRexType = block_trex_type + + return + + def bc_mesh_sizing(self, fun3d_bcs: list): + if self.root_proc: + self.aim.input.Mesh_Sizing = { + fun3d_bc.name: fun3d_bc.BC_dict for fun3d_bc in fun3d_bcs + } + return + + def save_dict_options(self, dictOptions): + self._dictOptions = dictOptions + + return self + + def _set_dict_options(self): + """ + Set AFLR3 and AFLR4 options via dictionaries. + """ + if self.root_proc and self._dictOptions is not None: + dictOptions = self._dictOptions + + if dictOptions["pointwiseAIM"] is not None: + for ind, option in enumerate(dictOptions["pointwiseAIM"]): + self.aim.input[option].value = dictOptions["pointwiseAIM"][option] + + return self diff --git a/funtofem/interface/solver_manager.py b/funtofem/interface/solver_manager.py index 1f7cb4d5..db8e3362 100644 --- a/funtofem/interface/solver_manager.py +++ b/funtofem/interface/solver_manager.py @@ -103,6 +103,14 @@ def solver_list(self): mlist.append(self.structural) return mlist + @property + def forward_residual(self) -> float: + return max([abs(solver.get_forward_residual()) for solver in self.solver_list]) + + @property + def adjoint_residual(self) -> float: + return max([abs(solver.get_adjoint_residual()) for solver in self.solver_list]) + @property def flow(self): return self._flow diff --git a/funtofem/optimization/optimization_manager.py b/funtofem/optimization/optimization_manager.py index d327326a..cee0d536 100644 --- a/funtofem/optimization/optimization_manager.py +++ b/funtofem/optimization/optimization_manager.py @@ -143,7 +143,7 @@ def _gatekeeper(self, x_dict): # write the new design dict if self.comm.rank == 0 and self.write_designs: regular_dict = {key: float(x_dict[key]) for key in x_dict} - self._design_hdl.write(f"New design = {regular_dict}\n") + self._design_hdl.write(f"Design {self._iteration} = {regular_dict}\n") self._design_hdl.flush() # change the design @@ -173,7 +173,14 @@ def _gatekeeper(self, x_dict): # write the new function values if self.comm.rank == 0 and self.write_designs: - self._design_hdl.write(f"Functions = {self._funcs}\n") + solvers = self.driver.solvers + self._design_hdl.write( + f"\tForward residual = {solvers.forward_residual}\n" + ) + self._design_hdl.write( + f"\tAdjoint residual = {solvers.adjoint_residual}\n" + ) + self._design_hdl.write(f"\tFunctions = {self._funcs}\n") self._design_hdl.flush() # only update design file if analysis didn't fail and give nans