Accessing Recorded Metadata

In addition to the cases themselves, a CaseReader may also record certain metadata about the model and its constituent systems and solvers.

Problem Metadata

By default, a case recorder will save metadata about the model to assist in later visualization and debugging. This information is made available via the problem_metadata attribute of a CaseReader.

import openmdao.api as om
from openmdao.test_suite.components.sellar import SellarDerivatives

prob = om.Problem(SellarDerivatives())

recorder = om.SqliteRecorder("cases.sql")
prob.driver.add_recorder(recorder)

prob.setup()
prob.run_driver()
prob.cleanup()

cr = om.CaseReader("cases.sql")
NL: NLBGS Converged in 8 iterations
# access list of connections stored in metadata
cr.problem_metadata['connections_list']
[{'src': '_auto_ivc.v0', 'tgt': 'd1.z'},
 {'src': '_auto_ivc.v1', 'tgt': 'd1.x'},
 {'src': '_auto_ivc.v0', 'tgt': 'd2.z'},
 {'src': '_auto_ivc.v1', 'tgt': 'obj_cmp.x'},
 {'src': '_auto_ivc.v0', 'tgt': 'obj_cmp.z'},
 {'src': 'd1.y1', 'tgt': 'd2.y1'},
 {'src': 'd1.y1', 'tgt': 'obj_cmp.y1'},
 {'src': 'd1.y1', 'tgt': 'con_cmp1.y1'},
 {'src': 'd2.y2', 'tgt': 'd1.y2'},
 {'src': 'd2.y2', 'tgt': 'obj_cmp.y2'},
 {'src': 'd2.y2', 'tgt': 'con_cmp2.y2'}]
