Case Reader Object

The CaseReader object is provided to read case recordings no matter what case recorder was used. Currently, OpenMDAO only supports the SqliteCaseRecorder case recorder. Therefore, all the examples will make use of this recorder. OpenMDAO will support other case recorders in the future.

CaseReader Constructor

The call signature for the CaseReader constructor is:

SqliteCaseReader.__init__(filename, pre_load=False)[source]

Initialize.

Parameters
filenamestr

The path to the filename containing the recorded data.

pre_loadbool

If True, load all the data into memory during initialization.

Determining What Sources and Variables Were Recorded

The CaseReader object provides methods to determine which objects in the original problem were sources for recording cases and what variables they recorded. Recording sources can be either drivers, problems, components, or solvers.

The list_sources method provides a list of the names of objects that are the sources of recorded data in the file.

BaseCaseReader.list_sources()[source]

List of all the different recording sources for which there is recorded data.

Returns
list
One or more of: ‘problem’, ‘driver’, <system hierarchy location>,

<solver hierarchy location>

The complementary list_source_vars method will provide a list of the input and output variables recorded for a given source.

BaseCaseReader.list_source_vars(source)[source]

List of all inputs and outputs recorded by the specified source.

Parameters
source{‘problem’, ‘driver’, <system hierarchy location>, <solver hierarchy location>}

Identifies the source for which to return information.

Returns
dict

{‘inputs’:[list of keys], ‘outputs’:[list of keys]}. Does not recurse.

Here is an example of their usage.

import openmdao.api as om
from openmdao.test_suite.components.sellar_feature import SellarMDA

import numpy as np

# define Sellar MDA problem
prob = om.Problem(model=SellarMDA())

model = prob.model
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)

prob.driver = om.ScipyOptimizeDriver(optimizer='SLSQP', tol=1e-9, disp=False)

# add recorder to the driver, model and solver
recorder = om.SqliteRecorder('cases.sql')

prob.driver.add_recorder(recorder)
model.add_recorder(recorder)
model.nonlinear_solver.add_recorder(recorder)

# run the problem
prob.setup()
prob.set_solver_print(0)
prob.run_driver()
prob.cleanup()

# examine cases to see what was recorded
cr = om.CaseReader('cases.sql')

print(sorted(cr.list_sources(out_stream=None)))
['driver', 'root', 'root.nonlinear_solver']
driver_vars = cr.list_source_vars('driver', out_stream=None)
print(('inputs:', sorted(driver_vars['inputs']), 'outputs:', sorted(driver_vars['outputs'])))
('inputs:', [], 'outputs:', ['con1', 'con2', 'obj', 'x', 'z'])
model_vars = cr.list_source_vars('root', out_stream=None)
print(('inputs:', sorted(model_vars['inputs']), 'outputs:', sorted(model_vars['outputs'])))
('inputs:', ['x', 'y1', 'y2', 'z'], 'outputs:', ['con1', 'con2', 'obj', 'x', 'y1', 'y2', 'z'])
solver_vars = cr.list_source_vars('root.nonlinear_solver', out_stream=None)
print(('inputs:', sorted(solver_vars['inputs']), 'outputs:', sorted(solver_vars['outputs'])))
('inputs:', ['x', 'y1', 'y2', 'z'], 'outputs:', ['con1', 'con2', 'obj', 'x', 'y1', 'y2', 'z'])

Case Names

The CaseReader provides access to Case objects, each of which encapsulates a data point recorded by one of the sources.

Case objects are uniquely identified in a case recorder file by their case names. A case name is a string. As an example, here is a case name:

‘rank0:ScipyOptimize_SLSQP|1|root._solve_nonlinear|1’

The first part of the case name indicates which rank or process that the case was recorded from. The remainder of the case name shows the hierarchical path to the object that was recorded along with the iteration counts for each object along the path. It follows a pattern of repeated pairs of

  • object name ( problem, driver, system, or solver )

  • iteration count

These are separated by the | character.

So in the given example, the case is:

  • from rank 0

  • the first iteration of the driver, ScipyOptimize_SLSQP

  • the first execution of the root system which is the top-level model

Getting Names of the Cases

The list_cases method returns the names of the cases in the order in which the cases were executed. You can optionally request cases only from a specific source.

BaseCaseReader.list_cases(source=None, recurse=True, flat=True)[source]

