Advanced Recording Example

Below we demonstrate a more advanced example of case recording including the four different objects that a recorder can be attached to. We will then show how to extract various data from the model and finally, relate that back to an XDSM for illustrative purposes.

import openmdao.api as om
from openmdao.test_suite.components.sellar_feature import SellarMDAWithUnits
import numpy as np
import matplotlib.pyplot as plt

# build the model
prob = om.Problem(model=SellarMDAWithUnits())

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)

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

# Here we show how to attach recorders to each of the four objects; problem, driver, solver, and system
# Create a recorder variable
recorder = om.SqliteRecorder('cases.sql')
# Attach a recorder to the problem
prob.add_recorder(recorder)
# Attach a recorder to the driver
driver.add_recorder(recorder)

prob.setup()

# To attach a recorder to the system, you need to call it after `setup` so the model hierarchy has been generated
obj_cmp = prob.model.obj_cmp
obj_cmp.add_recorder(recorder)
# Attach a recorder to the solver
model.cycle.add_recorder(recorder)

prob.set_solver_print(0)
prob.run_driver()
prob.record("final_state")
prob.cleanup()

The following XDSM diagram shows the SellarMDA component equations and their inputs and outputs. Through the different recorders we can access the different parts of the model. We’ll take you through an example of each object and relate it back to the diagram.

../_images/sellar_xdsm.jpg

System

First, we’ll examine the system recorder. Suppose we want to know what the value of y1 is going into the objective function (obj_func). Using the list_cases method, we’ll get the list of cases that were recorded by the objective component root.obj_comp. You could also access the discipline equations by swapping out the subsystem root.obj_comp for root.con_cmp1. Next we use get_case to determine the input keys of the first case’s dictionary. Here we find that x, y1, y2, z are returned. Since we originally sought find the value of y1 going into the objective function, we’ll loop through the 14 cases to find what the value is in each case.

import openmdao.api as om

# Instantiate your CaseReader
cr = om.CaseReader("cases.sql")

system_cases = cr.list_cases('root.obj_cmp')

# Number of cases in the optimization
num_cases = len(system_cases)
print("Number of cases:", num_cases)
system
    rank0:ScipyOptimize_SLSQP|0|root._solve_nonlinear|0|NLRunOnce|0|obj_cmp._solve_nonlinear|0
system
    rank0:ScipyOptimize_SLSQP|1|root._solve_nonlinear|1|NLRunOnce|0|obj_cmp._solve_nonlinear|1
system
    rank0:ScipyOptimize_SLSQP|2|root._solve_nonlinear|2|NLRunOnce|0|obj_cmp._solve_nonlinear|2
system
    rank0:ScipyOptimize_SLSQP|3|root._solve_nonlinear|3|NLRunOnce|0|obj_cmp._solve_nonlinear|3
system
    rank0:ScipyOptimize_SLSQP|4|root._solve_nonlinear|4|NLRunOnce|0|obj_cmp._solve_nonlinear|4
system
    rank0:ScipyOptimize_SLSQP|5|root._solve_nonlinear|5|NLRunOnce|0|obj_cmp._solve_nonlinear|5
system
    rank0:ScipyOptimize_SLSQP|6|root._solve_nonlinear|6|NLRunOnce|0|obj_cmp._solve_nonlinear|6
system
    rank0:ScipyOptimize_SLSQP|7|root._solve_nonlinear|7|NLRunOnce|0|obj_cmp._solve_nonlinear|7
system
    rank0:ScipyOptimize_SLSQP|8|root._solve_nonlinear|8|NLRunOnce|0|obj_cmp._solve_nonlinear|8
system
    rank0:ScipyOptimize_SLSQP|9|root._solve_nonlinear|9|NLRunOnce|0|obj_cmp._solve_nonlinear|9
system
    rank0:ScipyOptimize_SLSQP|10|root._solve_nonlinear|10|NLRunOnce|0|obj_cmp._solve_nonlinear|10
system
    rank0:ScipyOptimize_SLSQP|11|root._solve_nonlinear|11|NLRunOnce|0|obj_cmp._solve_nonlinear|11
system
    rank0:ScipyOptimize_SLSQP|12|root._solve_nonlinear|12|NLRunOnce|0|obj_cmp._solve_nonlinear|12