# access the model tree stored in metadata
cr.problem_metadata['tree']
{'name': 'root',
 'type': 'root',
 'class': 'SellarDerivatives',
 'expressions': None,
 'nonlinear_solver': 'NL: NLBGS',
 'nonlinear_solver_options': {'maxiter': 10,
  'atol': 1e-10,
  'rtol': 1e-10,
  'iprint': 1,
  'err_on_non_converge': False,
  'debug_print': False,
  'stall_limit': 0,
  'stall_tol': 1e-12,
  'use_aitken': False,
  'aitken_min_factor': 0.1,
  'aitken_max_factor': 1.5,
  'aitken_initial_factor': 1.0,
  'cs_reconverge': True,
  'use_apply_nonlinear': False,
  'reraise_child_analysiserror': False},
 'linear_solver': 'LN: SCIPY',
 'linear_solver_options': {'maxiter': 1000,
  'atol': 1e-12,
  'rtol': 1e-10,
  'iprint': 1,
  'err_on_non_converge': False,
  'assemble_jac': False,
  'solver': 'gmres',
  'restart': 20},
 'component_type': None,
 'subsystem_type': 'group',
 'is_parallel': False,
 'children': [{'name': '_auto_ivc',
   'type': 'subsystem',
   'class': '_AutoIndepVarComp',
   'expressions': None,
   'nonlinear_solver': '',
   'nonlinear_solver_options': None,
   'linear_solver': '',
   'linear_solver_options': None,
   'subsystem_type': 'component',
   'is_parallel': False,
   'component_type': 'indep',
   'children': [{'name': 'v0',
     'type': 'output',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(2,)',
     'implicit': False,
     'units': 'None',
     'val': [5.0, 2.0]},
    {'name': 'v1',
     'type': 'output',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'implicit': False,
     'units': 'None',
     'val': [1.0]}],
   'options': {'distributed': False,
    'run_root_only': False,
    'name': 'UNDEFINED',
    'val': 1.0,
    'shape': None,
    'units': None,
    'res_units': None,
    'desc': None,
    'lower': None,
    'upper': None,
    'ref': 1.0,
    'ref0': 0.0,
    'res_ref': None,
    'tags': None}},
  {'name': 'd1',
   'type': 'subsystem',
   'class': 'SellarDis1withDerivatives',
   'expressions': None,
   'nonlinear_solver': '',
   'nonlinear_solver_options': None,
   'linear_solver': '',
   'linear_solver_options': None,
   'subsystem_type': 'component',
   'is_parallel': False,
   'component_type': 'explicit',
   'children': [{'name': 'z',
     'type': 'input',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(2,)',
     'units': 'None',
     'val': [5.0, 2.0]},
    {'name': 'x',
     'type': 'input',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'units': 'None',
     'val': [1.0]},
    {'name': 'y2',
     'type': 'input',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'units': 'None',
     'val': [1.0]},
    {'name': 'y1',
     'type': 'output',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'implicit': False,
     'units': 'None',
     'val': [1.0]}],
   'options': {'distributed': False, 'run_root_only': False}},
  {'name': 'd2',
   'type': 'subsystem',
   'class': 'SellarDis2withDerivatives',
   'expressions': None,
   'nonlinear_solver': '',
   'nonlinear_solver_options': None,
   'linear_solver': '',
   'linear_solver_options': None,
   'subsystem_type': 'component',
   'is_parallel': False,
   'component_type': 'explicit',
   'children': [{'name': 'z',
     'type': 'input',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(2,)',
     'units': 'None',
     'val': [5.0, 2.0]},
    {'name': 'y1',
     'type': 'input',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'units': 'None',
     'val': [1.0]},
    {'name': 'y2',
     'type': 'output',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'implicit': False,
     'units': 'None',
     'val': [1.0]}],
   'options': {'distributed': False, 'run_root_only': False}},
  {'name': 'obj_cmp',
   'type': 'subsystem',
   'class': 'ExecComp',
   'expressions': ['obj = x**2 + z[1] + y1 + exp(-y2)'],
   'nonlinear_solver': '',
   'nonlinear_solver_options': None,
   'linear_solver': '',
   'linear_solver_options': None,
   'subsystem_type': 'component',
   'is_parallel': False,
   'component_type': 'exec',
   'children': [{'name': 'x',
     'type': 'input',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'units': 'None',
     'val': [1.0]},
    {'name': 'y1',
     'type': 'input',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'units': 'None',
     'val': [1.0]},
    {'name': 'y2',
     'type': 'input',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'units': 'None',
     'val': [1.0]},
    {'name': 'z',
     'type': 'input',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(2,)',
     'units': 'None',
     'val': [5.0, 2.0]},
    {'name': 'obj',
     'type': 'output',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'implicit': False,
     'units': 'None',
     'val': [0.0]}],
   'options': {'distributed': False,
    'run_root_only': False,
    'has_diag_partials': False,
    'units': None,
    'shape': None,
    'shape_by_conn': False}},
  {'name': 'con_cmp1',
   'type': 'subsystem',
   'class': 'ExecComp',
   'expressions': ['con1 = 3.16 - y1'],
   'nonlinear_solver': '',
   'nonlinear_solver_options': None,
   'linear_solver': '',
   'linear_solver_options': None,
   'subsystem_type': 'component',
   'is_parallel': False,
   'component_type': 'exec',
   'children': [{'name': 'y1',
     'type': 'input',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'units': 'None',
     'val': [1.0]},
    {'name': 'con1',
     'type': 'output',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'implicit': False,
     'units': 'None',
     'val': [0.0]}],
   'options': {'distributed': False,
    'run_root_only': False,
    'has_diag_partials': False,
    'units': None,
    'shape': None,
    'shape_by_conn': False}},
  {'name': 'con_cmp2',
   'type': 'subsystem',
   'class': 'ExecComp',
   'expressions': ['con2 = y2 - 24.0'],
   'nonlinear_solver': '',
   'nonlinear_solver_options': None,
   'linear_solver': '',
   'linear_solver_options': None,
   'subsystem_type': 'component',
   'is_parallel': False,
   'component_type': 'exec',
   'children': [{'name': 'y2',
     'type': 'input',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'units': 'None',
     'val': [1.0]},
    {'name': 'con2',
     'type': 'output',
     'dtype': 'ndarray',
     'is_discrete': False,
     'distributed': False,
     'shape': '(1,)',
     'implicit': False,
     'units': 'None',
     'val': [0.0]}],
   'options': {'distributed': False,
    'run_root_only': False,
    'has_diag_partials': False,
    'units': None,
    'shape': None,
    'shape_by_conn': False}}],
 'options': {'assembled_jac_type': 'csc',
  'nonlinear_solver': 'NL: NLBGS',
  'nl_atol': None,
  'nl_maxiter': None,
  'linear_solver': 'LN: SCIPY',
  'ln_atol': None,
  'ln_maxiter': None}}

System Options

All case recorders record the component options and scaling factors for all systems in the model.

These values are accessible using the list_model_options function of a case reader object. This function displays and returns a dictionary of the option values for each system in the model.

If the model has been run multiple times, you can specify the run for which to get/display options.

import openmdao.api as om
from openmdao.test_suite.components.sellar import SellarDerivativesGrouped

prob = om.Problem(model=SellarDerivativesGrouped())
prob.add_recorder(om.SqliteRecorder("cases.sql"))

# set option and run model
prob.model.options['nl_maxiter'] = 1
prob.setup()
prob.run_model()

# change option and run again
prob.model.options['nl_maxiter'] = 9
prob.setup()
prob.run_model()

