Driver Recording#
A CaseRecorder is commonly attached to the problem’s Driver in order to gain insight into the convergence of the model as the driver finds a solution. By default, a recorder attached to a driver will record the design variables, constraints and objectives.
The driver recorder is capable of capturing any values from any part of the model, not just the design variables, constraints, and objectives.
/usr/share/miniconda/envs/test/lib/python3.11/site-packages/openmdao/utils/notebook_utils.py:120: OMDeprecationWarning:Argument `recording_options` is deprecated. Use `options_dict="recording_options" to remove this warning.
Option | Default | Acceptable Values | Acceptable Types | Description |
---|---|---|---|---|
excludes | [] | N/A | ['list'] | Patterns for vars to exclude in recording (processed post-includes). Uses fnmatch wildcards |
includes | [] | N/A | ['list'] | Patterns for variables to include in recording. Uses fnmatch wildcards |
record_constraints | True | [True, False] | ['bool'] | Set to True to record constraints at the driver level |
record_derivatives | False | [True, False] | ['bool'] | Set to True to record derivatives at the driver level |
record_desvars | True | [True, False] | ['bool'] | Set to True to record design variables at the driver level |
record_inputs | True | [True, False] | ['bool'] | Set to True to record inputs at the driver level |
record_objectives | True | [True, False] | ['bool'] | Set to True to record objectives at the driver level |
record_outputs | True | [True, False] | ['bool'] | Set True to record outputs at the driver level. |
record_residuals | False | [True, False] | ['bool'] | Set True to record residuals at the driver level. |
record_responses | False | [True, False] | ['bool'] | Set True to record constraints and objectives at the driver level |
Note
Note that the excludes
option takes precedence over the includes
option.
Driver Recording Example#
In the example below, we first run a case while recording at the driver level. Then, we examine the objective, constraint, and design variable values at the last recorded case. Lastly, we print the full contents of the last case, including outputs from the problem that are not design variables, constraints, or objectives.
Specifically, y1
and y2
are some of those intermediate outputs that are recorded due to the use of:
driver.recording_options['includes'] = ['*']
SellarDerivatives
class definition
class SellarDerivatives(om.Group):
"""
Group containing the Sellar MDA. This version uses the disciplines with derivatives.
"""
def setup(self):
self.add_subsystem('d1', SellarDis1withDerivatives(), promotes=['x', 'z', 'y1', 'y2'])
self.add_subsystem('d2', SellarDis2withDerivatives(), promotes=['z', 'y1', 'y2'])
self.add_subsystem('obj_cmp', om.ExecComp('obj = x**2 + z[1] + y1 + exp(-y2)', obj=0.0,
x=0.0, z=np.array([0.0, 0.0]), y1=0.0, y2=0.0),
promotes=['obj', 'x', 'z', 'y1', 'y2'])
self.add_subsystem('con_cmp1', om.ExecComp('con1 = 3.16 - y1', con1=0.0, y1=0.0),
promotes=['con1', 'y1'])
self.add_subsystem('con_cmp2', om.ExecComp('con2 = y2 - 24.0', con2=0.0, y2=0.0),
promotes=['con2', 'y2'])
self.set_input_defaults('x', 1.0)
self.set_input_defaults('z', np.array([5.0, 2.0]))
import openmdao.api as om
from openmdao.test_suite.components.sellar_feature import SellarDerivatives
import numpy as np
model = SellarDerivatives()
model.nonlinear_solver = om.NonlinearBlockGS()
model.linear_solver = om.ScipyKrylov()
model.add_design_var('z', lower=np.array([-10.0, 0.0]),
upper=np.array([10.0, 10.0]))
model.add_design_var('x', lower=0.0, upper=10.0)
model.add_objective('obj')
model.add_constraint('con1', upper=0.0)
model.add_constraint('con2', upper=0.0)
driver = om.ScipyOptimizeDriver(optimizer='SLSQP', tol=1e-9)
driver.recording_options['includes'] = ['*']
driver.recording_options['record_objectives'] = True
driver.recording_options['record_constraints'] = True
driver.recording_options['record_desvars'] = True
driver.recording_options['record_inputs'] = True
driver.recording_options['record_outputs'] = True
driver.recording_options['record_residuals'] = True
driver.add_recorder(om.SqliteRecorder("cases.sql"))
prob = om.Problem(model, driver)
prob.setup()
prob.run_driver()
NL: NLBGS Converged in 8 iterations
NL: NLBGS Converged in 1 iterations
NL: NLBGS Converged in 9 iterations
NL: NLBGS Converged in 10 iterations
NL: NLBGS Converged in 10 iterations
NL: NLBGS Converged in 9 iterations
NL: NLBGS Converged in 6 iterations
Optimization terminated successfully (Exit mode 0)
Current function value: 3.183393951728078
Iterations: 6
Function evaluations: 6
Gradient evaluations: 6
Optimization Complete
-----------------------------------
Problem: problem
Driver: ScipyOptimizeDriver
success : True
iterations : 7
runtime : 4.2666E-02 s
model_evals : 7
model_time : 8.9344E-03 s
deriv_evals : 6
deriv_time : 1.8061E-02 s
exit_status : SUCCESS
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / "cases.sql")
driver_cases = cr.list_cases('driver')
driver |
---|
rank0:ScipyOptimize_SLSQP|0 |
rank0:ScipyOptimize_SLSQP|1 |
rank0:ScipyOptimize_SLSQP|2 |
rank0:ScipyOptimize_SLSQP|3 |
rank0:ScipyOptimize_SLSQP|4 |
rank0:ScipyOptimize_SLSQP|5 |
rank0:ScipyOptimize_SLSQP|6 |
last_case = cr.get_case(driver_cases[-1])
print(last_case)
driver rank0:ScipyOptimize_SLSQP|6 {'z': array([1.97763888, 0. ]), 'x': array([3.56410563e-15]), 'con1': array([-8.86162255e-11]), 'con2': array([-20.24472223]), 'y1': array([3.16]), 'y2': array([3.75527777]), 'obj': array([3.18339395])}
last_case.get_objectives()
{'obj': array([3.18339395])}
last_case.get_design_vars()
{'z': array([1.97763888, 0. ]), 'x': array([3.56410563e-15])}
last_case.get_constraints()
{'con1': array([-8.86162255e-11]), 'con2': array([-20.24472223])}
last_case.inputs['obj_cmp.x']
array([3.56410563e-15])
last_case.outputs['z']
array([1.97763888, 0. ])
last_case.residuals['obj']
array([3.88031829e-11])
last_case['y1']
array([3.16])