Adding Design Variables#

To add a design variable to an optimization, use the add_design_var method on System.

System.add_design_var(name, lower=None, upper=None, ref=None, ref0=None, indices=None, adder=None, scaler=None, units=None, parallel_deriv_color=None, cache_linear_solution=False, flat_indices=False)[source]

Add a design variable to this system.

Parameters:
namestr

Promoted name of the design variable in the system.

lowerfloat or ndarray, optional

Lower boundary for the input.

upperupper or ndarray, optional

Upper boundary for the input.

reffloat or ndarray, optional

Value of design var that scales to 1.0 in the driver.

ref0float or ndarray, optional

Value of design var that scales to 0.0 in the driver.

indicesiter of int, optional

If an input is an array, these indicate which entries are of interest for this particular design variable. These may be positive or negative integers.

adderfloat or ndarray, optional

Value to add to the model value to get the scaled value for the driver. adder is first in precedence. adder and scaler are an alterantive to using ref and ref0.

scalerfloat or ndarray, optional

Value to multiply the model value to get the scaled value for the driver. scaler is second in precedence. adder and scaler are an alterantive to using ref and ref0.

unitsstr, optional

Units to convert to before applying scaling.

parallel_deriv_colorstr

If specified, this design var will be grouped for parallel derivative calculations with other variables sharing the same parallel_deriv_color.

cache_linear_solutionbool

If True, store the linear solution vectors for this variable so they can be used to start the next linear solution with an initial guess equal to the solution from the previous linear solve.

flat_indicesbool

If True, interpret specified indices as being indices into a flat source array.

Notes

The response can be scaled using ref and ref0. The argument ref0 represents the physical value when the scaled value is 0. The argument ref represents the physical value when the scaled value is 1.

Specifying units#

You can specify units when adding a design variable. When this is done, the quanitity is converted from the target output’s units to the desired unit before giving it to the optimizer. When the optimizer commands a new design variable, it is assumed to be in the given units, and converted to the units of the input before setting the value in your model. If you also specify scaling, that scaling is applied after the unit conversion when being passed from the model to the optimizer, and before the unit conversion when being passed back into the model. Moreover, the upper and lower bound should be specified using these units.

import openmdao.api as om

prob = om.Problem()
model = prob.model

model.add_subsystem('comp1', om.ExecComp('y1 = 2.0*x',
                                         x={'val': 2.0, 'units': 'degF'},
                                         y1={'val': 2.0, 'units': 'degF'}),
                    promotes=['x', 'y1'])

model.add_subsystem('comp2', om.ExecComp('y2 = 3.0*x',
                                         x={'val': 2.0, 'units': 'degF'},
                                         y2={'val': 2.0, 'units': 'degF'}),
                    promotes=['x', 'y2'])

model.set_input_defaults('x', 35.0, units='degF')

model.add_design_var('x', units='degC', lower=0.0, upper=100.0)
model.add_constraint('y1', units='degC', lower=0.0, upper=100.0)
model.add_objective('y2', units='degC')

prob.setup()
prob.run_driver()
print('Model variables')
print(prob.get_val('x', indices=[0]))
Model variables
[35.]
print(prob.get_val('comp2.y2', indices=[0]))
[105.]
print(prob.get_val('comp1.y1', indices=[0]))
[70.]
print('Driver variables')
dv = prob.driver.get_design_var_values()
print(dv['x'][0])
Driver variables
1.6666666666666983
obj = prob.driver.get_objective_values(driver_scaling=True)
print(obj['y2'][0])
40.555555555555586
con = prob.driver.get_constraint_values(driver_scaling=True)
print(con['y1'][0])
21.111111111111143

How the optimizer sees scaled variables#

As stated above, when dealing with both a unit conversion and user-defined scaling for design variables (or constraints or objectives), the quantity is first converted to the specified units and then scaled according to the users ref|ref0|scaler|adder specification.

This means that any scaling specification should be done in the specified units.

The following derivation of the “total_scaler” and the “total_adder” applies to both driver design variables and responses.

Given some design variable quantity y, we first convert it to the driver’s units. OpenMDAO’s unit system determines the scale factor (unit_scaler) and offset (unit_adder) that convert y from it’s units within the model to its units from the driver’s perspective. These are the units specified in the call to add_design_var (or add_constraint or add_objective).

y_in_desired_units = unit_scaler * (y + unit_adder)

Then we apply the user-declared scaling

y_opt = declared_scaler * (y_in_desired_units + declared_adder)

Thus the optimizer sees the quantity as:

y_opt = declared_scaler * (unit_scaler * (y + unit_adder) + declared_adder)

If we gather the scaler and adder terms we have

y_opt = [declared_scaler * unit_scaler] * (y + unit_adder + declared_adder / unit_scaler)

And therefore the “total_scaler” and “total_adder” are:

total_scaler = declared_scaler * unit_scaler

total_adder = unit_adder + declared_adder / unit_scaler