Case Recording#
Case Recording Options#
The available recording options for different OpenMDAO classes are summarized in this table:
Recording Options |
Driver |
System |
Solver |
Problem |
---|---|---|---|---|
record_constraints |
X |
X |
||
record_desvars |
X |
X |
||
record_objectives |
X |
X |
||
record_derivatives |
X |
X |
||
record_responses |
X |
X |
||
record_inputs |
X |
X |
X |
X |
record_outputs |
X |
X |
X |
X |
record_residuals |
X |
X |
X |
|
record_abs_error |
X |
|||
record_rel_error |
X |
|||
record_solver_residuals |
X |
|||
includes |
X |
X |
X |
X |
excludes |
X |
X |
X |
X |
options_excludes |
X |
Case Recorder Files#
When you instantiate a recorder, you must specify the filename to which the SQLite database of data is stored. By default, this file will be stored under the output directory of the problem.
import os
import openmdao.api as om
p = om.Problem(reports=None)
p.model.add_subsystem('exec', om.ExecComp('z = x + y'),
promotes_inputs=['x', 'y'], promotes_outputs=['z'])
p.add_recorder(om.SqliteRecorder('prob_recorder.sql'))
p.setup()
p.set_val('x', 3)
p.set_val('y', 5)
p.run_model()
p.record('first_case')
p.set_val('x', 4)
p.set_val('y', 5)
p.run_model()
p.record('second_case')
p.cleanup()
# Now prob_recorder.sql should exist in the problem's outputs directory.
os.listdir(p.get_outputs_dir())
Model viewer data has already been recorded for Driver.
['.openmdao_out', 'prob_recorder.sql']
If the filename contains path separators (/
), then OpenMDAO will assume that the recorder file path is not to be prepended with the problem’s output directory.
For instance, to place the file in the current working directory rather than the problem outputs directory, one could prepend it with ./
.
import pathlib
p = om.Problem(reports=None)
p.model.add_subsystem('exec', om.ExecComp('z = x + y'),
promotes_inputs=['x', 'y'], promotes_outputs=['z'])
p.add_recorder(om.SqliteRecorder('./prob_recorder.sql'))
p.setup()
p.set_val('x', 3)
p.set_val('y', 5)
p.run_model()
p.record('first_case')
p.set_val('x', 4)
p.set_val('y', 5)
p.run_model()
p.record('second_case')
p.cleanup()
# Now prob_recorder.sql should exist in the current working directory.
print([file.parts[-1] for file in pathlib.Path.cwd().glob('*.sql')])
Model viewer data has already been recorded for Driver.
['prob_recorder.sql']
Specifying a Case Prefix#
It is possible to record data from multiple executions by specifying a prefix that will be used to differentiate the cases. This prefix can be specified when calling run_model or run_driver and will be prepended to the case ID in the recorded case data:
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]))
from openmdao.test_suite.components.sellar_feature import SellarDerivatives
prob = om.Problem(model=SellarDerivatives())
prob.setup()
recorder = om.SqliteRecorder("cases.sql", record_viewer_data=False)
prob.model.add_recorder(recorder)
prob.driver.add_recorder(recorder)
prob.set_solver_print(0)
prob.run_model(case_prefix='Model_Run1')
prob.run_driver(case_prefix='Driver_Run1')
prob.run_model('Model_Run2')
prob.run_driver('Driver_Run2')
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / "cases.sql")
# all cases recorded by the root system
model_cases = cr.list_cases('root', recurse=False)
root |
---|
Model_Run1_rank0:root._solve_nonlinear|0 |
Driver_Run1_rank0:Driver|0|root._solve_nonlinear|0 |
Model_Run2_rank0:root._solve_nonlinear|0 |
Driver_Run2_rank0:Driver|0|root._solve_nonlinear|0 |
# all cases recorded by the driver
driver_cases = cr.list_cases('driver', recurse=False)
driver |
---|
Driver_Run1_rank0:Driver|0 |
Driver_Run2_rank0:Driver|0 |
Note
A recorder can be attached to more than one object. Also, more than one recorder can be attached to an object.
Note
In this example, we have disabled the saving of data needed by the standalone N2 visualizer and debugging tool by setting record_viewer_data
to False
.
Recording Options Include and Exclude Matching#
The includes
and excludes
recording options provide support for Unix shell-style wildcards, which are not
the same as regular expressions. The documentation for the fnmatchcase
function from the Python standard library
documents the wildcards.
Recording Options Precedence#
The precedence of recording options that determines what gets recorded can sometime be a little confusing. Here is an example that might help. The code shows how the record_desvars and includes options interact.
Paraboloid
class definition
class Paraboloid(om.ExplicitComponent):
"""
Evaluates the equation f(x,y) = (x-3)^2 + xy + (y+4)^2 - 3.
"""
def setup(self):
self.add_input('x', val=0.0)
self.add_input('y', val=0.0)
self.add_output('f_xy', val=0.0)
def setup_partials(self):
self.declare_partials('*', '*')
def compute(self, inputs, outputs):
"""
f(x,y) = (x-3)^2 + xy + (y+4)^2 - 3
Optimal solution (minimum): x = 6.6667; y = -7.3333
"""
x = inputs['x']
y = inputs['y']
outputs['f_xy'] = (x-3.0)**2 + x*y + (y+4.0)**2 - 3.0
def compute_partials(self, inputs, partials):
"""
Jacobian for our paraboloid.
"""
x = inputs['x']
y = inputs['y']
partials['f_xy', 'x'] = 2.0*x - 6.0 + y
partials['f_xy', 'y'] = 2.0*y + 8.0 + x
import openmdao.api as om
from openmdao.test_suite.components.paraboloid import Paraboloid
prob = om.Problem()
prob.driver = om.ScipyOptimizeDriver(optimizer='SLSQP', tol=1e-9, disp=False)
model = prob.model
model.add_subsystem('comp', Paraboloid(), promotes=['*'])
model.add_subsystem('con', om.ExecComp('c = x - y'), promotes=['*'])
model.set_input_defaults('x', val=50.0)
model.set_input_defaults('y', val=50.0)
model.add_design_var('x', lower=-50.0, upper=50.0)
model.add_design_var('y', lower=-50.0, upper=50.0)
model.add_objective('f_xy')
model.add_constraint('c', lower=15.0)
case_file = 'cases.sql'
# First case with record_desvars = True and includes = []
recorder = om.SqliteRecorder(case_file)
prob.driver.add_recorder(recorder)
prob.driver.recording_options['record_desvars'] = True
prob.driver.recording_options['record_outputs'] = True
prob.driver.recording_options['includes'] = []
prob.driver.recording_options['excludes'] = []
prob.setup()
prob.run_driver()
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / case_file)
case = cr.get_case(0)
case.outputs
{'x': array([50.]),
'y': array([50.]),
'f_xy': array([7622.]),
'c': array([0.])}
# Second case with record_desvars = False and includes = []
recorder = om.SqliteRecorder(case_file)
prob.driver.add_recorder(recorder)
prob.driver.recording_options['record_desvars'] = False
prob.driver.recording_options['record_outputs'] = True
prob.driver.recording_options['includes'] = []
prob.setup()
prob.run_driver()
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / case_file)
case = cr.get_case(0)
case.outputs
{'f_xy': array([7622.]), 'c': array([0.])}
# Third case with record_desvars = True and includes = ['*']
recorder = om.SqliteRecorder(case_file)
prob.driver.add_recorder(recorder)
prob.driver.recording_options['record_desvars'] = True
prob.driver.recording_options['record_outputs'] = True
prob.driver.recording_options['includes'] = ['*']
prob.setup()
prob.run_driver()
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / case_file)
case = cr.get_case(0)
case.outputs
{'x': array([50.]),
'y': array([50.]),
'f_xy': array([7622.]),
'c': array([0.])}
# Fourth case with record_desvars = False, record_outputs = True, and includes = ['*']
recorder = om.SqliteRecorder(case_file)
prob.driver.add_recorder(recorder)
prob.driver.recording_options['record_desvars'] = False
prob.driver.recording_options['record_outputs'] = True
prob.driver.recording_options['includes'] = ['*']
prob.setup()
prob.run_driver()
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / case_file)
case = cr.get_case(0)
case.outputs
{'x': array([50.]),
'y': array([50.]),
'f_xy': array([7622.]),
'c': array([0.])}