import os
import pathlib
import warnings
import openmdao.api as om
from openmdao.recorders.case import Case
from ._options import options as dymos_options
from dymos.trajectory.trajectory import Trajectory
from dymos.visualization.timeseries_plots import timeseries_plots
from .grid_refinement.refinement import _refine_iter
[docs]def run_problem(problem, refine_method='hp', refine_iteration_limit=0, run_driver=True,
simulate=False, restart=None,
solution_record_file='dymos_solution.db',
simulation_record_file='dymos_simulation.db',
make_plots=False,
plot_dir="plots",
case_prefix=None,
reset_iter_counts=True,
simulate_kwargs=None,
plot_kwargs=None,
):
"""
A Dymos-specific interface to execute an OpenMDAO problem containing Dymos Trajectories or
Phases. This function can iteratively call run_driver to perform grid refinement, and
automatically call simulate following a run to check the validity of a result.
Parameters
----------
problem : om.Problem
The OpenMDAO problem object to be run, presumed to contain one or more dymos phases.
refine_method : String
The choice of refinement algorithm to use for grid refinement. Current options are 'hp' for h-then-p adaptive
or 'ph' for 'p-then-h' adaptive.
refine_iteration_limit : int
The maximum number of passes through the grid refinement algorithm to be made.
run_driver : bool
If True, run the driver attached to the problem, otherwise just run the model one time.
simulate : bool
If True, perform a simulation of any Trajectories found in the Problem model after the driver
has been run and grid refinement is complete.
restart : str, Case, or None
If given as a dict returned by om.CaseReader.get_case, automatically load the states, controls, and
parameters as given in the provided case as the initial guess for the next run.
If given as a string, assume the user is providing the path to a CaseRecorder file that contains a case named
"final" that the user wants to use as the guess for the case.
make_plots : bool
If True, automatically generate plots of all timeseries outputs.
These are stored in the reports subdirectory generated by OpenMDAO.
solution_record_file : str
Path to case recorder file use to store results from solution.
simulation_record_file : str
Path to case recorder file use to store results from simulation.
plot_dir : str
Name of the plot directory to be created.
simulate_kwargs : dict
A dictionary of argument: value pairs to be passed to simulate. These are ignored when simulate=False.
plot_kwargs : dict
A dictionary of argument: value pairs that are passed to `timeseries_plots`. Only used when make_plots=True.
case_prefix : str or None
Prefix to prepend to coordinates when recording.
reset_iter_counts : bool
If True and model has been run previously, reset all iteration counters.
"""
if restart is not None:
if isinstance(restart, (str, pathlib.Path)):
case = om.CaseReader(restart).get_case('final')
elif isinstance(restart, Case):
case = restart
else:
raise ValueError('If given, option restart must specify a string to the filepath of a valid dymos '
'output case, or a case dictionary returned from om.CaseReader.get_case.')
if solution_record_file not in [rec._filepath for rec in iter(problem._rec_mgr)]:
recorder = om.SqliteRecorder(solution_record_file)
problem.add_recorder(recorder)
# record_outputs is need to capture the timeseries outputs
problem.recording_options['record_outputs'] = True
problem.final_setup()
if restart is not None:
problem.load_case(case)
for traj in problem.model.system_iter(include_self=True, recurse=True, typ=Trajectory):
traj._check_phase_graph()
if run_driver:
failed = _refine_iter(problem, refine_iteration_limit, refine_method, case_prefix=case_prefix,
reset_iter_counts=reset_iter_counts)
else:
failed = problem.run_model()
if refine_iteration_limit > 0:
warnings.warn("Refinement not performed. Set run_driver to True to perform refinement.")
_case_prefix = '' if case_prefix is None else f'{case_prefix}_'
problem.record(f'{_case_prefix}final') # save case for potential restart
problem.cleanup()
sims = {}
if simulate:
_simulate_kwargs = simulate_kwargs if simulate_kwargs is not None else {}
if 'record_file' in _simulate_kwargs:
raise ValueError('Key "record_file" was found in simulate_kwargs but should instead by provided by the '
'argument "simulation_record_file".')
if 'case_prefix' in _simulate_kwargs:
raise ValueError('Key "case_prefix" was found in simulate_kwargs but should instead by provided by the '
'argument "case_prefix", not part of the simulate_kwargs dictionary.')
for subsys in problem.model.system_iter(include_self=True, recurse=True):
if isinstance(subsys, Trajectory):
sim_prob = subsys.simulate(record_file=simulation_record_file,
case_prefix=case_prefix,
**_simulate_kwargs)
sims[subsys.pathname] = sim_prob
if make_plots:
outputs_dir = problem.get_outputs_dir()
if os.sep in str(solution_record_file):
_sol_record_file = solution_record_file
else:
_sol_record_file = outputs_dir / solution_record_file
if simulate:
sim_outputs_dir = list(sims.values())[0].get_outputs_dir()
if os.sep in str(simulation_record_file):
_sim_record_file = simulation_record_file
else:
_sim_record_file = sim_outputs_dir / simulation_record_file
else:
_sim_record_file = None
_plot_kwargs = plot_kwargs if plot_kwargs is not None else {}
if dymos_options['plots'] == 'bokeh':
from dymos.visualization.timeseries.bokeh_timeseries_report import make_timeseries_report
make_timeseries_report(prob=problem,
solution_record_file=_sol_record_file,
simulation_record_file=_sim_record_file,
**_plot_kwargs)
else:
plots_dir = problem.get_reports_dir() / 'plots'
timeseries_plots(_sol_record_file,
simulation_record_file=_sim_record_file,
plot_dir=plots_dir, **_plot_kwargs)
return failed