# Saving Data with the Case Recorder¶

In OpenMDAO, you can instantiate recorder objects and attach them to the System, Driver or Solver instance(s) of your choice.

## Instantiating a Recorder¶

Instantiating a recorder is easy. Simply give it a name, choose which type of recorder you want (currently only SqliteRecorder exists), and name the output file that you would like to write to.

my_recorder = SqliteRecorder("filename")


Note

Currently, appending to an existing DB file is not supported; the SQLite recorder will automatically write over an existing file if it carries the same name.

## Setting Recording Options¶

There are many recording options that can be set. This affects the amount of information retained by the recorders. These options are associated with the System, Driver or Solver that is being recorded.

The following examples use the Sellar model and demonstrate setting the recording options on each of these types.

### Recording on System Objects¶

#### System Recording Options¶

Option Default Acceptable Values Acceptable Types Description
excludes [] N/A [‘list’] Patterns for vars to exclude in recording (processed post-includes)
includes [‘*’] N/A [‘list’] Patterns for variables to include in recording
options_excludes [] N/A [‘list’] User-defined metadata to exclude in recording
record_inputs True N/A [‘bool’] Set to True to record inputs at the system level
record_outputs True N/A [‘bool’] Set to True to record outputs at the system level
record_residuals True N/A [‘bool’] Set to True to record residuals at the system level

To record on a System object, simply add the recorder to the System and set the recording options. Note that the ‘excludes’ option takes precedence over the ‘includes’ option, as shown in the example below where we exclude obj_cmp.x and see that it isn’t in the set of recorded inputs.

from openmdao.api import Problem, SqliteRecorder, CaseReader
from openmdao.test_suite.components.sellar import SellarDerivatives

prob = Problem(model=SellarDerivatives())
prob.setup()

recorder = SqliteRecorder("cases.sql")

obj_cmp = prob.model.obj_cmp
obj_cmp.recording_options['includes'] = ['*']
obj_cmp.recording_options['excludes'] = ['obj_cmp.x']

prob.run_model()

NL: NLBGS Converged in 7 iterations
prob.cleanup()

first_system_case = cr.system_cases.get_case(0)
recorded_inputs = first_system_case.inputs.keys
print(set(recorded_inputs))

{'z', 'y1', 'y2'}

### Recording on Driver Objects¶

#### Driver Recording Options¶

Option Default Acceptable Values Acceptable Types Description
excludes [] N/A [‘list’] Patterns for vars to exclude in recording (processed post-includes)
includes [‘*’] N/A [‘list’] Patterns for variables to include in recording
record_constraints True N/A [‘bool’] Set to True to record constraints at the driver level
record_derivatives False N/A [‘bool’] Set to True to record derivatives at the driver level
record_desvars True N/A [‘bool’] Set to True to record design variables at the driver level
record_inputs True N/A [‘bool’] Set to True to record inputs at the driver level
record_n2_data True N/A [‘bool’] Set to True to record metadata required for N^2 viewing
record_objectives True N/A [‘bool’] Set to True to record objectives at the driver level
record_responses False N/A [‘bool’] Set to True to record responses at the driver level

Recording on a Driver is very similar to recording on a System, though it has a few additional recording options. The options ‘record_objectives’, ‘record_constraints’, ‘record_desvars’, and ‘record_responses’ are all still limited by ‘excludes’, but they do take precedence over ‘includes’, as shown below where ‘includes’ is empty but objectives, constraints, and desvars are still recorded.

from openmdao.api import Problem, ScipyOptimizeDriver, SqliteRecorder, CaseReader
from openmdao.test_suite.components.sellar import SellarDerivatives

prob = Problem(model=SellarDerivatives())

model = prob.model
upper=np.array([10.0, 10.0]))

driver = prob.driver = ScipyOptimizeDriver()
driver.recording_options['includes'] = []
driver.recording_options['record_objectives'] = True
driver.recording_options['record_constraints'] = True
driver.recording_options['record_desvars'] = True

recorder = SqliteRecorder("cases.sql")

prob.setup()
prob.run_driver()

NL: NLBGS Converged in 7 iterations
NL: NLBGS Converged in 1 iterations
NL: NLBGS Converged in 8 iterations
NL: NLBGS Converged in 9 iterations
NL: NLBGS Converged in 9 iterations
NL: NLBGS Converged in 7 iterations
NL: NLBGS Converged in 5 iterations
Optimization terminated successfully.    (Exit mode 0)
Current function value: 3.1833939517633247
Iterations: 6
Function evaluations: 6
Optimization Complete
-----------------------------------
prob.cleanup()

first_driver_case = cr.driver_cases.get_case(0)
recorded_objectives = first_driver_case.get_objectives().keys
recorded_constraints = first_driver_case.get_constraints().keys
recorded_desvars = first_driver_case.get_desvars().keys

print(set(recorded_objectives))

{'obj'}
print(set(recorded_constraints))

{'con2', 'con1'}
print(set(recorded_desvars))

{'z', 'x'}

### Recording on Solver Objects¶

#### Solver Recording Options¶

Option Default Acceptable Values Acceptable Types Description
excludes [] N/A [‘list’] Patterns for vars to exclude in recording (processed post-includes)
includes [‘*’] N/A [‘list’] Patterns for variables to include in recording
record_abs_error True N/A [‘bool’] Set to True to record absolute error at the solver level
record_inputs True N/A [‘bool’] Set to True to record inputs at the solver level
record_outputs True N/A [‘bool’] Set to True to record outputs at the solver level
record_rel_error True N/A [‘bool’] Set to True to record relative error at the solver level
record_solver_residuals False N/A [‘bool’] Set to True to record residuals at the solver level

Recording on Solvers is nearly identical to recording on Systems with the additon of options for recording absolute and relative error. Below is a basic example of adding a recorder to a solver object and recording absolute error.

import numpy as np

from openmdao.api import Problem, SqliteRecorder, CaseReader
from openmdao.test_suite.components.sellar import SellarDerivatives

prob = Problem(model=SellarDerivatives())
prob.setup()

recorder = SqliteRecorder("cases.sql")

solver = prob.model.nonlinear_solver
solver.recording_options['record_abs_error'] = True

prob.run_model()

NL: NLBGS Converged in 7 iterations
prob.cleanup()

first_solver_case = cr.solver_cases.get_case(0)

print(first_solver_case.abs_err)

2.2545141061171243

Note

A recorder can be attached to more than one object. Also, more than one recorder can be attached to an object.

### 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:

from openmdao.api import Problem, SqliteRecorder, CaseReader
from openmdao.test_suite.components.sellar import SellarDerivatives

prob = Problem(model=SellarDerivatives())
prob.setup()

recorder = SqliteRecorder("cases.sql")

prob.run_model(case_prefix='Model_Run1')

NL: NLBGS Converged in 7 iterations
prob.run_driver(case_prefix='Driver_Run1')

Metadata has already been recorded for Driver.
NL: NLBGS Converged in 1 iterations
prob.run_model('Model_Run2')

Metadata has already been recorded for Driver.
NL: NLBGS Converged in 0 iterations
prob.run_driver('Driver_Run2')

Metadata has already been recorded for Driver.
NL: NLBGS Converged in 0 iterations
prob.cleanup()


Model_Run1_rank0:root._solve_nonlinear|0
Driver_Run2_rank0:Driver|0|root._solve_nonlinear|0
driver_cases = cr.driver_cases.list_cases()

Driver_Run1_rank0:Driver|0
Driver_Run2_rank0:Driver|0