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(prob.get_outputs_dir() / 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:230: UserWarning:The existing case recorder file, /home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/openmdao_book/features/recording/problem_out/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(prob.get_outputs_dir() / 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