Iterate over Driver, Solver and System cases in order.

Parameters
source{‘problem’, ‘driver’, <system hierarchy location>, <solver hierarchy location>,

case name} If not None, only cases originating from the specified source or case are returned.

recursebool, optional

If True, will enable iterating over all successors in case hierarchy.

flatbool, optional

If False and there are child cases, then a nested ordered dictionary is returned rather than an iterator.

Returns
iterator or dict

An iterator or a nested dictionary of identified cases.

There are two optional arguments to the list_cases method that affect what is returned.

  • recurse: causes the returned value to include child cases.

  • flat: works in conjunction with the recurse argument to determine if the returned results are in the form of a list or nested dict. If recurse=True, flat=False, and there are child cases, then the returned value is a nested ordered dict. Otherwise, it is a list.

Getting Access to Cases

Getting information from the cases is a two-step process. First, you need to get access to the Case object and then you can call a variety of methods on the Case object to get values from it. The second step is described on the Accessing Recorded Data page.

There are two methods used to get access to Cases:

  • get_cases

  • get_case

Getting Access to Cases Using get_cases Method

The get_cases method provides a quick and easy way to iterate over all the cases.

BaseCaseReader.get_cases(source, recurse=True, flat=False)[source]

Iterate over the cases.

Parameters
source{‘problem’, ‘driver’, <system hierarchy location>, <solver hierarchy location>,

case name} Identifies which cases to return.

recursebool, optional

If True, will enable iterating over all successors in case hierarchy

flatbool, optional

If False and there are child cases, then a nested ordered dictionary is returned rather than an iterator.

Returns
list or dict

The cases identified by source

The method get_cases is similar to the list_cases method in that it has the two optional arguments recurse and flat to control what is returned and the data structure returned. See explanation of the list_cases args.

Here is an example of its usage.

import openmdao.api as om
from openmdao.test_suite.components.sellar_feature import SellarMDA

import numpy as np

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

model = prob.model
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 = prob.driver = om.ScipyOptimizeDriver(optimizer='SLSQP', tol=1e-9)
driver.add_recorder(om.SqliteRecorder('cases.sql'))

prob.setup()
prob.set_solver_print(0)
prob.run_driver()
prob.cleanup()

cr = om.CaseReader('cases.sql')

cases = cr.get_cases()

print(len(cases))

for case in cases:
    print(case)
Optimization terminated successfully.    (Exit mode 0)
            Current function value: 3.183393951806944
            Iterations: 12
            Function evaluations: 13
            Gradient evaluations: 12