# clean up after runs and open a case reader
prob.cleanup()
cr = om.CaseReader("cases.sql")
|  
|  ===
|  mda
|  ===
|  NL: NLBGS Converged in 8 iterations
NL: NLBGSSolver 'NL: NLBGS' on system '' failed to converge in 1 iterations.
Model viewer data has already has already been recorded for Driver.
|  
|  ===
|  mda
|  ===
|  NL: NLBGS Converged in 8 iterations
|  
|  ===
|  mda
|  ===
|  NL: NLBGS Converged in 1 iterations
NL: NLBGS Converged in 2 iterations
/usr/share/miniconda/envs/test/lib/python3.8/site-packages/openmdao/recorders/sqlite_recorder.py:223: UserWarning:The existing case recorder file, cases.sql, is being overwritten.
# get/display options for initial run
options = cr.list_model_options()
Run Number: 0
    Subsystem : root
        assembled_jac_type: csc
        nl_atol: None
        nl_maxiter: 1
        ln_atol: None
        ln_maxiter: None
    Subsystem : _auto_ivc
        distributed: False
        run_root_only: False
        name: UNDEFINED
        val: 1.0
        shape: None
        units: None
        res_units: None
        desc: None
        lower: None
        upper: None
        ref: 1.0
        ref0: 0.0
        res_ref: None
        tags: None
    Subsystem : mda
        assembled_jac_type: csc
    Subsystem : mda.d1
        distributed: False
        run_root_only: False
    Subsystem : mda.d2
        distributed: False
        run_root_only: False
    Subsystem : obj_cmp
        distributed: False
        run_root_only: False
        has_diag_partials: False
        units: None
        shape: None
        shape_by_conn: False
    Subsystem : con_cmp1
        distributed: False
        run_root_only: False
        has_diag_partials: False
        units: None
        shape: None
        shape_by_conn: False
    Subsystem : con_cmp2
        distributed: False
        run_root_only: False
        has_diag_partials: False
        units: None
        shape: None
        shape_by_conn: False
# check nl_maxiter option for the second run
options = cr.list_model_options(run_number=1, out_stream=None)
options['root']['nl_maxiter']
9

Solver Options

All case recorders record the solver options for all solvers in the model.

These values are accessible using the list_solver_options function of a case reader object.

This function displays and returns a dictionary of the option values for each solver in the model. If the model has been run multiple times, you can specify the run for which to get/display options.

import openmdao.api as om
from openmdao.test_suite.components.sellar import SellarDerivativesGrouped

# configure a Newton solver with linesearch for the Sellar MDA Group
newton = om.NewtonSolver(solve_subsystems=True, max_sub_solves=4)
newton.linesearch = om.BoundsEnforceLS()

model = SellarDerivativesGrouped(mda_nonlinear_solver=newton)

prob = om.Problem(model)
prob.add_recorder(om.SqliteRecorder("cases.sql"))
prob.setup()

# initial run
newton.linesearch.options['bound_enforcement'] = 'vector'
prob.run_model()

# change linesearch and run again
newton.linesearch.options['bound_enforcement'] = 'wall'
prob.run_model()

# clean up after runs and open a case reader
prob.cleanup()
cr = om.CaseReader("cases.sql")
|  
|  ===
|  mda
|  ===
|  NL: Newton Converged in 2 iterations
|  
|  ===
|  mda
|  ===
|  NL: Newton Converged in 0 iterations
NL: NLBGS Converged in 2 iterations
Model viewer data has already has already been recorded for Driver.
|  
|  ===
|  mda
|  ===
|  NL: Newton Converged in 0 iterations
NL: NLBGS Converged in 1 iterations
# get/display options for initial run
options = cr.list_solver_options()
Run Number: 0
    Solver: root.NonlinearBlockGS
        maxiter: 10
        atol: 1e-10
        rtol: 1e-10
        iprint: 1
        err_on_non_converge: False
        debug_print: False
        stall_limit: 0
        stall_tol: 1e-12
        use_aitken: False
        aitken_min_factor: 0.1
        aitken_max_factor: 1.5
        aitken_initial_factor: 1.0
        cs_reconverge: True
        use_apply_nonlinear: False
        reraise_child_analysiserror: False
    Solver: root.ScipyKrylov
        maxiter: 1000
        atol: 1e-12
        rtol: 1e-10
        iprint: 1
        err_on_non_converge: False
        assemble_jac: False
        solver: gmres
        restart: 20
    Solver: mda.NewtonSolver
        maxiter: 10
        atol: 1e-10
        rtol: 1e-10
        iprint: 1
        err_on_non_converge: False
        debug_print: False
        stall_limit: 0
        stall_tol: 1e-12
        solve_subsystems: True
        max_sub_solves: 4
        cs_reconverge: True
        reraise_child_analysiserror: False
    Solver: mda.BoundsEnforceLS
        iprint: 1
        debug_print: False
        stall_limit: 0
        stall_tol: 1e-12
        bound_enforcement: vector
        print_bound_enforce: False
    Solver: mda.ScipyKrylov
        maxiter: 1000
        atol: 1e-12
        rtol: 1e-10
        iprint: 1
        err_on_non_converge: False
        assemble_jac: False
        solver: gmres
        restart: 20
print(sorted(options.keys()))
['mda.BoundsEnforceLS', 'mda.NewtonSolver', 'mda.ScipyKrylov', 'root.NonlinearBlockGS', 'root.ScipyKrylov']
print(options['root.NonlinearBlockGS']['maxiter'])
10
print(options['root.ScipyKrylov']['maxiter'])
1000
print(options['mda.NewtonSolver']['maxiter'])
10
print(options['mda.NewtonSolver']['solve_subsystems'])
True
print(options['mda.NewtonSolver']['max_sub_solves'])
4
print(options['mda.BoundsEnforceLS']['bound_enforcement'])
vector
# get options for second run
options = cr.list_solver_options(run_number=1, out_stream=None)
print(options['mda.BoundsEnforceLS']['bound_enforcement'])
wall