Reloading Case Data into a Model#

The OpenMDAO API provides a way to load into a model a case from a case recorder file. The method to do this is Problem.load_case. Its only argument is a case object from a case recorder file.

Problem.load_case(case)[source]

Pull all input and output variables from a case into the model.

Parameters:
caseCase or dict

A Case from a CaseReader, or a dictionary with key ‘inputs’ mapped to the output of problem.model.list_inputs and key ‘outputs’ mapped to the output of prob.model.list_outputs. Both list_inputs and list_outputs should be called with prom_name=True and return_format=’dict’.

The method pulls all input and output variables from a case into the model.

Using case recorders, you can record many sources (driver, system, problem, solver). Therefore, cases can have many different types of values. load_case works with all of these recordings.

It is important to keep in mind that just the variables in the case are updated in the model. The rest of the model variables are left unchanged.

Why would you want to load a case?#

There are several use cases for using load_case.

  • Save points from a Design of Experiments (DOE) run and then go back and run further analysis on them

  • Give an optimization run an initial guess with values from a previous optimization run. For example, a user would use one optimization method to quickly get a solution that is close, save the case, then using a more accurate, but slower method to find the solution after loading in that case from the first run

  • Run a sweep of constraint values. This would allow a user to save time by doing runs using initial values from previous runs instead of starting from scratch each time

Simple load case example#

Here is a simple example of how to call the load_case method.

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

prob = SellarProblem()

model = prob.model
model.recording_options['record_inputs'] = True
model.recording_options['record_outputs'] = True
model.recording_options['record_residuals'] = True
case_recorder_filename = "cases.sql"
model.add_recorder(om.SqliteRecorder(case_recorder_filename))

prob.setup()

prob.run_driver()
prob.cleanup()

cr = om.CaseReader(case_recorder_filename)

system_cases = cr.list_cases('root', out_stream=None)
case = cr.get_case(system_cases[0])

# Now load in the case we recorded
prob.load_case(case)
/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.

As was mentioned before, just the variables values in the case get updated in the model. In this example, the case contains values for the model variables x,z,y1,y2,con1,con2, and obj.

But if the recorder had only been attached to a subsystem, like this

model.d2.add_recorder(om.SqliteRecorder(case_recorder_filename))

only the variables z,y1, and y2 would have been recorded and when load_case was called with a case from that recording, only those variables in the model would be updated.

Here is a somewhat more realistic use case for load_case. In this example, an optimization is run and then the third case is used to load the model and start the optimization again. Notice how in the second optimization run, the optimization requires fewer iterations since it was given an initial guess.

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

prob = SellarProblem(SellarMDA)
prob.setup()

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

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

driver = prob.driver = om.ScipyOptimizeDriver()
driver.options['optimizer'] = 'SLSQP'
driver.options['tol'] = 1e-9
driver.options['disp'] = False
driver.recording_options['record_desvars'] = True
driver.recording_options['record_objectives'] = True
driver.recording_options['record_constraints'] = True

prob.run_driver()
prob.cleanup()

inputs_before = prob.model.list_inputs(val=True, units=True, out_stream=None)
outputs_before = prob.model.list_outputs(val=True, units=True, out_stream=None)

cr = om.CaseReader(case_recorder_filename)

# get third case
system_cases = cr.list_cases('root')  #, out_stream=None)
third_case = cr.get_case(system_cases[4])

iter_count_before = driver.iter_count

# run the model again with a fresh model
prob = SellarProblem(SellarMDA)
prob.setup()

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

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

prob.load_case(third_case)
prob.run_driver()
prob.cleanup()

inputs_after = prob.model.list_inputs(val=True, units=True, out_stream=None)
outputs_after = prob.model.list_outputs(val=True, units=True, out_stream=None)

iter_count_after = driver.iter_count
print(f"iterations before: {iter_count_before} iterations after: {iter_count_after}")

root
rank0:ScipyOptimize_SLSQP|0|root._solve_nonlinear|0
rank0:ScipyOptimize_SLSQP|1|root._solve_nonlinear|1
rank0:ScipyOptimize_SLSQP|2|root._solve_nonlinear|2
rank0:ScipyOptimize_SLSQP|3|root._solve_nonlinear|3
rank0:ScipyOptimize_SLSQP|4|root._solve_nonlinear|4
rank0:ScipyOptimize_SLSQP|5|root._solve_nonlinear|5
rank0:ScipyOptimize_SLSQP|6|root._solve_nonlinear|6
iterations before: 7 iterations after: 4