system
    rank0:ScipyOptimize_SLSQP|13|root._solve_nonlinear|13|NLRunOnce|0|obj_cmp._solve_nonlinear|13
Number of cases: 14
# Get the keys of all the inputs to the obj_func
case = cr.get_case(system_cases[0])
print(list(case.inputs.keys()))
['x', 'y1', 'y2', 'z']
for i in range(num_cases):
    case = cr.get_case(system_cases[i])
    print(case['y1'])
[3.16]

Solver

Similar to the system recorder, we can query the cases recorded by the solver. You can also access the values of inputs to the equation with the solver but in this case we’ll focus on y1 and y2 values from the discipline functions going into the objective function the system recorder.

We’ll pass ‘root.cycle’ to the method list_cases, find how many cases there are and arbitrarily pick number 3.

import openmdao.api as om

# Instantiate your CaseReader
cr = om.CaseReader("cases.sql")

solver_cases = cr.list_cases('root.cycle')

num_cases = len(solver_cases)
print("Number of cases:", num_cases)
system
    rank0:ScipyOptimize_SLSQP|0|root._solve_nonlinear|0|NLRunOnce|0|cycle._solve_nonlinear|0
system
    rank0:ScipyOptimize_SLSQP|1|root._solve_nonlinear|1|NLRunOnce|0|cycle._solve_nonlinear|1
system
    rank0:ScipyOptimize_SLSQP|2|root._solve_nonlinear|2|NLRunOnce|0|cycle._solve_nonlinear|2
system
    rank0:ScipyOptimize_SLSQP|3|root._solve_nonlinear|3|NLRunOnce|0|cycle._solve_nonlinear|3
system
    rank0:ScipyOptimize_SLSQP|4|root._solve_nonlinear|4|NLRunOnce|0|cycle._solve_nonlinear|4
system
    rank0:ScipyOptimize_SLSQP|5|root._solve_nonlinear|5|NLRunOnce|0|cycle._solve_nonlinear|5
system
    rank0:ScipyOptimize_SLSQP|6|root._solve_nonlinear|6|NLRunOnce|0|cycle._solve_nonlinear|6
system
    rank0:ScipyOptimize_SLSQP|7|root._solve_nonlinear|7|NLRunOnce|0|cycle._solve_nonlinear|7
system
    rank0:ScipyOptimize_SLSQP|8|root._solve_nonlinear|8|NLRunOnce|0|cycle._solve_nonlinear|8
system
    rank0:ScipyOptimize_SLSQP|9|root._solve_nonlinear|9|NLRunOnce|0|cycle._solve_nonlinear|9
system
    rank0:ScipyOptimize_SLSQP|10|root._solve_nonlinear|10|NLRunOnce|0|cycle._solve_nonlinear|10
system
    rank0:ScipyOptimize_SLSQP|11|root._solve_nonlinear|11|NLRunOnce|0|cycle._solve_nonlinear|11
system
    rank0:ScipyOptimize_SLSQP|12|root._solve_nonlinear|12|NLRunOnce|0|cycle._solve_nonlinear|12
system
    rank0:ScipyOptimize_SLSQP|13|root._solve_nonlinear|13|NLRunOnce|0|cycle._solve_nonlinear|13
Number of cases: 14
case = cr.get_case(solver_cases[3])
print(case['y1'])
[4.17430704]
print(case['y2'])
[4.28622419]

Driver

If we want to view the convergence of the model, the best place to find that by looking at the cases recorded by the driver. By default, a recorder attached to a driver will record the design variables, constraints and objectives, so we will print them for the model at the end of the optimization. We’ll use the helper methods like get_objectives, get_design_vars, get_constraints to return the info we’re seeking.

import openmdao.api as om

# Instantiate your CaseReader
cr = om.CaseReader("cases.sql")

driver_cases = cr.list_cases('driver')

last_case = cr.get_case(driver_cases[-1])

objectives = last_case.get_objectives()
design_vars = last_case.get_design_vars()
constraints = last_case.get_constraints()

print(objectives['obj'])
system
    rank0:ScipyOptimize_SLSQP|0|root._solve_nonlinear|0|NLRunOnce|0|cycle._solve_nonlinear|0
    rank0:ScipyOptimize_SLSQP|0|root._solve_nonlinear|0|NLRunOnce|0|obj_cmp._solve_nonlinear|0