Optimization Complete
-----------------------------------
14
driver rank0:ScipyOptimize_SLSQP|0 {'con2': array([-11.94151185]), 'x': array([1.]), 'con1': array([-22.42830237]), 'obj': array([28.58830817]), 'z': array([5., 2.])}
driver rank0:ScipyOptimize_SLSQP|1 {'con2': array([-11.94151185]), 'x': array([1.]), 'con1': array([-22.42830237]), 'obj': array([28.58830817]), 'z': array([5., 2.])}
driver rank0:ScipyOptimize_SLSQP|2 {'con2': array([-17.33862636]), 'x': array([0.]), 'con1': array([-5.17034752]), 'obj': array([9.12937206]), 'z': array([2.97739435, 0.79774515])}
driver rank0:ScipyOptimize_SLSQP|3 {'con2': array([-19.71377581]), 'x': array([1.24381997e-14]), 'con1': array([-1.01430704]), 'obj': array([4.18806381]), 'z': array([2.24311210e+00, 4.48974191e-13])}
driver rank0:ScipyOptimize_SLSQP|4 {'con2': array([-20.16619715]), 'x': array([0.0014523]), 'con1': array([-0.14181909]), 'obj': array([3.32344841]), 'z': array([2.01671202e+00, 6.16526663e-13])}
driver rank0:ScipyOptimize_SLSQP|5 {'con2': array([-20.2361779]), 'x': array([0.]), 'con1': array([-0.015207]), 'obj': array([3.19840191]), 'z': array([1.98191105, 0.        ])}
driver rank0:ScipyOptimize_SLSQP|6 {'con2': array([-20.24385074]), 'x': array([0.]), 'con1': array([-0.00154939]), 'obj': array([3.18492296]), 'z': array([1.97807463e+00, 1.56769835e-15])}
driver rank0:ScipyOptimize_SLSQP|7 {'con2': array([-20.24463403]), 'x': array([0.]), 'con1': array([-0.00015679]), 'obj': array([3.18354868]), 'z': array([1.97768298e+00, 1.27517757e-14])}
driver rank0:ScipyOptimize_SLSQP|8 {'con2': array([-20.24471331]), 'x': array([3.24738796e-15]), 'con1': array([-1.58610711e-05]), 'obj': array([3.1834096]), 'z': array([1.97764334e+00, 1.02233532e-14])}
driver rank0:ScipyOptimize_SLSQP|9 {'con2': array([-20.24472133]), 'x': array([0.]), 'con1': array([-1.60409322e-06]), 'obj': array([3.18339553]), 'z': array([1.97763933, 0.        ])}
driver rank0:ScipyOptimize_SLSQP|10 {'con2': array([-20.24472214]), 'x': array([0.]), 'con1': array([-1.63976894e-07]), 'obj': array([3.18339411]), 'z': array([1.97763893, 0.        ])}
driver rank0:ScipyOptimize_SLSQP|11 {'con2': array([-20.24472222]), 'x': array([8.82006605e-16]), 'con1': array([-1.65888538e-08]), 'obj': array([3.18339397]), 'z': array([1.97763889e+00, 2.76939916e-14])}
driver rank0:ScipyOptimize_SLSQP|12 {'con2': array([-20.24472223]), 'x': array([2.40547256e-15]), 'con1': array([-1.67487757e-09]), 'obj': array([3.18339395]), 'z': array([1.97763888e+00, 3.20921654e-14])}
driver rank0:ScipyOptimize_SLSQP|13 {'con2': array([-20.24472223]), 'x': array([0.]), 'con1': array([-1.68550507e-10]), 'obj': array([3.18339395]), 'z': array([1.97763888e+00, 1.25035459e-15])}

Getting Access to the Case Values Using get_case Method

The get_case method returns a Case object given a case name.

BaseCaseReader.get_case(case_id, recurse=True)[source]

Get case identified by case_id.

Parameters
case_idstr or int

The unique identifier of the case to return or an index into all cases.

recursebool, optional

If True, will return all successors to the case as well.

Returns
dict

The case identified by case_id

You can use the get_case method to get a specific case from the list of case names returned by list_cases.

This code snippet shows how to get the first case.

cr = om.CaseReader('cases.sql')
case_names = cr.list_cases()
case = cr.get_case(case_names[0])

You could also use the feature of get_case where you provide an index into all the cases. This snippet shows how to get the first case using an index.

cr = om.CaseReader('cases.sql')
case = cr.get_case(0)

Finally, looping over all the case names and getting access to the cases is shown in this example.

import numpy as np
import openmdao.api as om
from openmdao.test_suite.components.sellar_feature import SellarMDA

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

model = prob.model
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 = prob.driver = om.ScipyOptimizeDriver(optimizer='SLSQP', tol=1e-9)

driver.add_recorder(om.SqliteRecorder('cases.sql'))

prob.setup()
prob.set_solver_print(0)
prob.run_driver()
prob.cleanup()

cr = om.CaseReader('cases.sql')

case_names = cr.list_cases(out_stream=None)

print(len(case_names))
print(case_names)
print('')

for name in case_names:
    case = cr.get_case(name)
    print(case)
Optimization terminated successfully.    (Exit mode 0)
            Current function value: 3.183393951806944
            Iterations: 12
            Function evaluations: 13
            Gradient evaluations: 12
Optimization Complete
-----------------------------------
14
['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', 'rank0:ScipyOptimize_SLSQP|7', 'rank0:ScipyOptimize_SLSQP|8', 'rank0:ScipyOptimize_SLSQP|9', 'rank0:ScipyOptimize_SLSQP|10', 'rank0:ScipyOptimize_SLSQP|11', 'rank0:ScipyOptimize_SLSQP|12', 'rank0:ScipyOptimize_SLSQP|13']

