Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corrections to Caps2tacs docs and support for funtofem shape variable animations #269

Merged
merged 12 commits into from
Oct 31, 2023
33 changes: 16 additions & 17 deletions docs/source/caps2tacs/main.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ won't work until after the full install so you can ignore that if you source it

export ESP_ROOT=~/packages/ESP123/EngSketchPad
export PYTHONPATH=${PYTHONPATH}:$ESP_ROOT/pyESP
source $ESP_ROOT/ESPenv.shell
source $ESP_ROOT/ESPenv.sh
alias csm='$ESP_ROOT/bin/serveCSM'

The installation on a Linux machine proceeds as follows. Note if the ESP/OpenCASCADE version changes
Expand Down Expand Up @@ -39,13 +39,13 @@ Important things to include in your CSM file are included below (for reference s
* Configuration parameters such as ``cfgpmtr nribs 10`` which are integer variables usually fixed during optimization.
* Design parameters such as ``despmtr span 10`` which are real number variables that can be tied to a TACS shape variable.
* ``capsGroup`` attributes define regions of the geometry with the same property cars, denoted ``capsGroup $rib1``, etc.
We often use pattern statements to define these, and sometimes a user-defined primitive ``udprim editAttr``.
* ``capsConstraint`` attributes define regions of the geometry intended to have the same structural constraints, e.g. ``capsConstraint fix``.
These constraints include Temperature and elastic constraints as of right now.
* ``capsLoad`` attributes define regions of the geometry with the same fixed loads. This is useful in simple structural analyses.
Note that aerodynamic loads cannot be setup this way (see the funtofem github for how to do this).
* ``capsAIM`` attribute - this specifies for a body the analysis tools or AIMs using this CSM file. Note that we often add the
``tacsAIM, egadsTessAIM`` here for structural analyses.
We often use pattern statements to define these, and sometimes a user-defined primitive ``udprim editAttr``.
* ``capsConstraint`` attributes define regions of the geometry intended to have the same structural constraints, e.g. ``capsConstraint fix``.
These constraints include Temperature and elastic constraints as of right now.
* ``capsLoad`` attributes define regions of the geometry with the same fixed loads.
This is useful in simple structural analyses. Note that aerodynamic loads cannot be setup this way (see the funtofem github for how to do this).
* ``capsAIM`` attribute - this specifies for a body the analysis tools or AIMs using this CSM file.
Note that we often add the ``tacsAIM, egadsTessAIM`` here for structural analyses.
* Occasionally ``capsMesh`` attribute - which can be used to set alternative auto-mesh settings on different sections of the geometry.

The main ``TacsModel`` object supervises the process of TACS analysis and geometry updates for optimization.
Expand Down Expand Up @@ -73,18 +73,17 @@ hyperparameters by running a small analysis / using egadsAIM view routine until
There are certain objects that must be setup and registered to the tacs model before running a TACS analysis.
If these are not correctly specified, then the tacs model will throw an error in the setup check phase.

* Material properties - you must setup one or more material objects for use in the element property definitions.
Available material types include Isotropic and Orthotropic (Anisotropic can be added directly through the underlying tacsAIM).
Several common materials are saved as class methods such as aluminum, steel, titanium, carbon fiber.
* Material properties - you must setup one or more material objects for use in the element property definitions.
Available material types include Isotropic and Orthotropic (Anisotropic can be added directly through the underlying tacsAIM). Several common materials are saved as class methods such as aluminum, steel, titanium, carbon fiber.
* Element Properties - currently caps2tacs only supports shell elements for use in aerodynamic structures.
Element properties can be setup directly through a `ShellProperty` object or indirectly through the `ThicknessVariable` object.
The names of shell properties or the thickness variables must be tied to a `capsGroup` setup in the CSM file.
Element properties can be setup directly through a `ShellProperty` object or indirectly through the `ThicknessVariable` object.
The names of shell properties or the thickness variables must be tied to a `capsGroup` setup in the CSM file.
* Constraints - elastic and thermal constraints are available in caps2tacs. Common instances are the ``PinConstraint`` and ``TemperatureConstraint``.
The name/capsConstraint input to these constraint objects must match the ``capsConstraint`` attributes in the CSM file.
The name/capsConstraint input to these constraint objects must match the ``capsConstraint`` attributes in the CSM file.
* Loads - for static problems ``GridForce`` and ``Pressure`` load objects are available whose names must match the ``capsLoad`` attributes.
Note that for aerodynamic structures only coupling with a CFD software such as through the ``FUNtoFEM repo (see below`` can setup aerodynamic loads.
Note that for aerodynamic structures only coupling with a CFD software such as through the ``FUNtoFEM repo (see below`` can setup aerodynamic loads.
* Output Functionals for optimization - functionals such as ``ksfailure``, ``mass``, ``temperature``, ``compliance``
are available for use in structural optimizations.
are available for use in structural optimizations.

Once all of the above structural analysis setup objects are provided, the TacsModel is ready for analysis. You can
then run the setup method which completes the setup phase. Then, the routines ``pre_analysis()`` generates a mesh,
Expand All @@ -106,7 +105,7 @@ on a coarse mesh of a symmetric NACA 0012 wing structure.
2. An unsteady analysis with a vertical distributed load varying sinusoidally in time.
3. A sizing optimization which finds the optimal panel thicknesses to hold fixed aero loads.
4. A sizing and shape optimization which optimizes the panel thicknesses and location of ribs
and spars inside the wing to hold fixed aero loads.
and spars inside the wing to hold fixed aero loads.
5. A steady analysis, using the AFLR AIM for meshing, with a vertical distributed load.

The sizing optimization shown below resulted in about a 40\% drop in weight from the equal thickness design. Notice
Expand Down
7 changes: 4 additions & 3 deletions tacs/caps2tacs/gif_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ class GifWriter:
module to write gifs from a set of pngs
"""

def __init__(self, frames_per_second: int = 4):
self._fps = frames_per_second
def __init__(self, duration: int = 20):
# duration of each frame
self._duration = duration

def __call__(self, gif_filename: str, path: str):
"""
call on current path to create gif of given filename
"""
gif_filepath = os.path.join(path, gif_filename)
with imageio.get_writer(gif_filepath, mode="I", fps=self._fps) as writer:
with imageio.get_writer(gif_filepath, mode="I", duration=self._duration) as writer:
sean-engelstad marked this conversation as resolved.
Show resolved Hide resolved
path_dir = os.listdir(path)
path_dir = sorted(path_dir)
for image_file in path_dir:
Expand Down
52 changes: 52 additions & 0 deletions tacs/caps2tacs/tacs_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
from typing import List
from tacs.pytacs import pyTACS

# optional funtofem dependency for shape variable animations
# will still work without funtofem
import importlib
f2f_loader = importlib.util.find_spec("funtofem")

class TacsModel:
MESH_AIMS = ["egads", "aflr"]
Expand Down Expand Up @@ -273,6 +277,41 @@ def createTACSProbs(self, addFunctions: bool = True):
)
return self.SPs

def _convert_shape_vars_dict(self, shape_vars_dict:dict):
"""convert the shape variable dict keys from str or funtofem variables to Tacs Shape Variables if necessary"""
# convert shape variable dict to caps2tacs ShapeVariable keys if necessary
shape_vars_dict2 = {}
first_key = list(shape_vars_dict.keys())[0]
if isinstance(first_key, ShapeVariable):
shape_vars_dict2 = shape_vars_dict
elif isinstance(first_key, str):
# get matching caps2tacs shape variable for each string
for key in shape_vars_dict:
for var in self.tacs_aim.shape_variables:
if var.name == key:
shape_vars_dict2[var] = shape_vars_dict[key]
break # go to next key
elif f2f_loader is not None: # optional funtofem dependency
# import has to go here to prevent circular import
from funtofem import Variable

if isinstance(first_key, Variable):
# get matching caps2tacs shape variable for each F2F shape variable
for f2f_var in shape_vars_dict:
for tacs_var in self.tacs_aim.shape_variables:
if f2f_var.name == tacs_var.name:

# copy the value list to the new dictionary
shape_vars_dict2[tacs_var] = shape_vars_dict[f2f_var]
break # go to next variable

else:
raise AssertionError(f"The datatype {type(first_key)} is not allowed in caps2tacs shape variable dictionaries.")
else:
raise AssertionError(f"The datatype {type(first_key)} is not allowed in caps2tacs shape variable dictionaries.")

return shape_vars_dict2

def animate_shape_vars(self, shape_vars_dict: dict):
"""
Animate the shape variables in ESP/CAPS.
Expand All @@ -284,6 +323,7 @@ def animate_shape_vars(self, shape_vars_dict: dict):

e.g. if you wish to animate over the ShapeVariable objects var1, and var2
create the following shape_vars_dict
NOTE : the keys can be str, ShapeVariable or funtofem variable objects objects
shape_vars_dict = {
var1 : [_*0.1 for _ in range(1,4)],
var2 : [_*0.05 for _ in range(-3,4)],
Expand All @@ -301,6 +341,9 @@ def animate_shape_vars(self, shape_vars_dict: dict):
for shape_var in self.tacs_aim.shape_variables:
shape_var._active = False

# convert the shape variables dict keys from str or F2F variable to Tacs Shape Variables
shape_vars_dict = self._convert_shape_vars_dict(shape_vars_dict)

for shape_var in shape_vars_dict:
value_list = shape_vars_dict[shape_var]

Expand All @@ -309,6 +352,9 @@ def animate_shape_vars(self, shape_vars_dict: dict):
shape_var._active = True
self.tacs_aim.setup_aim()

# save the original value
orig_value = shape_var.value * 1.0

for i, value in enumerate(value_list):
shape_var.value = value
self.geometry.despmtr[shape_var.name].value = value
Expand All @@ -328,8 +374,14 @@ def animate_shape_vars(self, shape_vars_dict: dict):
self.SPs[caseID].writeSensFile(
evalFuncs=None, tacsAim=self.tacs_aim,
)

del self.SPs
self.tacs_aim.post_analysis()

# return to the original value
shape_var.value = orig_value
self.geometry.despmtr[shape_var.name].value = orig_value

# make the shape variable inactive again
shape_var._active = False
print(
Expand Down
Loading