driver
    rank0:ScipyOptimize_SLSQP|0
system
    rank0:ScipyOptimize_SLSQP|1|root._solve_nonlinear|1|NLRunOnce|0|cycle._solve_nonlinear|1
    rank0:ScipyOptimize_SLSQP|1|root._solve_nonlinear|1|NLRunOnce|0|obj_cmp._solve_nonlinear|1
driver
    rank0:ScipyOptimize_SLSQP|1
system
    rank0:ScipyOptimize_SLSQP|2|root._solve_nonlinear|2|NLRunOnce|0|cycle._solve_nonlinear|2
    rank0:ScipyOptimize_SLSQP|2|root._solve_nonlinear|2|NLRunOnce|0|obj_cmp._solve_nonlinear|2
driver
    rank0:ScipyOptimize_SLSQP|2
system
    rank0:ScipyOptimize_SLSQP|3|root._solve_nonlinear|3|NLRunOnce|0|cycle._solve_nonlinear|3
    rank0:ScipyOptimize_SLSQP|3|root._solve_nonlinear|3|NLRunOnce|0|obj_cmp._solve_nonlinear|3
driver
    rank0:ScipyOptimize_SLSQP|3
system
    rank0:ScipyOptimize_SLSQP|4|root._solve_nonlinear|4|NLRunOnce|0|cycle._solve_nonlinear|4
    rank0:ScipyOptimize_SLSQP|4|root._solve_nonlinear|4|NLRunOnce|0|obj_cmp._solve_nonlinear|4
driver
    rank0:ScipyOptimize_SLSQP|4
system
    rank0:ScipyOptimize_SLSQP|5|root._solve_nonlinear|5|NLRunOnce|0|cycle._solve_nonlinear|5
    rank0:ScipyOptimize_SLSQP|5|root._solve_nonlinear|5|NLRunOnce|0|obj_cmp._solve_nonlinear|5
driver
    rank0:ScipyOptimize_SLSQP|5
system
    rank0:ScipyOptimize_SLSQP|6|root._solve_nonlinear|6|NLRunOnce|0|cycle._solve_nonlinear|6
    rank0:ScipyOptimize_SLSQP|6|root._solve_nonlinear|6|NLRunOnce|0|obj_cmp._solve_nonlinear|6
driver
    rank0:ScipyOptimize_SLSQP|6
system
    rank0:ScipyOptimize_SLSQP|7|root._solve_nonlinear|7|NLRunOnce|0|cycle._solve_nonlinear|7
    rank0:ScipyOptimize_SLSQP|7|root._solve_nonlinear|7|NLRunOnce|0|obj_cmp._solve_nonlinear|7
driver
    rank0:ScipyOptimize_SLSQP|7
system
    rank0:ScipyOptimize_SLSQP|8|root._solve_nonlinear|8|NLRunOnce|0|cycle._solve_nonlinear|8
    rank0:ScipyOptimize_SLSQP|8|root._solve_nonlinear|8|NLRunOnce|0|obj_cmp._solve_nonlinear|8
driver
    rank0:ScipyOptimize_SLSQP|8
system
    rank0:ScipyOptimize_SLSQP|9|root._solve_nonlinear|9|NLRunOnce|0|cycle._solve_nonlinear|9
    rank0:ScipyOptimize_SLSQP|9|root._solve_nonlinear|9|NLRunOnce|0|obj_cmp._solve_nonlinear|9
driver
    rank0:ScipyOptimize_SLSQP|9
system
    rank0:ScipyOptimize_SLSQP|10|root._solve_nonlinear|10|NLRunOnce|0|cycle._solve_nonlinear|10
    rank0:ScipyOptimize_SLSQP|10|root._solve_nonlinear|10|NLRunOnce|0|obj_cmp._solve_nonlinear|10
driver
    rank0:ScipyOptimize_SLSQP|10
system
    rank0:ScipyOptimize_SLSQP|11|root._solve_nonlinear|11|NLRunOnce|0|cycle._solve_nonlinear|11
    rank0:ScipyOptimize_SLSQP|11|root._solve_nonlinear|11|NLRunOnce|0|obj_cmp._solve_nonlinear|11
driver
    rank0:ScipyOptimize_SLSQP|11
