Getting Data from a Case#

The Case object contains all the information about a specific recorded case whether it was recorded by a problem, driver, system, or solver. Case objects have a number methods for accessing variables and their values.

Example of Getting Variable Data from Case Recording of a Driver#

Here are the methods typically used when retrieving data from the recording of a :code:Driver.

Case.get_objectives(scaled=True, use_indices=True)[source]

Get the values of the objectives, as seen by the driver, for this case.

Parameters:
scaledbool

If True, then return scaled values.

use_indicesbool

If True, apply indices.

Returns:
PromAbsDict

Map of variables to their values.

Case.get_constraints(scaled=True, use_indices=True)[source]

Get the values of the constraints, as seen by the driver, for this case.

Parameters:
scaledbool

If True, then return scaled values.

use_indicesbool

If True, apply indices.

Returns:
PromAbsDict

Map of variables to their values.

Case.get_design_vars(scaled=True, use_indices=True)[source]

Get the values of the design variables, as seen by the driver, for this case.

Parameters:
scaledbool

If True, then return scaled values.

use_indicesbool

If True, apply indices.

Returns:
PromAbsDict

Map of variables to their values.

Case.get_responses(scaled=True, use_indices=True)[source]

Get the values of the responses, as seen by the driver, for this case.

Parameters:
scaledbool

If True, then return scaled values.

use_indicesbool

If True, apply indices.

Returns:
PromAbsDict

Map of variables to their values.

The following example shows how to use these methods to easily check the variables of interest from the first driver iteration.

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

import numpy as np

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

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)

model.nonlinear_solver = om.NonlinearBlockGS()
model.linear_solver = om.ScipyKrylov()

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

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

driver.recording_options['includes'] = []
driver.recording_options['record_objectives'] = True
driver.recording_options['record_constraints'] = True
driver.recording_options['record_desvars'] = True

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

