diff --git a/examples/caps_wing/1_steady_analysis.py b/examples/caps_wing/1_steady_analysis.py index d5e704196..aab8d01a1 100644 --- a/examples/caps_wing/1_steady_analysis.py +++ b/examples/caps_wing/1_steady_analysis.py @@ -20,7 +20,9 @@ print(f"proc on rank {comm.rank}") -tacs_model = caps2tacs.TacsModel.build(csm_file="simple_naca_wing.csm", comm=comm, active_procs=[0]) +tacs_model = caps2tacs.TacsModel.build( + csm_file="simple_naca_wing.csm", comm=comm, active_procs=[0] +) tacs_model.mesh_aim.set_mesh( edge_pt_min=15, edge_pt_max=20, diff --git a/examples/caps_wing/4_sizing_and_shape.py b/examples/caps_wing/4_sizing_and_shape.py index f80ffe301..989a6452b 100644 --- a/examples/caps_wing/4_sizing_and_shape.py +++ b/examples/caps_wing/4_sizing_and_shape.py @@ -12,7 +12,9 @@ # Setup CAPS Problem # --------------------------------------------------------------# comm = MPI.COMM_WORLD -tacs_model = caps2tacs.TacsModel.build(csm_file="large_naca_wing.csm", comm=comm, active_procs=[0]) +tacs_model = caps2tacs.TacsModel.build( + csm_file="large_naca_wing.csm", comm=comm, active_procs=[0] +) tacs_model.mesh_aim.set_mesh( # need a refined-enough mesh for the derivative test to pass edge_pt_min=15, edge_pt_max=20, diff --git a/examples/caps_wing/7_multiproc_post_analysis.py b/examples/caps_wing/7_multiproc_post_analysis.py index 4d72f01af..c3b9ee7f8 100644 --- a/examples/caps_wing/7_multiproc_post_analysis.py +++ b/examples/caps_wing/7_multiproc_post_analysis.py @@ -36,7 +36,9 @@ # Setup CAPS Problem # --------------------------------------------------------------# comm = MPI.COMM_WORLD -tacs_model = caps2tacs.TacsModel.build(csm_file="large_naca_wing.csm", comm=comm, active_procs=[0,1]) +tacs_model = caps2tacs.TacsModel.build( + csm_file="large_naca_wing.csm", comm=comm, active_procs=[0, 1] +) tacs_model.mesh_aim.set_mesh( # need a refined-enough mesh for the derivative test to pass edge_pt_min=15, edge_pt_max=20, @@ -115,4 +117,4 @@ naims = len(tacs_model.active_procs) if comm.rank == 0: print(f"Elapsed time with {naims} procs is {mins_elapsed} mins using 4 shape vars.") - print(f"Also each post analysis took {post_analysis_time/60.0:.4f} min", flush=True) \ No newline at end of file + print(f"Also each post analysis took {post_analysis_time/60.0:.4f} min", flush=True) diff --git a/examples/caps_wing/8_make_exploded_views.py b/examples/caps_wing/8_make_exploded_views.py new file mode 100644 index 000000000..e8112092c --- /dev/null +++ b/examples/caps_wing/8_make_exploded_views.py @@ -0,0 +1,110 @@ +""" +Sean Engelstad, Febuary 2023 +GT SMDO Lab, Dr. Graeme Kennedy +Caps to TACS example + +December 1, 2023 +Run this with conda-mpirun -n 3 python 8_make_exploded_view.py + Then process the f5 files which are moved to the exploded folder. +""" + +from tacs import caps2tacs +from mpi4py import MPI +import os, shutil + +# run a steady elastic structural analysis in TACS using the tacsAIM wrapper caps2tacs submodule +# ------------------------------------------------------------------------------------------------- +# 1: build the tacs aim, egads aim wrapper classes + +comm = MPI.COMM_WORLD + +print(f"proc on rank {comm.rank}") + +tacs_model = caps2tacs.TacsModel.build( + csm_file="large_naca_wing.csm", + comm=comm, + problem_name="capsExploded", + active_procs=[_ for _ in range(3)], +) +tacs_model.mesh_aim.set_mesh( + edge_pt_min=15, + edge_pt_max=20, + global_mesh_size=0.25, + max_surf_offset=0.01, + max_dihedral_angle=15, +).register_to(tacs_model) + +# set the exploded views to 1,2,3 for each of the 3 separate procs +tacs_aim = tacs_model.tacs_aim +exploded_view = comm.rank + 1 +tacs_aim.set_config_parameter("exploded", exploded_view) + +aluminum = caps2tacs.Isotropic.aluminum().register_to(tacs_model) + +# setup the thickness design variables + automatic shell properties +# choose pattern based dv1 values +nribs = int(tacs_model.get_config_parameter("nribs")) +nspars = int(tacs_model.get_config_parameter("nspars")) +nOML = nribs - 1 + +if exploded_view == 2: # only make these internal struct variables on proc 1 tacs model + for irib in range(1, nribs + 1): + caps2tacs.ThicknessVariable( + caps_group=f"rib{irib}", value=0.1 - 0.01 * irib, material=aluminum + ).register_to(tacs_model) + for ispar in range(1, nspars + 1): + caps2tacs.ThicknessVariable( + caps_group=f"spar{ispar}", value=0.1 - 0.01 * irib, material=aluminum + ).register_to(tacs_model) + +if exploded_view in [ + 1, + 3, +]: # only make these OML struct variables on procs 0,2 tacs model + for iOML in range(1, nOML + 1): + caps2tacs.ThicknessVariable( + caps_group=f"OML{iOML}", value=0.1 - 0.01 * iOML, material=aluminum + ).register_to(tacs_model) + +# add constraints and loads +caps2tacs.PinConstraint("root").register_to(tacs_model) +caps2tacs.GridForce("OML", direction=[0, 0, 1.0], magnitude=100).register_to(tacs_model) + +# run the pre analysis to build tacs input files +# alternative is to call tacs_aim.setup_aim().pre_analysis() with tacs_aim = tacs_model.tacs_aim +tacs_model.setup() + +comm.Barrier() + +SPs = tacs_model.createTACSProbs(addFunctions=True) + +# make a directory for the exploded views +exploded_dir = os.path.join(os.getcwd(), "exploded") +if not os.path.exists(exploded_dir) and comm.rank == 0: + os.mkdir(exploded_dir) + +# create a mesh of each of the exploded views +names = ["upperOML", "int-struct", "lowerOML"] +for rank in range(3): + # create tacs problems in the tacs directory of this proc + SPs = tacs_model.createTACSProbs(root=rank) + # then write solution using all procs + for caseID in SPs: + SPs[caseID].writeSolution( + baseName=names[rank], outputDir=tacs_model.analysis_dir(proc=rank), number=0 + ) + + # move the file to the exploded view directory + filename = names[rank] + "_000.f5" + src = os.path.join(tacs_model.analysis_dir(proc=rank), filename) + dest = os.path.join(exploded_dir, filename) + + if comm.rank == 0: + shutil.copy(src, dest) + + comm.Barrier() + +print("Done creating exploded views for the large_naca_wing.csm..") +print( + "\tGoto the exploded/ folder and process the f5 files to create an exploded view visualization!" +) diff --git a/examples/caps_wing/large_naca_wing.csm b/examples/caps_wing/large_naca_wing.csm index d8214083d..68165acf2 100644 --- a/examples/caps_wing/large_naca_wing.csm +++ b/examples/caps_wing/large_naca_wing.csm @@ -8,6 +8,7 @@ # number of ribs and spars cfgpmtr nribs 10 cfgpmtr nspars 2 +cfgpmtr exploded 0 # 0 - non-exploded, 1 upperOML, 2 internalStruct, 3 - lowerOML # root and tip chord despmtr chord0 2.0 @@ -58,7 +59,7 @@ rotatex 90 0 0 translate xf -sspan zf # loft the solid wing and store it -loft 0 +rule # attribute the solid wing select face @@ -67,14 +68,22 @@ attribute capsLoad $OML attribute _color $blue # make root and tip caps the ribs -select face 5 +select face 4 attribute capsGroup $rib1 +attribute tagComp $rib attribute _color $green -select face 4 +select face 5 attribute capsGroup !$rib+nribs +attribute tagComp $rib attribute _color $green +select face 1 +attribute tagComp $OMLtop + +select face 2 +attribute tagComp $OMLbot + store solidWing ### 2. Internal Structure ### @@ -101,7 +110,7 @@ patbeg ispar nspars # add caps attributes select face attribute capsGroup !$spar+ispar - attribute has_group $yes + attribute tagComp $spar attribute _color $green patend @@ -121,7 +130,7 @@ patbeg index ninnerRibs # add caps attributes select face attribute capsGroup !$rib+irib - attribute has_group $yes + attribute tagComp $rib attribute _color $green # union with previous ribs/spars @@ -155,6 +164,9 @@ union select face $capsGroup $rib1 attribute capsConstraint $root +select face $capsGroup $* +attribute has_group $yes + # add the constraint attribute to adjacent edges, nodes # otherwise they can pop out of the mesh in the structural analysis udprim editAttr filename << @@ -173,6 +185,18 @@ udprim editAttr filename << patend >> +# extract only certain faces for the exploded view +ifthen exploded EQ 1 + select face $tagComp $OMLtop + extract @sellist +elseif exploded EQ 2 + select face $tagComp $rib + select add $tagComp $spar + extract @sellist +elseif exploded EQ 3 + select face $tagComp $OMLbot + extract @sellist +endif # add AIM attribute to specify the analyses to use select body @@ -180,4 +204,4 @@ attribute capsAIM $egadsTessAIM;tacsAIM end ||||| -||| \ No newline at end of file +|||||||||| \ No newline at end of file diff --git a/examples/caps_wing/simple_naca_wing.csm b/examples/caps_wing/simple_naca_wing.csm index 01c3c4c1d..3010e9554 100644 --- a/examples/caps_wing/simple_naca_wing.csm +++ b/examples/caps_wing/simple_naca_wing.csm @@ -59,7 +59,7 @@ rotatex 90 0 0 translate xf -sspan zf # loft the solid wing and store it -loft 0 +rule # attribute the solid wing select face diff --git a/tacs/caps2tacs/tacs_aim.py b/tacs/caps2tacs/tacs_aim.py index 1e322696b..babd5642b 100644 --- a/tacs/caps2tacs/tacs_aim.py +++ b/tacs/caps2tacs/tacs_aim.py @@ -108,7 +108,9 @@ def get_proc_with_shape_var(self, shape_var: ShapeVariable or str): if shape_var.name == this_shape_var.name: return rank # if not found in for loop trigger error - raise AssertionError(f"failed to find shape var {shape_var} on rank {self.comm.rank}") + raise AssertionError( + f"failed to find shape var {shape_var} on rank {self.comm.rank}" + ) @property def local_shape_vars(self) -> list: @@ -130,7 +132,7 @@ def setup_aim( self, large_format: bool = True, static: bool = True, - barrier:bool=True, + barrier: bool = True, ): # make sure there is at least one material, property, constraint, etc. assert len(self._materials) > 0 diff --git a/tacs/caps2tacs/tacs_model.py b/tacs/caps2tacs/tacs_model.py index 96ceb5e83..384becf46 100644 --- a/tacs/caps2tacs/tacs_model.py +++ b/tacs/caps2tacs/tacs_model.py @@ -63,7 +63,7 @@ def build( active_procs: list = [0], problem_name: str = "capsStruct", mesh_morph: bool = False, - verbosity:int=0, + verbosity: int = 0, ): """ make a pyCAPS problem with the tacsAIM and egadsAIM on serial / root proc @@ -79,7 +79,7 @@ def build( tacs_project: str project name for all ESP/CAPS work directory files (affects filenames) active_procs: list - list of active procs for parallel tacsAIM instances. Ex: if you want to use 5 + list of active procs for parallel tacsAIM instances. Ex: if you want to use 5 parallel tacsAIMs you can choose active_procs=[_ for _ in range(5)] or equivalently active_procs=[0,1,2,3,4] problem_name: str @@ -93,7 +93,7 @@ def build( assert 0 in active_procs caps_problem = None assert mesh in cls.MESH_AIMS - assert max(active_procs)+1 <= comm.Get_size() + assert max(active_procs) + 1 <= comm.Get_size() for iproc in active_procs: if comm.rank == iproc: @@ -291,19 +291,18 @@ def update_design(self, input_dict: dict = None): return changed_design - @property - def fea_solver(self) -> pyTACS: + def fea_solver(self, root=0) -> pyTACS: """ build pyTACS from nastran dat file on the root proc """ - return pyTACS(self.tacs_aim.dat_file_path(self.root_proc_ind), self.comm) + return pyTACS(self.tacs_aim.dat_file_path(root), self.comm) - def createTACSProbs(self, addFunctions: bool = True): + def createTACSProbs(self, root=0, addFunctions: bool = True): """ creates TACS list of static, transient, or modal analysis TACS problems from the TacsAim class most important call method from the tacsAim class: SPs = tacs_aim.createTACSProbs """ - fea_solver = self.fea_solver + fea_solver = self.fea_solver(root) fea_solver.initialize() SPs = fea_solver.createTACSProbsFromBDF() self.SPs = SPs # store the static problems as well