system
    rank0:ScipyOptimize_SLSQP|12|root._solve_nonlinear|12|NLRunOnce|0|cycle._solve_nonlinear|12
    rank0:ScipyOptimize_SLSQP|12|root._solve_nonlinear|12|NLRunOnce|0|obj_cmp._solve_nonlinear|12
driver
    rank0:ScipyOptimize_SLSQP|12
system
    rank0:ScipyOptimize_SLSQP|13|root._solve_nonlinear|13|NLRunOnce|0|cycle._solve_nonlinear|13
    rank0:ScipyOptimize_SLSQP|13|root._solve_nonlinear|13|NLRunOnce|0|obj_cmp._solve_nonlinear|13
driver
    rank0:ScipyOptimize_SLSQP|13
[3.18339395]
print(design_vars['x'])
[0.]
print(design_vars['z'][0])
1.9776388835106768
print(design_vars['z'][1])
1.2503545854874587e-15
print(constraints['con1'])
[-1.68550507e-10]
print(constraints['con2'])
[-20.24472223]

Problem

A Problem recorder is best if you want to record an arbitrary case before or after a running the model. In our case, we have placed our point at the end of the model.

import openmdao.api as om

# Instantiate your CaseReader
cr = om.CaseReader("cases.sql")

# get list of cases recorded on problem
problem_cases = cr.list_cases('problem')
print(problem_cases)
['final_state']
# get list of output variables recorded on problem
problem_vars = cr.list_source_vars('problem', out_stream=None)
print(sorted(problem_vars['outputs']))
['con1', 'con2', 'obj', 'x', 'y1', 'y2', 'z']
# get the recorded case and check values
case = cr.get_case('final_state')

objectives = case.get_objectives()
design_vars = case.get_design_vars()
constraints = case.get_constraints()

print(objectives['obj'])
[3.18339395]

Plotting Design Variables

When inspecting or debugging a model, it can be helpful to visualize the path of the design variables to their final values. To do this, we can list the cases of the driver and plot the data with respect to the iteration number.

import matplotlib.pyplot as plt
import numpy as np
import openmdao.api as om

# Instantiate your CaseReader
cr = om.CaseReader("cases.sql")
driver_cases = cr.list_cases('driver')

dv_x_values = []
dv_z_values = []
for i in range(len(driver_cases)):
    last_case = cr.get_case(driver_cases[i])
    design_vars = last_case.get_design_vars()
    if design_vars:
        dv_x_values.append(design_vars['x'])
        dv_z_values.append(design_vars['z'])

# Below is a short script to see the path the design variables took to convergence

fig, (ax1, ax2) = plt.subplots(1, 2)
fig.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0.5, hspace=None)
ax1.plot(np.arange(len(dv_x_values)), np.array(dv_x_values))

ax1.set(xlabel='Iterations', ylabel='Design Var: X', title='Optimization History')
ax1.grid()

ax2.plot(np.arange(len(dv_z_values)), np.array(dv_z_values))