driver rank0:ScipyOptimize_SLSQP|0 {'con2': array([-11.94151185]), 'x': array([1.]), 'con1': array([-22.42830237]), 'obj': array([28.58830817]), 'z': array([5., 2.])}
driver rank0:ScipyOptimize_SLSQP|1 {'con2': array([-11.94151185]), 'x': array([1.]), 'con1': array([-22.42830237]), 'obj': array([28.58830817]), 'z': array([5., 2.])}
driver rank0:ScipyOptimize_SLSQP|2 {'con2': array([-17.33862636]), 'x': array([0.]), 'con1': array([-5.17034752]), 'obj': array([9.12937206]), 'z': array([2.97739435, 0.79774515])}
driver rank0:ScipyOptimize_SLSQP|3 {'con2': array([-19.71377581]), 'x': array([1.24381997e-14]), 'con1': array([-1.01430704]), 'obj': array([4.18806381]), 'z': array([2.24311210e+00, 4.48974191e-13])}
driver rank0:ScipyOptimize_SLSQP|4 {'con2': array([-20.16619715]), 'x': array([0.0014523]), 'con1': array([-0.14181909]), 'obj': array([3.32344841]), 'z': array([2.01671202e+00, 6.16526663e-13])}
driver rank0:ScipyOptimize_SLSQP|5 {'con2': array([-20.2361779]), 'x': array([0.]), 'con1': array([-0.015207]), 'obj': array([3.19840191]), 'z': array([1.98191105, 0.        ])}
driver rank0:ScipyOptimize_SLSQP|6 {'con2': array([-20.24385074]), 'x': array([0.]), 'con1': array([-0.00154939]), 'obj': array([3.18492296]), 'z': array([1.97807463e+00, 1.56769835e-15])}
driver rank0:ScipyOptimize_SLSQP|7 {'con2': array([-20.24463403]), 'x': array([0.]), 'con1': array([-0.00015679]), 'obj': array([3.18354868]), 'z': array([1.97768298e+00, 1.27517757e-14])}
driver rank0:ScipyOptimize_SLSQP|8 {'con2': array([-20.24471331]), 'x': array([3.24738796e-15]), 'con1': array([-1.58610711e-05]), 'obj': array([3.1834096]), 'z': array([1.97764334e+00, 1.02233532e-14])}
driver rank0:ScipyOptimize_SLSQP|9 {'con2': array([-20.24472133]), 'x': array([0.]), 'con1': array([-1.60409322e-06]), 'obj': array([3.18339553]), 'z': array([1.97763933, 0.        ])}
driver rank0:ScipyOptimize_SLSQP|10 {'con2': array([-20.24472214]), 'x': array([0.]), 'con1': array([-1.63976894e-07]), 'obj': array([3.18339411]), 'z': array([1.97763893, 0.        ])}
driver rank0:ScipyOptimize_SLSQP|11 {'con2': array([-20.24472222]), 'x': array([8.82006605e-16]), 'con1': array([-1.65888538e-08]), 'obj': array([3.18339397]), 'z': array([1.97763889e+00, 2.76939916e-14])}
driver rank0:ScipyOptimize_SLSQP|12 {'con2': array([-20.24472223]), 'x': array([2.40547256e-15]), 'con1': array([-1.67487757e-09]), 'obj': array([3.18339395]), 'z': array([1.97763888e+00, 3.20921654e-14])}
driver rank0:ScipyOptimize_SLSQP|13 {'con2': array([-20.24472223]), 'x': array([0.]), 'con1': array([-1.68550507e-10]), 'obj': array([3.18339395]), 'z': array([1.97763888e+00, 1.25035459e-15])}

Processing a Nested Dictionary of Its Child Cases

The following example demonstrates selecting a case from a case list and processing a nested dictionary of its child cases.

import openmdao.api as om
from openmdao.test_suite.components.sellar_feature import SellarMDA

import numpy as np

# define Sellar MDA problem
prob = om.Problem(model=SellarMDA())

model = prob.model
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)

prob.driver = om.ScipyOptimizeDriver(optimizer='SLSQP', tol=1e-9, disp=False)

# add recorder to the driver, model and solver
recorder = om.SqliteRecorder('cases.sql')

prob.driver.add_recorder(recorder)
model.add_recorder(recorder)
model.nonlinear_solver.add_recorder(recorder)

# run the problem
prob.setup()
prob.set_solver_print(0)
prob.run_driver()
prob.cleanup()

# get the last driver case
cr = om.CaseReader('cases.sql')