cr = om.CaseReader("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
case = cr.get_case(driver_cases[0])
case.outputs.keys()
dict_keys(['z', 'x', 'con1', 'con2', 'obj'])
objs = case.get_objectives()
cons = case.get_constraints()
dvs = case.get_design_vars()
rsps = case.get_responses()

Using keys() will give you the promoted variable names:

print((sorted(objs.keys()), sorted(cons.keys()), sorted(dvs.keys())))
(['obj'], ['con1', 'con2'], ['x', 'z'])

Alternatively, you can get the absolute names:

print((sorted(objs.absolute_names()), sorted(cons.absolute_names()), sorted(dvs.absolute_names())))
(['obj_cmp.obj'], ['con_cmp1.con1', 'con_cmp2.con2'], ['x', 'z'])

You can access variable values using either the promoted or the absolute name:

print('objective (obj):\t', objs['obj'], objs['obj_cmp.obj'])
print('constraint (con1):\t', cons['con1'], cons['con_cmp1.con1'])
# Note that x is supplied by an automatically generated IndepVarComp
print('design vars (x):\t', dvs['x'], dvs['_auto_ivc.v1'])
print('response vars (con2):\t', rsps['con2'], rsps['con_cmp2.con2'])
objective (obj):	 [28.58830817] [28.58830817]
constraint (con1):	 [-22.42830237] [-22.42830237]
design vars (x):	 [1.] [1.]
response vars (con2):	 [-11.94151185] [-11.94151185]

You can also access the variables directly from the case object:

print((case['obj'], case['obj_cmp.obj']))
print((case['x'], case['_auto_ivc.v1']))
(array([28.58830817]), array([28.58830817]))
(array([1.]), array([1.]))

Getting Variable Data from Case Recording of a Problem#

Here are the methods typically used when retrieving data from the recording of a Problem.

Case.list_inputs(val=True, prom_name=True, units=False, shape=False, desc=False, hierarchical=True, print_arrays=False, tags=None, includes=None, excludes=None, is_indep_var=None, is_design_var=None, out_stream=DEFAULT_OUT_STREAM, print_min=False, print_max=False, return_format='list')[source]

Return and optionally log a list of input names and other optional information.

Parameters:
valbool, optional

When True, display/return input values. Default is True.

prom_namebool, optional

When True, display/return the promoted name of the variable. Default is True.

unitsbool, optional

When True, display/return units. Default is False.

shapebool, optional

When True, display/return the shape of the value. Default is False.

descbool, optional

When True, display/return description. Default is False.

hierarchicalbool, optional

When True, human readable output shows variables in hierarchical format.

print_arraysbool, optional

When False, in the columnar display, just display norm of any ndarrays with size > 1. The norm is surrounded by vertical bars to indicate that it is a norm. When True, also display full values of the ndarray below the row. Format is affected by the values set with numpy.set_printoptions Default is False.

tagsstr or list of strs

User defined tags that can be used to filter what gets listed. Only inputs with the given tags will be listed. Default is None, which means there will be no filtering based on tags.

includesstr, iter of str, or None

Glob patterns for pathnames to include in the check. Default is None, which includes all.

excludesstr, iter of str, or None

Glob patterns for pathnames to exclude from the check. Default is None, which excludes nothing.

is_indep_varbool or None

If None (the default), do no additional filtering of the inputs. If True, list only inputs connected to an output tagged openmdao:indep_var. If False, list only inputs _not_ connected to outputs tagged openmdao:indep_var.

is_design_varbool or None

If None (the default), do no additional filtering of the inputs. If True, list only inputs connected to outputs that are driver design variables. If False, list only inputs _not_ connected to outputs that are driver design variables.

out_streamfile-like object

Where to send human readable output. Default is sys.stdout. Set to None to suppress.

print_minbool, optional

When true, if the input value is an array, print its smallest value.

print_maxbool, optional

When true, if the input value is an array, print its largest value.

return_formatstr

Indicates the desired format of the return value. Can have value of ‘list’ or ‘dict’. If ‘list’, the return value is a list of (name, metadata) tuples. if ‘dict’, the return value is a dictionary mapping {name: metadata}.

Returns:
list of (name, metadata) or dict of {name: metadata}

List or dict of input names and other optional information about those inputs.

Case.list_outputs(explicit=True, implicit=True, val=True, prom_name=True, residuals=False, residuals_tol=None, units=False, shape=False, bounds=False, scaling=False, desc=False, hierarchical=True, print_arrays=False, tags=None, includes=None, excludes=None, is_indep_var=None, is_design_var=None, list_autoivcs=False, out_stream=DEFAULT_OUT_STREAM, print_min=False, print_max=False, return_format='list')[source]

Return and optionally log a list of output names and other optional information.

Parameters:
explicitbool, optional

Include outputs from explicit components. Default is True.

implicitbool, optional

Include outputs from implicit components. Default is True.

valbool, optional

When True, display/return output values. Default is True.

prom_namebool, optional

When True, display/return the promoted name of the variable. Default is False.

residualsbool, optional

When True, display/return residual values. Default is False.

residuals_tolfloat, optional

If set, limits the output of list_outputs to only variables where the norm of the resids array is greater than the given ‘residuals_tol’. Default is None.

unitsbool, optional

When True, display/return units. Default is False.

shapebool, optional

When True, display/return the shape of the value. Default is False.

boundsbool, optional

When True, display/return bounds (lower and upper). Default is False.

scalingbool, optional

When True, display/return scaling (ref, ref0, and res_ref). Default is False.

descbool, optional

When True, display/return description. Default is False.

hierarchicalbool, optional

When True, human readable output shows variables in hierarchical format.

print_arraysbool, optional

When False, in the columnar display, just display norm of any ndarrays with size > 1. The norm is surrounded by vertical bars to indicate that it is a norm. When True, also display full values of the ndarray below the row. Format is affected by the values set with numpy.set_printoptions Default is False.

tagsstr or list of strs

User defined tags that can be used to filter what gets listed. Only outputs with the given tags will be listed. Default is None, which means there will be no filtering based on tags.

includesstr, iter of str, or None

Glob patterns for pathnames to include in the check. Default is None, which includes all.

excludesstr, iter of str, or None

Glob patterns for pathnames to exclude from the check. Default is None, which excludes nothing.

is_indep_varbool or None

If None (the default), do no additional filtering of the inputs. If True, list only inputs connected to an output tagged openmdao:indep_var. If False, list only inputs _not_ connected to outputs tagged openmdao:indep_var.

is_design_varbool or None

If None (the default), do no additional filtering of the inputs. If True, list only inputs connected to outputs that are driver design variables. If False, list only inputs _not_ connected to outputs that are driver design variables.

list_autoivcsbool

If True, include auto_ivc outputs in the listing. Defaults to False.

out_streamfile-like

Where to send human readable output. Default is sys.stdout. Set to None to suppress.

print_minbool, optional

When true, if the output value is an array, print its smallest value.

print_maxbool, optional

When true, if the output value is an array, print its largest value.

return_formatstr

Indicates the desired format of the return value. Can have value of ‘list’ or ‘dict’. If ‘list’, the return value is a list of (name, metadata) tuples. if ‘dict’, the return value is a dictionary mapping {name: metadata}.

Returns:
list of (name, metadata) or dict of {name: metadata}

List or dict of output names and other optional information about those outputs.

The following example shows how to use these methods.

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

prob = SellarProblem(model_class=SellarDerivatives)

recorder = om.SqliteRecorder("cases.sql")
prob.model.nonlinear_solver = om.NonlinearBlockGS()
prob.model.linear_solver = om.ScipyKrylov()

prob.model.add_recorder(recorder)
prob.model.recording_options['record_residuals'] = True

prob.setup()

d1 = prob.model.d1
d1.add_recorder(recorder)

prob.run_driver()
prob.cleanup()

cr = om.CaseReader("cases.sql")
/usr/share/miniconda/envs/test/lib/python3.11/site-packages/openmdao/recorders/sqlite_recorder.py:226: UserWarning:The existing case recorder file, cases.sql, is being overwritten.
system_cases = cr.list_cases('root.d1')

root.d1
rank0:Driver|0|root._solve_nonlinear|0|d1._solve_nonlinear|0
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|1|d1._solve_nonlinear|1
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|2|d1._solve_nonlinear|2
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|3|d1._solve_nonlinear|3
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|4|d1._solve_nonlinear|4
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|5|d1._solve_nonlinear|5
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|6|d1._solve_nonlinear|6
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|7|d1._solve_nonlinear|7

list_inputs() and list_outputs() will print a report to the screen as well as returning a list of the variables and their values.

case = cr.get_case(system_cases[1])

case_inputs = case.list_inputs()
case_outputs = case.list_outputs()
3 Input(s) in 'd1'

varname  val                  prom_name
-------  -------------------  ---------
z        |5.38516481|         z        
x        [1.]                 x        
y2       [12.27257053]        y2       


1 Explicit Output(s) in 'd1'

varname  val            prom_name
-------  -------------  ---------
y1       [25.54548589]  y1       


0 Implicit Output(s) in 'd1'
from pprint import pprint
pprint(case_inputs)
pprint(case_outputs)
[('d1.x', {'prom_name': 'x', 'val': array([1.])}),
 ('d1.y2', {'prom_name': 'y2', 'val': array([12.27257053])}),
 ('d1.z', {'prom_name': 'z', 'val': array([5., 2.])})]
[('d1.y1', {'prom_name': 'y1', 'val': array([25.54548589])})]

The list_inputs() and list_outputs() methods have optional arguments that let you filter based on variable names what gets listed. This is shown in the following examples.

import openmdao.api as om
from openmdao.core.tests.test_expl_comp import RectangleComp

model = om.Group()
prob = om.Problem(model)
model.add_recorder(om.SqliteRecorder('cases.sql'))

model.add_subsystem('rect', RectangleComp(), promotes=['length', 'width', 'area'])

prob.setup(check=False)

prob.set_val('length', 100.)
prob.set_val('width', 60.0)

prob.run_model()

prob.cleanup()

cr = om.CaseReader("cases.sql")
case = cr.get_case(0)
# Inputs with includes
inputs = case.list_inputs(includes=['*length'])
print(inputs)
1 Input(s) in 'model'

varname   val     prom_name
--------  ------  ---------
rect
  length  [100.]  length   


[('rect.length', {'val': array([100.]), 'prom_name': 'length'})]
# Inputs with excludes
inputs = case.list_inputs(excludes=['*length'])
print(inputs)
1 Input(s) in 'model'

varname  val    prom_name
-------  -----  ---------
rect
  width  [60.]  width    


[('rect.width', {'val': array([60.]), 'prom_name': 'width'})]
# Outputs with includes
outputs = case.list_outputs(includes=['*area'])
print(outputs)
1 Explicit Output(s) in 'model'

varname  val      prom_name
-------  -------  ---------
rect
  area   [6000.]  area     


0 Implicit Output(s) in 'model'


[('rect.area', {'val': array([6000.]), 'prom_name': 'area'})]

Finally, you can also make use of the variable tagging feature when getting values from cases. This example shows how to do that.

import openmdao.api as om

class RectangleCompWithTags(om.ExplicitComponent):
    """
    A simple Explicit Component that also has input and output with tags.
    """

    def setup(self):
        self.add_input('length', val=1., tags=["tag1", "tag2"])
        self.add_input('width', val=1., tags=["tag2"])
        self.add_output('area', val=1., tags="tag1")

    def setup_partials(self):
        self.declare_partials('*', '*')

    def compute(self, inputs, outputs):
        outputs['area'] = inputs['length'] * inputs['width']

model = om.Group()
prob = om.Problem(model)
model.add_recorder(om.SqliteRecorder('cases.sql'))

model.add_subsystem('rect', RectangleCompWithTags(), promotes=['length', 'width', 'area'])

prob.setup(check=False)

prob.set_val('length', 100.0)
prob.set_val('width', 60.0)

prob.run_model()

prob.cleanup()

cr = om.CaseReader("cases.sql")
case = cr.get_case(0)
# Inputs with tag that matches
inputs = case.list_inputs(tags="tag1")
print(inputs)
1 Input(s) in 'model'

varname   val     prom_name
--------  ------  ---------
rect
  length  [100.]  length   


[('rect.length', {'val': array([100.]), 'prom_name': 'length'})]
# Inputs with multiple tags
inputs = case.list_inputs(tags=["tag1", "tag2"])
print(inputs)
2 Input(s) in 'model'

varname   val     prom_name
--------  ------  ---------
rect
  length  [100.]  length   
  width   [60.]   width    


[('rect.length', {'val': array([100.]), 'prom_name': 'length'}), ('rect.width', {'val': array([60.]), 'prom_name': 'width'})]
# Outputs with tag that does match
outputs = case.list_outputs(tags="tag1")
print(outputs)
1 Explicit Output(s) in 'model'

varname  val      prom_name
-------  -------  ---------
rect
  area   [6000.]  area     


0 Implicit Output(s) in 'model'


[('rect.area', {'val': array([6000.]), 'prom_name': 'area'})]

Getting Variable Data from Case By Specifying Variable Name and Units Desired#

You can also get variable values from a Case like you would from a Problem using dictionary-like access or, if you want the value in different units, using the get_val method.

Case.get_val(name, units=None, indices=None)[source]

Get an output/input variable.

Function is used if you want to specify display units.

Parameters:
namestr

Promoted or relative variable name in the root system’s namespace.

unitsstr, optional

Units to convert to before upon return.

indicesint or list of ints or tuple of ints or int ndarray or Iterable or None, optional

Indices or slice to return.

Returns:
float or ndarray

The requested output/input variable.

This example shows both methods of getting variable data by name.

import openmdao.api as om

model = om.Group()
model.add_recorder(om.SqliteRecorder('cases.sql'))

speed = om.ExecComp('v=x/t', x={'units': 'm'}, t={'units': 's'}, v={'units': 'm/s'})

model.add_subsystem('speed', speed, promotes=['x', 't', 'v'])

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

prob.set_val('x', 100., units='m')
prob.set_val('t', 60., units='s')

prob.run_model()
prob.cleanup()

cr = om.CaseReader('cases.sql')
case = cr.get_case(0)
print(case['x'])
[100.]
print(case.get_val('x', units='ft'))
[328.0839895]
print(case['v'])
[1.66666667]
print(case.get_val('v', units='ft/s'))
[5.46806649]

Getting Derivative Data from a Case#

A driver has the ability to record derivatives but it is not enabled by default. If you do enable this option, the recorded cases will have a value for the jacobian.

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, disp=False)
driver.recording_options['record_derivatives'] = True

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

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