ax2.set(xlabel='Iterations', ylabel='Design Var: Z', title='Optimization History')
ax2.grid()
['system', '    rank0:ScipyOptimize_SLSQP|0|root._solve_nonlinear|0|NLRunOnce|0|cycle._solve_nonlinear|0', '    rank0:ScipyOptimize_SLSQP|0|root._solve_nonlinear|0|NLRunOnce|0|obj_cmp._solve_nonlinear|0', 'driver', '    rank0:ScipyOptimize_SLSQP|0', 'system', '    rank0:ScipyOptimize_SLSQP|1|root._solve_nonlinear|1|NLRunOnce|0|cycle._solve_nonlinear|1', '    rank0:ScipyOptimize_SLSQP|1|root._solve_nonlinear|1|NLRunOnce|0|obj_cmp._solve_nonlinear|1', 'driver', '    rank0:ScipyOptimize_SLSQP|1', 'system', '    rank0:ScipyOptimize_SLSQP|2|root._solve_nonlinear|2|NLRunOnce|0|cycle._solve_nonlinear|2', '    rank0:ScipyOptimize_SLSQP|2|root._solve_nonlinear|2|NLRunOnce|0|obj_cmp._solve_nonlinear|2', 'driver', '    rank0:ScipyOptimize_SLSQP|2', 'system', '    rank0:ScipyOptimize_SLSQP|3|root._solve_nonlinear|3|NLRunOnce|0|cycle._solve_nonlinear|3', '    rank0:ScipyOptimize_SLSQP|3|root._solve_nonlinear|3|NLRunOnce|0|obj_cmp._solve_nonlinear|3', 'driver', '    rank0:ScipyOptimize_SLSQP|3', 'system', '    rank0:ScipyOptimize_SLSQP|4|root._solve_nonlinear|4|NLRunOnce|0|cycle._solve_nonlinear|4', '    rank0:ScipyOptimize_SLSQP|4|root._solve_nonlinear|4|NLRunOnce|0|obj_cmp._solve_nonlinear|4', 'driver', '    rank0:ScipyOptimize_SLSQP|4', 'system', '    rank0:ScipyOptimize_SLSQP|5|root._solve_nonlinear|5|NLRunOnce|0|cycle._solve_nonlinear|5', '    rank0:ScipyOptimize_SLSQP|5|root._solve_nonlinear|5|NLRunOnce|0|obj_cmp._solve_nonlinear|5', 'driver', '    rank0:ScipyOptimize_SLSQP|5', 'system', '    rank0:ScipyOptimize_SLSQP|6|root._solve_nonlinear|6|NLRunOnce|0|cycle._solve_nonlinear|6', '    rank0:ScipyOptimize_SLSQP|6|root._solve_nonlinear|6|NLRunOnce|0|obj_cmp._solve_nonlinear|6', 'driver', '    rank0:ScipyOptimize_SLSQP|6', 'system', '    rank0:ScipyOptimize_SLSQP|7|root._solve_nonlinear|7|NLRunOnce|0|cycle._solve_nonlinear|7', '    rank0:ScipyOptimize_SLSQP|7|root._solve_nonlinear|7|NLRunOnce|0|obj_cmp._solve_nonlinear|7', 'driver', '    rank0:ScipyOptimize_SLSQP|7', 'system', '    rank0:ScipyOptimize_SLSQP|8|root._solve_nonlinear|8|NLRunOnce|0|cycle._solve_nonlinear|8', '    rank0:ScipyOptimize_SLSQP|8|root._solve_nonlinear|8|NLRunOnce|0|obj_cmp._solve_nonlinear|8', 'driver', '    rank0:ScipyOptimize_SLSQP|8', 'system', '    rank0:ScipyOptimize_SLSQP|9|root._solve_nonlinear|9|NLRunOnce|0|cycle._solve_nonlinear|9', '    rank0:ScipyOptimize_SLSQP|9|root._solve_nonlinear|9|NLRunOnce|0|obj_cmp._solve_nonlinear|9', 'driver', '    rank0:ScipyOptimize_SLSQP|9', 'system', '    rank0:ScipyOptimize_SLSQP|10|root._solve_nonlinear|10|NLRunOnce|0|cycle._solve_nonlinear|10', '    rank0:ScipyOptimize_SLSQP|10|root._solve_nonlinear|10|NLRunOnce|0|obj_cmp._solve_nonlinear|10', 'driver', '    rank0:ScipyOptimize_SLSQP|10', 'system', '    rank0:ScipyOptimize_SLSQP|11|root._solve_nonlinear|11|NLRunOnce|0|cycle._solve_nonlinear|11', '    rank0:ScipyOptimize_SLSQP|11|root._solve_nonlinear|11|NLRunOnce|0|obj_cmp._solve_nonlinear|11', 'driver', '    rank0:ScipyOptimize_SLSQP|11', 'system', '    rank0:ScipyOptimize_SLSQP|12|root._solve_nonlinear|12|NLRunOnce|0|cycle._solve_nonlinear|12', '    rank0:ScipyOptimize_SLSQP|12|root._solve_nonlinear|12|NLRunOnce|0|obj_cmp._solve_nonlinear|12', 'driver', '    rank0:ScipyOptimize_SLSQP|12', 'system', '    rank0:ScipyOptimize_SLSQP|13|root._solve_nonlinear|13|NLRunOnce|0|cycle._solve_nonlinear|13', '    rank0:ScipyOptimize_SLSQP|13|root._solve_nonlinear|13|NLRunOnce|0|obj_cmp._solve_nonlinear|13', 'driver', '    rank0:ScipyOptimize_SLSQP|13']
../_images/design_vars.jpg