FUNtoFEM Model¶
The model classes are used to stored design and coupling data related to the aeroelastic problem. The model is made up of bodies and scenarios .
The bodies are the different bodies in the simulations, such as the blades of a rotor. The scenarios are the different points of a multipoint optimization.
Methods on this page with [driver call] and [model call] are calls that the user does not make directly and can be ignored.
Assembling the model¶
One way to build a model is by using the add_body()
and
add_scenario()
calls. New class methods are available for these classes
which provide shortcuts to their creation. See bodies and scenarios for details about their creation.
The bodies and scenarios are kept in lists in the same order that they are input.
Exchanging the model data with an optimizer¶
The model object provides a convenient way to exchange data with a python-based optimizer.
Design variables¶
The assembled model contains all the design variables information that an optimizer such as PyOpt needs.
A list of the active design for the entire model is returned by get_variables()
.
The output list contains the variable
type. Here is an example of passing the
variable information from the model to a PyOpt optimizer:
The variables are stored in the model as a dictionary. They return in of ordered list of scenarios in the order they were added to the model, then the bodies in the order that they were added. Within each scenario and body, the order is alphabetical by variable type. Within each variable type, the variables are in the order added to the body/scenario.
from pyOpt import Optimization
import numpy as np
opt_prob = Optimization('optimization',design_problem.eval_obj_con)
variables = model.get_variables()
for i,var in enumerate(variables):
opt_prob.addVar(var.name,type='c',value=var.value/var.scaling,
lower=var.lower/var.scaling,
upper=var.upper/var.scaling)
Optimizers such as PyOpt will typically provide a list of new values at each design point.
These can be set back into model type using set_variables()
.
The variables can be pre-scaled and passed directing into this call, or with the scale argument set to
True, the scaling will be done automatically.
Function values¶
Once the driver has been run, solve_forward()
, the functions can be
retrieved using get_functions()
.
The output list contains the function
type. The following code prints the function values:
driver.solve_forward()
functions = model.get_functions()
print "Function values:"
for function in enumerate(variables):
print function.name, '=', function.value
The functions in the FUNtoFEM model need to be single discipline functions to maintain modularity. The following pseudo code lays out a function that can be used as an objective evaluation with PyObj:
def eval_obj_con(x)
fail = 0
model.set_variables(x,scale=True)
fail = driver.solve_forward()
functions = model.get_functions()
# Manipulate/scale/combine single discipline functions
obj = XXXXX
con[:] = YYYYY
return obj, con, fail
Gradients¶
The derivatives in the model class are populated by solve_adjoint()
.
Gradients can be retrieved using get_function_gradients()
.
variables = model.get_variables()
driver.solve_forward()
functions = model.get_functions()
fail = driver.solve_adjoint()
grad = model.get_functions_gradients()
for ifunc,func in enumerate(functions):
for ivar,var in enumerate(variables):
print 'Derivative of ', func.name, 'w.r.t.', var.name,'=', grad[ifunc][ivar]
The following is a pseudo code would be a gradient evaluation for pyOpt.
def eval_obj_con_grad(x,obj,con)
fail = 0
# get the scaling values
variables = model.get_variables()
var_scale = []
for var in variables:
var_scaling.append(var.scale)
# solve the adjoint problem
fail = driver.solve_adjoint()
grad = model.get_functions_gradients()
# use the chain / automatic differentiation to get the composite function derivatives
g = dobj/dfunc0 * grad[0][:] *variable_scaling + dobj/dfunc1 * grad[1][:] * variable_scaling + ...
A = YYYYY
return g, A, fail
FUNtoFEM Model Class¶
Bodies and Scenarios¶
Bodies and scenarios organize the data associated for the coupling and design.
Additionally, the body class holds the shape parameterization and transfer scheme.
Shape parameterization is added by creating a new body class that subclasses the body
class.
See MassoudBody
for an example.
The driver adds the details for the transfer scheme. See FUNtoFEMDriver
.
Scenarios¶
Scenarios hold variables and functions in lists.
Variables and functions are added using add_function()
and add_variable()
.
An example of a scenario variable could be flow variables such as angle of attack or Mach number.
Bodies¶
Shape parameterization is added by creating a new body class that subclasses the body
class.
See MassoudBody
for an example.
The driver adds the details for the transfer scheme. See FUNtoFEMDriver
.
The aero_loads array is of size 3 x aero_nnodes.
Name |
Default Value |
Description |
---|---|---|
self.name |
Required Input |
Name |
self.analysis_type |
Required Input |
Analysis type (aeroelastic, aerothermal, aerothermoelastic) |
self.id |
Required Input |
ID |
self.group |
Required Input |
Group categorization |
self.group_master |
False |
Top-level body group |
self.boundary |
0.0 |
Body boundary |
self.motion_type |
deform |
Motion type |
self.variables |
{} |
Owned variables |
self.derivatives |
{} |
Owned derivatives |
self.parent |
None |
Body parent |
self.children |
[] |
Owned bodies |
self.shape |
None |
Shape |
self.parameterization |
1.0 |
Geometry type |
self.transfer |
None |
Transfer method |
self.thermal_transfer |
None |
Thermal transfer method |
self.struct_nnodes |
None |
Number of structural nodes |
self.aero_nnodes |
None |
Number of aerodynamic nodes |
self.xfer_ndof |
3.0 |
Variable degree of freedom |
self.therm_xfer_ndof |
1.0 |
Thermal variable degree of freedom |
self.T_ref |
300 |
Reference temperature |
self.struct_X |
None |
Structural variables |
self.aero_X |
None |
Aerodynamic variables |
self.struct_id |
None |
Structural ID |
self.aero_id |
None |
Aerodynamic ID |
self.struct_disps |
None |
Structural displacements |
self.struct_forces |
None |
Structural forces |
self.struct_loads |
None |
Structural loads |
self.rigid_transform |
None |
Body transformation scheme |
self.aero_disps |
None |
Aerodynamic displacements |
self.aero_forces |
None |
Aerodynamic forces |
self.aero_loads |
None |
Aerodynamic loads |
self.struct_temps |
None |
Structural temperature |
self.struct_heat_flux |
None |
Structural heat flux |
self.aero_temps |
None |
Aerodynamic temperature |
self.aero_heat_flux |
None |
Aerodynamic heat flux |
Base class¶
The body and scenario classes subclass the base class.
Although most of the methods in the base class are used internally in the model, set_variable()
allows the user to change attributes of existing variables in the body or scenario.
For example, if the shape parameterization created a specific number of variables, but you do not want to use all
of them in the optimization, you can set the variable to be inactive.
MassoudBody¶
Variables¶
Some solvers like FUN3D require some design variables to be defined, whether or not it is being used in design.
The active attribute of the Variable
class. Setting it to False, allows you to still define
the variable but it won’t be returned when calling get_variables()
.
When the variable is added to a Scenario
or Body
using add_variable()
,
the vartype argument specifies what discipline the variable is associated with.
Functions¶
Functions in the FUNtoFEM framework are single discipline functions. The analysis_type argument specifics which discipline the function is associated with. Currently the options are ‘aerodynamic’ and ‘structural’.
Once the function is instantiated, add_function
is used to add it to a scenario.
Although the function values could be accessed from the model type, get_functions()
will return all the functions defined in the model.
When FUNtoFEM is used with FUN3D, the name of any function that would be defined in the rubber.data can be used. For functions that do not need an adjoint, such as structural mass, the boolean, adjoint, should be set to false. Some functions are associated with particular bodies, for instance, if you wanted the thrust or torque of a rotor without including that of the sting model, you can use the body argument to pass that information to the solver.