cr = om.CaseReader('cases.sql')
# Get derivatives associated with the last iteration.
derivs = cr.get_case(-1).derivatives
derivs
{('obj', 'z'): array([[3.90585345, 1.97002049]]),
 ('obj', 'x'): array([[0.9934144]]),
 ('con1', 'z'): array([[-3.95527316, -0.99999439]]),
 ('con1', 'x'): array([[-0.99999439]]),
 ('con2', 'z'): array([[2.11249959, 1.28126722]]),
 ('con2', 'x'): array([[0.2812688]])}
# Get specific derivative.
print(derivs['obj', 'z'])
[[3.90585345 1.97002049]]

Problem recording can also include recording of the derivatives as this example shows.

import openmdao.api as om
from openmdao.test_suite.components.eggcrate import EggCrate

prob = om.Problem()

model = prob.model
model.add_subsystem('egg_crate', EggCrate(), promotes=['x', 'y', 'f_xy'])
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')

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

prob.recording_options['record_derivatives'] = True
prob.add_recorder(om.SqliteRecorder('cases.sql'))

prob.setup()
prob.set_solver_print(0)

prob.set_val('x', 2.5)
prob.set_val('y', 2.5)

prob.run_driver()

case_name_1 = "c1"
prob.record(case_name_1)
prob.set_val('x', 0.1)
prob.set_val('y', -0.1)

prob.run_driver()

case_name_2 = "c2"
prob.record(case_name_2)

prob.cleanup()
Model viewer data has already been recorded for Driver.
cr = om.CaseReader('cases.sql')

c1 = cr.get_case(case_name_1)
c2 = cr.get_case(case_name_2)

print(c1.derivatives)
print(c2.derivatives)
{('f_xy', 'x'): array([[-1.50431553e-05]]), ('f_xy', 'y'): array([[-1.50431553e-05]])}
{('f_xy', 'x'): array([[-1.11442227e-06]]), ('f_xy', 'y'): array([[1.11442256e-06]])}

Note

For both Driver and Problem, the recording of the derivatives is not affected by the includes and excludes options.