diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index d12c9c29..64946d43 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -113,10 +113,10 @@ jobs: cd $F2F_DIR; mkdir extern cd $F2F_DIR/extern/ - wget https://acdl.mit.edu/ESP/PreBuilts/ESP123-linux-x86_64.tgz - tar -xvf ESP123-linux-x86_64.tgz - export ESP_ROOT=${F2F_DIR}/extern/ESP123/EngSketchPad - export CASROOT=${F2F_DIR}/extern/ESP123/OpenCASCADE-7.7.0 + wget https://acdl.mit.edu/ESP/PreBuilts/ESP124-linux-x86_64.tgz + tar -xvf ESP124-linux-x86_64.tgz + export ESP_ROOT=${F2F_DIR}/extern/ESP124/EngSketchPad + export CASROOT=${F2F_DIR}/extern/ESP124/OpenCASCADE-7.7.0 export CASARCH= export PATH=$PATH:$CASROOT/bin export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CASROOT/lib @@ -125,7 +125,7 @@ jobs: source $ESP_ROOT/../ESPenv.sh cd ./src/CAPS/aim make - cd $F2F_DIR/extern/ESP123/ + cd $F2F_DIR/extern/ESP124/ # remove all ESP/CAPS unit test files with recursive delete find . -name "test*" -type f -delete cd $F2F_DIR @@ -136,7 +136,7 @@ jobs: echo "Running Tests"; echo "============================================================="; if [[ ${{ matrix.NAME }} == 'Real' ]]; then - source ${F2F_DIR}/extern/ESP123/ESPenv.sh + source ${F2F_DIR}/extern/ESP124/ESPenv.sh fi testflo ${GITHUB_WORKSPACE}/tests/unit_tests/; diff --git a/examples/framework/functionality/stand_alone/matrix_pencil/fake_aerodynamics.py b/examples/framework/functionality/stand_alone/matrix_pencil/fake_aerodynamics.py index 695dcc14..1588f337 100644 --- a/examples/framework/functionality/stand_alone/matrix_pencil/fake_aerodynamics.py +++ b/examples/framework/functionality/stand_alone/matrix_pencil/fake_aerodynamics.py @@ -103,8 +103,8 @@ def get_function_gradients(self, scenario, bodies, offset): if vartype == "aerodynamic": for i, var in enumerate(scenario.variables[vartype]): if var.active and "force" in var.name: - scenario.derivatives[vartype][offset + func][ - i - ] = -np.sum(self.fixed_step_psi_A) + scenario.derivatives[vartype][offset + func][i] = ( + -np.sum(self.fixed_step_psi_A) + ) return 0 diff --git a/examples/framework/functionality/stand_alone/matrix_pencil/structural_model_nbg.py b/examples/framework/functionality/stand_alone/matrix_pencil/structural_model_nbg.py index 85a4becc..52e40660 100644 --- a/examples/framework/functionality/stand_alone/matrix_pencil/structural_model_nbg.py +++ b/examples/framework/functionality/stand_alone/matrix_pencil/structural_model_nbg.py @@ -533,26 +533,28 @@ def get_function_gradients(self, scenario, bodies, offset): for i, var in enumerate(scenario.variables[vartype]): if var.name == "alpha0" and var.active: if self.comm.rank == 0: - scenario.derivatives[vartype][offset + ifunc][ - i - ] = self.dfdx[1] - - scenario.derivatives[vartype][offset + ifunc][ - i - ] = self.comm.bcast( - scenario.derivatives[vartype][offset + ifunc][i], root=0 + scenario.derivatives[vartype][offset + ifunc][i] = ( + self.dfdx[1] + ) + + scenario.derivatives[vartype][offset + ifunc][i] = ( + self.comm.bcast( + scenario.derivatives[vartype][offset + ifunc][i], + root=0, + ) ) if var.name == "struct_dt" and var.active: if self.comm.rank == 0: - scenario.derivatives[vartype][offset + ifunc][ - i - ] = self.dfdx[0] - - scenario.derivatives[vartype][offset + ifunc][ - i - ] = self.comm.bcast( - scenario.derivatives[vartype][offset + ifunc][i], root=0 + scenario.derivatives[vartype][offset + ifunc][i] = ( + self.dfdx[0] + ) + + scenario.derivatives[vartype][offset + ifunc][i] = ( + self.comm.bcast( + scenario.derivatives[vartype][offset + ifunc][i], + root=0, + ) ) def eval_func(self, x, name="dt"): diff --git a/funtofem/driver/_funtofem_driver.py b/funtofem/driver/_funtofem_driver.py index 69c627fe..29bc9712 100644 --- a/funtofem/driver/_funtofem_driver.py +++ b/funtofem/driver/_funtofem_driver.py @@ -25,6 +25,7 @@ import numpy as np from mpi4py import MPI from funtofem import TransferScheme +from .transfer_settings import TransferSettings try: from .hermes_transfer import HermesTransfer @@ -68,6 +69,10 @@ def __init__( comm_manager = solvers.comm_manager self.comm_manager = comm_manager + if transfer_settings is None: + transfer_settings = TransferSettings() + self.transfer_settings = transfer_settings + # communicator self.comm = comm_manager.master_comm self.aero_comm = comm_manager.aero_comm @@ -113,7 +118,7 @@ def __init__( self.struct_root, self.aero_comm, self.aero_root, - transfer_settings=transfer_settings, + transfer_settings=self.transfer_settings, ) # Initialize the shape parameterization @@ -327,3 +332,49 @@ def _solve_steady_adjoint(self, scenario): def _solve_unsteady_adjoint(self, scenario): return 1 + + def print_summary(self, print_model=False, print_comm=False): + """ + Print out a summary of the FUNtoFEM driver for inspection. + """ + + print("==========================================================") + print("|| FUNtoFEM Driver Summary ||") + print("==========================================================") + print(self) + + self._print_transfer(print_comm=print_comm) + + if print_model: + print( + "\nPrinting abbreviated model summary. For details print model summary directly." + ) + self.model.print_summary(print_level=-1, ignore_rigid=True) + + return + + def _print_transfer(self, print_comm=False): + print("\n---------------------") + print("| Transfer Settings |") + print("---------------------") + + print(f" Elastic scheme: {self.transfer_settings.elastic_scheme}") + print(f" No. points: {self.transfer_settings.npts}") + print(f" Beta: {self.transfer_settings.beta}") + print(f" Thermal scheme: {self.transfer_settings.thermal_scheme}") + print(f" No. points: {self.transfer_settings.thermal_npts}") + print(f" Beta: {self.transfer_settings.thermal_beta}\n") + + if print_comm: + print(self.comm_manager) + + return + + def __str__(self): + line1 = f"Driver (): {self.__class__.__qualname__}" + line2 = f" Model: {self.model.name}" + line3 = f" Number of scenarios: {len(self.model.scenarios)}" + + output = (line1, line2, line3) + + return "\n".join(output) diff --git a/funtofem/driver/funtofem_shape_driver.py b/funtofem/driver/funtofem_shape_driver.py index 58129010..eb4c211b 100644 --- a/funtofem/driver/funtofem_shape_driver.py +++ b/funtofem/driver/funtofem_shape_driver.py @@ -170,7 +170,6 @@ def __init__( solvers, comm_manager, transfer_settings, model ) - self.transfer_settings = transfer_settings self.remote = remote self.is_paired = is_paired self.struct_nprocs = struct_nprocs @@ -364,16 +363,16 @@ def solve_forward(self): root=0, ) - if not (self.is_remote) and self.change_shape: - # case where analysis script does the meshing and the remote does not. - # need to read new shape variable values before doing the meshing - self.model.read_design_variables_file( - self.comm, - filename=Remote.paths(self.comm, self.flow_dir).design_file, - root=0, - ) - if not (self.is_remote) and self.is_paired: + if self.change_shape: + # case where analysis script does the meshing and the remote does not. + # need to read new shape variable values before doing the meshing + self.model.read_design_variables_file( + self.comm, + filename=Remote.paths(self.comm, self.flow_dir).design_file, + root=0, + ) + # remove the _functions_file so remote will fail if self.comm.rank == 0: analysis_functions_file = Remote.paths( @@ -709,11 +708,11 @@ def solve_adjoint(self): # not sure if this barrier is necessary here but just in case self.comm.Barrier() - if not self.is_remote: - # delete struct interface to free up memory in shape change - # self.solvers.structural._deallocate() - del self.solvers.structural - self.comm.Barrier() + # if not self.is_remote: + # # delete struct interface to free up memory in shape change + # # self.solvers.structural._deallocate() + # del self.solvers.structural + # self.comm.Barrier() self.comm.Barrier() start_time = time.time() @@ -1067,3 +1066,59 @@ def remote_meshing(self) -> bool: @property def tacs_model(self): return self.model.structural + + def print_summary(self, print_model=False, print_comm=False): + """ + Print out a summary of the FUNtoFEM driver for inspection. + """ + + print("\n\n==========================================================") + print("|| FUNtoFEM Driver Summary ||") + print("==========================================================") + print(self) + + self._print_shape_change() + self._print_transfer(print_comm=print_comm) + + if print_model: + print( + "\nPrinting abbreviated model summary. For details print model summary directly." + ) + self.model.print_summary(print_level=-1, ignore_rigid=True) + + return + + def _print_shape_change(self): + _num_shape_vars = len(self.shape_variables) + print("\n--------------------") + print("| Shape Change |") + print("--------------------") + + print(f" No. shape variables: {_num_shape_vars}") + print(f" Aerodynamic shape change: {self.aero_shape}") + print(f" Structural shape change: {self.struct_shape}") + + print(f" Meshing:", end=" ") + if self.is_paired: + # Remeshing + print(f" RE-MESH") + if self.change_shape: + print(f" Remote is meshing.") + else: + print(f" Analysis script is meshing.") + else: + # Morphing + print(f" MORPH") + + return + + def __str__(self): + line1 = f"Driver (): {self.__class__.__qualname__}" + line2 = f" Using remote: {self.is_remote}" + line3 = f" Flow solver type: {self._flow_solver_type}" + line4 = f" Structural solver type: {self._struct_solver_type}" + line5 = f" No. structural procs: {self.struct_nprocs}" + + output = (line1, line2, line3, line4, line5) + + return "\n".join(output) diff --git a/funtofem/driver/oneway_struct_driver.py b/funtofem/driver/oneway_struct_driver.py index bf9d0828..12daf863 100644 --- a/funtofem/driver/oneway_struct_driver.py +++ b/funtofem/driver/oneway_struct_driver.py @@ -545,9 +545,11 @@ def solve_adjoint(self): # write the sensitivity file for the tacs AIM self.model.write_sensitivity_file( comm=self.comm, - filename=self.struct_aim.root_sens_file - if not self.fun3d_dir - else self.analysis_sens_file, + filename=( + self.struct_aim.root_sens_file + if not self.fun3d_dir + else self.analysis_sens_file + ), discipline="structural", ) diff --git a/funtofem/interface/caps2fun/aflr_aim.py b/funtofem/interface/caps2fun/aflr_aim.py index 1d3ec871..b8fff8c5 100644 --- a/funtofem/interface/caps2fun/aflr_aim.py +++ b/funtofem/interface/caps2fun/aflr_aim.py @@ -104,12 +104,13 @@ def _setDictOptions(self): """ Set AFLR3 and AFLR4 options via dictionaries. """ - dictOptions = self._dictOptions + if self.root_proc: + dictOptions = self._dictOptions - for ind, option in enumerate(dictOptions["aflr4AIM"]): - self.surface_aim.input[option].value = dictOptions["aflr4AIM"][option] + for ind, option in enumerate(dictOptions["aflr4AIM"]): + self.surface_aim.input[option].value = dictOptions["aflr4AIM"][option] - for ind, option in enumerate(dictOptions["aflr3AIM"]): - self.volume_aim.input[option].value = dictOptions["aflr3AIM"][option] + for ind, option in enumerate(dictOptions["aflr3AIM"]): + self.volume_aim.input[option].value = dictOptions["aflr3AIM"][option] return self diff --git a/funtofem/interface/caps2fun/fun3d_model.py b/funtofem/interface/caps2fun/fun3d_model.py index 01aca993..04133d17 100644 --- a/funtofem/interface/caps2fun/fun3d_model.py +++ b/funtofem/interface/caps2fun/fun3d_model.py @@ -37,13 +37,25 @@ def build( verbosity=0, ): """ - make a pyCAPS problem with the tacsAIM and egadsAIM on serial / root proc + Make a pyCAPS problem with the tacsAIM and egadsAIM on serial / root proc Parameters --------------------------------- csm_file : filepath - filename / full path of ESP/CAPS Constructive Solid Model or .CSM file + Filename / full path of ESP/CAPS Constructive Solid Model or .CSM file. comm : MPI.COMM - MPI communicator + MPI communicator. + project_name : str + Name of the case that is passed to the flow side, e.g., what is used to name the FUN3D input files. + problem_name : str + CAPS problem name, internal name used to define the CAPS problem and determines the name of the directory + that is created by CAPS to build the fluid mesh, geometry, sensitivity files, etc. + mesh_morph : bool + Turn mesh morphing on or off for use with shape variables that alter the fluid geometry + (e.g., when using mesh deformation rather than remeshing). + root : int + The rank of the processor that will control this process. + verbosity : int + Parameter passed directly to pyCAPS to determine output level. """ caps_problem = None if comm.rank == root: @@ -55,24 +67,6 @@ def build( comm.Barrier() return cls(fun3d_aim, aflr_aim, comm, project_name, root=root) - @classmethod - def build_morph( - cls, - csm_file, - comm, - project_name="fun3d_CAPS", - root: int = 0, - problem_name: str = "capsFluid", - ): - return cls.build( - csm_file=csm_file, - comm=comm, - project_name=project_name, - problem_name=problem_name, - root=root, - mesh_morph=True, - ) - @property def root_proc(self) -> bool: return self.fun3d_aim.root_proc diff --git a/funtofem/interface/solver_manager.py b/funtofem/interface/solver_manager.py index 15a079ce..0d7a308c 100644 --- a/funtofem/interface/solver_manager.py +++ b/funtofem/interface/solver_manager.py @@ -41,6 +41,18 @@ def __init__( self.aero_comm = master_comm self.aero_root = aero_root + def __str__(self): + line0 = f"CommManager" + line1 = f" Master comm: {self.master_comm}" + line2 = f" Aero comm: {self.aero_comm}" + line3 = f" Aero root: {self.aero_root}" + line4 = f" Struct comm: {self.struct_comm}" + line5 = f" Struct root: {self.struct_root}" + + output = (line0, line1, line2, line3, line4, line5) + + return "\n".join(output) + class SolverManager: def __init__(self, comm, use_flow: bool = True, use_struct: bool = True): diff --git a/funtofem/interface/utils/fun3d_client.py b/funtofem/interface/utils/fun3d_client.py index 0c455f33..d6dcbbd8 100644 --- a/funtofem/interface/utils/fun3d_client.py +++ b/funtofem/interface/utils/fun3d_client.py @@ -435,20 +435,20 @@ def get_function_gradients(self, scenario, bodies, offset): for i, var in enumerate(scenario.variables[vartype]): if var.active: if function.adjoint: - scenario.derivatives[vartype][offset + func][ - i - ] = self.fun3d_client.get_design_global_derivative( - function.id, var.id + scenario.derivatives[vartype][offset + func][i] = ( + self.fun3d_client.get_design_global_derivative( + function.id, var.id + ) ) else: scenario.derivatives[vartype][offset + func][ i ] = 0.0 - scenario.derivatives[vartype][offset + func][ - i - ] = self.comm.bcast( - scenario.derivatives[vartype][offset + func][i], - root=0, + scenario.derivatives[vartype][offset + func][i] = ( + self.comm.bcast( + scenario.derivatives[vartype][offset + func][i], + root=0, + ) ) for ibody, body in enumerate(bodies, 1): @@ -457,20 +457,22 @@ def get_function_gradients(self, scenario, bodies, offset): for i, var in enumerate(body.variables[vartype]): if var.active: if function.adjoint: - body.derivatives[vartype][offset + func][ - i - ] = self.fun3d_client.get_design_rigid_derivative( - ibody, function.id, var.id + body.derivatives[vartype][offset + func][i] = ( + self.fun3d_client.get_design_rigid_derivative( + ibody, function.id, var.id + ) ) else: body.derivatives[vartype][offset + func][ i ] = 0.0 - scenario.derivatives[vartype][offset + func][ - i - ] = self.comm.bcast( - scenario.derivatives[vartype][offset + func][i], - root=0, + scenario.derivatives[vartype][offset + func][i] = ( + self.comm.bcast( + scenario.derivatives[vartype][ + offset + func + ][i], + root=0, + ) ) except FUN3DAeroException as e: diff --git a/funtofem/model/_base.py b/funtofem/model/_base.py index b33df90f..5e6179a2 100644 --- a/funtofem/model/_base.py +++ b/funtofem/model/_base.py @@ -258,6 +258,23 @@ def get_active_variables(self): return full_list + def get_inactive_variables(self): + """ + Get the list of active variables in body or scenario + + Returns + ------- + active_list: list of variables + list of active variables + """ + full_list = [] + is_active = lambda var: var.active == False + + for vartype in self.variables: + full_list.extend(list(filter(is_active, self.variables[vartype]))) + + return full_list + def get_uncoupled_variables(self): """ Get the list of uncoupled, active variables in body or scenario @@ -320,103 +337,83 @@ def set_id(self, id): def _print_functions(self): print( - " ------------------------------------------------------------------------------------" - ) - print( - " | Function \t| Analysis Type\t| Comp. Adjoint\t| Time Range\t| Averaging\t|" + " --------------------------------------------------------------------------------" ) + self._print_long("Function", width=12, indent_line=5) + self._print_long("Analysis Type", width=15) + self._print_long("Comp. Adjoint", width=15) + self._print_long("Time Range", width=20) + self._print_long("Averaging", end_line=True) + print( - " ------------------------------------------------------------------------------------" + " --------------------------------------------------------------------------------" ) for func in self.functions: - if len(func.name) >= 8: - print( - " | ", - func.name, - "\t| ", - func.analysis_type, - "\t| ", - func.adjoint, - "\t| [", - func.start, - ",", - func.stop, - "] \t| ", - func.averaging, - "\t|", - ) - else: - print( - " | ", - func.name, - "\t\t| ", - func.analysis_type, - "\t| ", - func.adjoint, - "\t| [", - func.start, - ",", - func.stop, - "] \t| ", - func.averaging, - "\t|", - ) + analysis_type = func.analysis_type + adjoint = func.adjoint + start = func.start + stop = func.stop + averaging = func.averaging + _time_range = " ".join(("[", str(start), ",", str(stop), "]")) + adjoint = str(adjoint) + self._print_long(func.name, width=12, indent_line=5) + self._print_long(analysis_type, width=15) + self._print_long(adjoint, width=15) + self._print_long(_time_range, width=20) + self._print_long(averaging, end_line=True) + print( - " ------------------------------------------------------------------------------------" + " --------------------------------------------------------------------------------" ) return def _print_variables(self, vartype): print( - " ------------------------------------------------------------------------------------------------------------" - ) - print( - " | Variable\t\t| Var. ID\t| Value\t\t| Bounds\t\t| Active\t| Coupled\t|" + " --------------------------------------------------------------------------------------" ) + self._print_long("Variable", width=12, indent_line=5) + self._print_long("Var. ID", width=10) + self._print_long("Value", width=16) + self._print_long("Bounds", width=24) + self._print_long("Active", width=8) + self._print_long("Coupled", width=9, end_line=True) + print( - " ------------------------------------------------------------------------------------------------------------" + " --------------------------------------------------------------------------------------" ) for var in self.variables[vartype]: - if len(var.name) >= 8: - print( - " | ", - var.name, - "\t|", - var.id, - "\t\t|", - var.value, - " \t| [", - var.lower, - ",", - var.upper, - "] \t|", - var.active, - " \t|", - var.coupled, - "\t|", - ) - else: - print( - " | ", - var.name, - "\t\t|", - var.id, - "\t\t|", - var.value, - " \t| [", - var.lower, - ",", - var.upper, - "] \t|", - var.active, - " \t|", - var.coupled, - "\t|", - ) + _name = "{:s}".format(var.name) + _id = "{: d}".format(var.id) + _value = "{:#.8g}".format(var.value) + _lower = "{:#.3g}".format(var.lower) + _upper = "{:#.3g}".format(var.upper) + _active = str(var.active) + _coupled = str(var.coupled) + _bounds = " ".join(("[", _lower, ",", _upper, "]")) + + self._print_long(_name, width=12, indent_line=5) + self._print_long(_id, width=10, align="<") + self._print_long(_value, width=16) + self._print_long(_bounds, width=24) + self._print_long(_active, width=8) + self._print_long(_coupled, width=9, end_line=True) print( - " ------------------------------------------------------------------------------------------------------------" + " --------------------------------------------------------------------------------------" ) return + + def _print_long(self, value, width=12, indent_line=0, end_line=False, align="^"): + if value is None: + value = "None" + if indent_line > 0: + print("{val:{wid}}".format(wid=indent_line, val=""), end="") + if not end_line: + print("|{val:{ali}{wid}}".format(wid=width, ali=align, val=value), end="") + else: + print( + "|{val:{ali}{wid}}|".format(wid=width, ali=align, val=value), end="\n" + ) + return diff --git a/funtofem/model/body.py b/funtofem/model/body.py index d1f2fa37..a9516783 100644 --- a/funtofem/model/body.py +++ b/funtofem/model/body.py @@ -1614,9 +1614,9 @@ def _distribute_aero_loads(self, data): for ind, aero_id in enumerate(self.aero_id): if self.transfer is not None: - self.aero_loads[scenario_id][ - 3 * ind : 3 * ind + 3 - ] = scenario_entry_dict[aero_id]["load"] + self.aero_loads[scenario_id][3 * ind : 3 * ind + 3] = ( + scenario_entry_dict[aero_id]["load"] + ) if self.thermal_transfer is not None: self.aero_heat_flux[scenario_id][ind] = scenario_entry_dict[ aero_id @@ -1902,7 +1902,8 @@ def shape_derivative(self, scenario, offset): return def __str__(self): - line1 = f"Body ( ): {self.id} {self.name}" + line0 = f"Body ( ): {self.id} {self.name}" + line1 = f" Analysis type: {self.analysis_type}" line2 = f" Boundary: {self.boundary}" line3 = f" Coupling Group: {self.group}" line4 = f" Motion type: {self.motion_type}" @@ -1910,7 +1911,7 @@ def __str__(self): line6 = f" Relaxation scheme: {type(self.relaxation_scheme)}" line7 = f" Shape parameterization: {type(self.shape)}" - output = (line1, line2, line3, line4, line5, line6, line7) + output = (line0, line1, line2, line3, line4, line5, line6, line7) return "\n".join(output) diff --git a/funtofem/model/funtofem_model.py b/funtofem/model/funtofem_model.py index 0cf6a269..c29d848b 100644 --- a/funtofem/model/funtofem_model.py +++ b/funtofem/model/funtofem_model.py @@ -150,8 +150,6 @@ def _send_struct_variables(self, base): shape_variables = base.variables["shape"] for var in struct_variables: - if not (var.active): - continue # check if matching shell property exists matching_prop = False for prop in self.structural.tacs_aim._properties: @@ -167,7 +165,10 @@ def _send_struct_variables(self, base): if matching_prop and not (matching_dv): caps2tacs.ThicknessVariable( - caps_group=var.name, value=var.value, name=var.name + caps_group=var.name, + value=var.value, + name=var.name, + active=var.active, ).register_to(self.structural) esp_caps_despmtrs = None @@ -241,7 +242,7 @@ def _send_flow_variables(self, base): self.flow.set_variables(active_shape_vars, active_aero_vars) return - def get_variables(self, names=None): + def get_variables(self, names=None, all=False): """ Get all the coupled and uncoupled variable objects for the entire model. Coupled variables only appear once. @@ -250,6 +251,8 @@ def get_variables(self, names=None): ---------- names: str or List[str] one variable name or a list of variable names + all: bool + Flag to include inactive variables. Returns ------- @@ -263,12 +266,16 @@ def get_variables(self, names=None): dv.extend(scenario.get_active_variables()) else: dv.extend(scenario.get_uncoupled_variables()) + if all: + dv.extend(scenario.get_inactive_variables()) for body in self.bodies: if body.group_root: dv.extend(body.get_active_variables()) else: dv.extend(body.get_uncoupled_variables()) + if all: + dv.extend(body.get_inactive_variables()) return dv elif isinstance(names, str): @@ -742,6 +749,9 @@ def read_design_variables_file(self, comm, filename, root=0): Discipline Var_name Var_value + IMPORTANT: To correctly set inactive variables, make sure to call (e.g.) tacs_aim.setup_aim(), + then read_design_variables_file, then tacs_aim.pre_analysis. + Parameters ---------- comm: MPI communicator @@ -773,10 +783,14 @@ def read_design_variables_file(self, comm, filename, root=0): variables_dict = comm.bcast(variables_dict, root=root) # update the variable values on each processor - for var in self.get_variables(): + for var in self.get_variables(all=True): if var.full_name in variables_dict: var.value = variables_dict[var.full_name] + if self.structural is not None: + input_dict = {var.name: var.value for var in self.get_variables(all=True)} + self.structural.update_design(input_dict) + return def write_design_variables_file(self, comm, filename, root=0): @@ -1021,13 +1035,16 @@ def print_summary( def _print_functions(self): model_functions = self.get_functions(all=True) print( - " ------------------------------------------------------------------------------------" - ) - print( - " | Function \t| Analysis Type\t| Comp. Adjoint\t| Time Range\t| Averaging\t|" + " --------------------------------------------------------------------------------" ) + self._print_long("Function", width=12, indent_line=5) + self._print_long("Analysis Type", width=15) + self._print_long("Comp. Adjoint", width=15) + self._print_long("Time Range", width=20) + self._print_long("Averaging", end_line=True) + print( - " ------------------------------------------------------------------------------------" + " --------------------------------------------------------------------------------" ) for func in model_functions: if isinstance(func, CompositeFunction): @@ -1042,40 +1059,15 @@ def _print_functions(self): start = func.start stop = func.stop averaging = func.averaging - if len(func.name) >= 8: - print( - " | ", - func.name, - "\t| ", - analysis_type, - "\t| ", - adjoint, - "\t| [", - start, - ",", - stop, - "] \t| ", - averaging, - "\t|", - ) - else: - print( - " | ", - func.name, - "\t\t| ", - analysis_type, - "\t| ", - adjoint, - "\t| [", - start, - ",", - stop, - "] \t| ", - averaging, - "\t|", - ) + _time_range = " ".join(("[", str(start), ",", str(stop), "]")) + adjoint = str(adjoint) + self._print_long(func.name, width=12, indent_line=5) + self._print_long(analysis_type, width=15) + self._print_long(adjoint, width=15) + self._print_long(_time_range, width=20) + self._print_long(averaging, end_line=True) print( - " ------------------------------------------------------------------------------------" + " --------------------------------------------------------------------------------" ) return @@ -1083,39 +1075,54 @@ def _print_functions(self): def _print_variables(self): model_variables = self.get_variables() print( - " ------------------------------------------------------------------------------------------------------------" - ) - print( - " | Variable\t\t| Var. ID\t| Value \t| Bounds\t\t| Active\t| Coupled\t|" + " --------------------------------------------------------------------------------------" ) + self._print_long("Variable", width=12, indent_line=5) + self._print_long("Var. ID", width=10) + self._print_long("Value", width=16) + self._print_long("Bounds", width=24) + self._print_long("Active", width=8) + self._print_long("Coupled", width=9, end_line=True) + print( - " ------------------------------------------------------------------------------------------------------------" + " --------------------------------------------------------------------------------------" ) for var in model_variables: - print( - " | ", - var.name, - "\t\t|", - var.id, - "\t\t|", - var.value, - " \t| [", - var.lower, - ",", - var.upper, - "] \t|", - var.active, - " \t|", - var.coupled, - "\t|", - ) + _name = "{:s}".format(var.name) + _id = "{: d}".format(var.id) + _value = "{:#.8g}".format(var.value) + _lower = "{:#.3g}".format(var.lower) + _upper = "{:#.3g}".format(var.upper) + _active = str(var.active) + _coupled = str(var.coupled) + _bounds = " ".join(("[", _lower, ",", _upper, "]")) + + self._print_long(_name, width=12, indent_line=5) + self._print_long(_id, width=10, align="<") + self._print_long(_value, width=16) + self._print_long(_bounds, width=24) + self._print_long(_active, width=8) + self._print_long(_coupled, width=9, end_line=True) print( - " ------------------------------------------------------------------------------------------------------------" + " --------------------------------------------------------------------------------------" ) return + def _print_long(self, value, width=12, indent_line=0, end_line=False, align="^"): + if value is None: + value = "None" + if indent_line > 0: + print("{val:{wid}}".format(wid=indent_line, val=""), end="") + if not end_line: + print("|{val:{ali}{wid}}".format(wid=width, ali=align, val=value), end="") + else: + print( + "|{val:{ali}{wid}}|".format(wid=width, ali=align, val=value), end="\n" + ) + return + def __str__(self): line1 = f"Model (): {self.name}" line2 = f" Number of bodies: {len(self.bodies)}" diff --git a/funtofem/mphys/mphys_meld.py b/funtofem/mphys/mphys_meld.py index aac9c751..e5620091 100644 --- a/funtofem/mphys/mphys_meld.py +++ b/funtofem/mphys/mphys_meld.py @@ -304,9 +304,9 @@ def compute(self, inputs, outputs): body.meld.transferLoads(f_a, f_s) for i in range(3): - outputs["f_struct"][ - body.struct_dof_indices[i :: self.struct_ndof] - ] = f_s[i::3] + outputs["f_struct"][body.struct_dof_indices[i :: self.struct_ndof]] = ( + f_s[i::3] + ) def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode): """