driver_cases = cr.list_cases('driver')
last_driver_case = driver_cases[-1]

# get a recursive dict of all child cases of the last driver case
cases = cr.get_cases(last_driver_case, recurse=True, flat=False)

# access the last driver case and it's children
for case in cases:
    print(case)
    for child_case in cases[case]:
        print(child_case)
        for grandchild in cases[case][child_case]:
            print(grandchild)
solver
    rank0:ScipyOptimize_SLSQP|0|root._solve_nonlinear|0|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|0|root._solve_nonlinear|0
driver
    rank0:ScipyOptimize_SLSQP|0
solver
    rank0:ScipyOptimize_SLSQP|1|root._solve_nonlinear|1|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|1|root._solve_nonlinear|1
driver
    rank0:ScipyOptimize_SLSQP|1
solver
    rank0:ScipyOptimize_SLSQP|2|root._solve_nonlinear|2|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|2|root._solve_nonlinear|2
driver
    rank0:ScipyOptimize_SLSQP|2
solver
    rank0:ScipyOptimize_SLSQP|3|root._solve_nonlinear|3|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|3|root._solve_nonlinear|3
driver
    rank0:ScipyOptimize_SLSQP|3
solver
    rank0:ScipyOptimize_SLSQP|4|root._solve_nonlinear|4|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|4|root._solve_nonlinear|4
driver
    rank0:ScipyOptimize_SLSQP|4
solver
    rank0:ScipyOptimize_SLSQP|5|root._solve_nonlinear|5|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|5|root._solve_nonlinear|5
driver
    rank0:ScipyOptimize_SLSQP|5
solver
    rank0:ScipyOptimize_SLSQP|6|root._solve_nonlinear|6|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|6|root._solve_nonlinear|6
driver
    rank0:ScipyOptimize_SLSQP|6
solver
    rank0:ScipyOptimize_SLSQP|7|root._solve_nonlinear|7|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|7|root._solve_nonlinear|7
driver
    rank0:ScipyOptimize_SLSQP|7
solver
    rank0:ScipyOptimize_SLSQP|8|root._solve_nonlinear|8|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|8|root._solve_nonlinear|8
driver
    rank0:ScipyOptimize_SLSQP|8
solver
    rank0:ScipyOptimize_SLSQP|9|root._solve_nonlinear|9|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|9|root._solve_nonlinear|9
driver
    rank0:ScipyOptimize_SLSQP|9
solver
    rank0:ScipyOptimize_SLSQP|10|root._solve_nonlinear|10|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|10|root._solve_nonlinear|10
driver
    rank0:ScipyOptimize_SLSQP|10
solver
    rank0:ScipyOptimize_SLSQP|11|root._solve_nonlinear|11|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|11|root._solve_nonlinear|11
driver
    rank0:ScipyOptimize_SLSQP|11
solver
    rank0:ScipyOptimize_SLSQP|12|root._solve_nonlinear|12|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|12|root._solve_nonlinear|12
driver
    rank0:ScipyOptimize_SLSQP|12
solver
    rank0:ScipyOptimize_SLSQP|13|root._solve_nonlinear|13|NLRunOnce|0
system
    rank0:ScipyOptimize_SLSQP|13|root._solve_nonlinear|13
driver
    rank0:ScipyOptimize_SLSQP|13
driver rank0:ScipyOptimize_SLSQP|13 {'con2': array([-20.24472223]), 'x': array([0.]), 'con1': array([-1.68550507e-10]), 'obj': array([3.18339395]), 'z': array([1.97763888e+00, 1.25035459e-15])}
root rank0:ScipyOptimize_SLSQP|13|root._solve_nonlinear|13 {'z': array([1.97763888e+00, 1.25035459e-15]), 'x': array([0.]), 'con1': array([-1.68550507e-10]), 'con2': array([-20.24472223]), 'y1': array([3.16]), 'y2': array([3.75527777]), 'obj': array([3.18339395])}
root.nonlinear_solver rank0:ScipyOptimize_SLSQP|13|root._solve_nonlinear|13|NLRunOnce|0 {'z': array([1.97763888e+00, 1.25035459e-15]), 'x': array([0.]), 'y1': array([3.16]), 'y2': array([3.75527777]), 'obj': array([3.18339395]), 'con1': array([-1.68550507e-10]), 'con2': array([-20.24472223])}