diff --git a/openmdao/core/driver.py b/openmdao/core/driver.py index 069d02293c..73d58da960 100644 --- a/openmdao/core/driver.py +++ b/openmdao/core/driver.py @@ -452,10 +452,6 @@ def _setup_recording(self): self._rec_mgr.startup(self) - # record the system metadata to the recorders attached to this Driver - for sub in self._problem().model.system_iter(recurse=True, include_self=True): - self._rec_mgr.record_metadata(sub) - def _get_voi_val(self, name, meta, remote_vois, driver_scaling=True, rank=None): """ Get the value of a variable of interest (objective, constraint, or design var). diff --git a/openmdao/core/problem.py b/openmdao/core/problem.py index 049ab41de8..7be984a383 100644 --- a/openmdao/core/problem.py +++ b/openmdao/core/problem.py @@ -25,7 +25,8 @@ from openmdao.solvers.solver import SolverInfo from openmdao.error_checking.check_config import _default_checks, _all_checks from openmdao.recorders.recording_iteration_stack import _RecIteration -from openmdao.recorders.recording_manager import RecordingManager, record_viewer_data +from openmdao.recorders.recording_manager import RecordingManager, record_viewer_data, \ + record_system_options from openmdao.utils.record_util import create_local_meta from openmdao.utils.general_utils import ContainsAll, pad_name, simple_warning, warn_deprecation @@ -825,6 +826,7 @@ def final_setup(self): driver._setup_recording() self._setup_recording() record_viewer_data(self) + record_system_options(self) if self._setup_status < 2: self._setup_status = 2 diff --git a/openmdao/core/system.py b/openmdao/core/system.py index 5d117d40e4..ce5f0cbf97 100644 --- a/openmdao/core/system.py +++ b/openmdao/core/system.py @@ -707,14 +707,6 @@ def _final_setup(self, comm, force_alloc_complex=False): self.set_initial_values() - # Tell all subsystems to record their metadata if they have recorders attached - for sub in self.system_iter(recurse=True, include_self=True): - sub._rec_mgr.record_metadata(sub) - - # Also, record to the recorders attached to this System, - # the system metadata for all the subsystems - self._rec_mgr.record_metadata(sub) - def use_fixed_coloring(self, coloring=_STD_COLORING_FNAME, recurse=True): """ Use a precomputed coloring for this System. diff --git a/openmdao/docs/features/recording/reading_metadata.rst b/openmdao/docs/features/recording/reading_metadata.rst index 2fb2e376d0..0516c18814 100644 --- a/openmdao/docs/features/recording/reading_metadata.rst +++ b/openmdao/docs/features/recording/reading_metadata.rst @@ -21,8 +21,11 @@ a `CaseReader`. System Options -------------- -Systems record both scaling factors and options within 'scaling_factors' and 'component_options', -respectively, in :code:`system_options`. +All case recorders record the component options and scaling factors for all systems in the model. +These values are accessible using code such as this, where :code:`cr` is a case reader object. + + - :code:`cr.system_options['root']['component_options']` + - :code:`cr.system_options['root']['scaling_factors']` The component options include user-defined options that were defined through the :code:`system.options.declare` method. By default, everything in options is @@ -33,9 +36,6 @@ to record, they can be excluded using the 'options_excludes' recording option on openmdao.recorders.tests.test_sqlite_recorder.TestFeatureSqliteRecorder.test_feature_recording_system_options :layout: interleave -.. note:: - Each system object must have a recorder explicitly attached in order for its metadata and options to be recorded. - Solver Metadata --------------- diff --git a/openmdao/recorders/recording_manager.py b/openmdao/recorders/recording_manager.py index e650528f63..f61e736651 100644 --- a/openmdao/recorders/recording_manager.py +++ b/openmdao/recorders/recording_manager.py @@ -217,6 +217,12 @@ def _get_all_viewer_data_recorders(problem): yield r +def _get_all_recorders(problem): + for req in _get_all_requesters(problem): + for r in req._rec_mgr._recorders: + yield r + + def record_viewer_data(problem): """ Record model viewer data for all recorders that have that option enabled. @@ -240,3 +246,20 @@ def record_viewer_data(problem): viewer_data.pop('abs2prom', None) # abs2prom already recorded in metadata table for recorder in recorders: recorder.record_viewer_data(viewer_data) + + +def record_system_options(problem): + """ + Record the system options for all systems in the model. + + Parameters + ---------- + problem : Problem + The problem for which all its systems' options are to be recorded. + """ + # get all recorders in the problem + recorders = set(_get_all_recorders(problem)) + if recorders: + for recorder in recorders: + for sub in problem.model.system_iter(recurse=True, include_self=True): + recorder.record_metadata_system(sub) diff --git a/openmdao/recorders/sqlite_recorder.py b/openmdao/recorders/sqlite_recorder.py index 2f4864474c..c71d080574 100644 --- a/openmdao/recorders/sqlite_recorder.py +++ b/openmdao/recorders/sqlite_recorder.py @@ -650,10 +650,6 @@ def record_metadata_system(self, recording_requester): pickled_metadata = sqlite3.Binary(pickled_metadata) with self.connection as c: - # Because we can have a recorder attached to multiple Systems, - # and because we are now recording System metadata recursively, - # we can store System metadata multiple times. Need to ignore when that happens - # so we don't get database errors. So use OR IGNORE c.execute("INSERT OR IGNORE INTO system_metadata" "(id, scaling_factors, component_metadata) " "VALUES(?,?,?)", (path, scaling_factors, pickled_metadata)) diff --git a/openmdao/recorders/tests/test_sqlite_recorder.py b/openmdao/recorders/tests/test_sqlite_recorder.py index ffdd6af1bc..208a39d629 100644 --- a/openmdao/recorders/tests/test_sqlite_recorder.py +++ b/openmdao/recorders/tests/test_sqlite_recorder.py @@ -463,41 +463,65 @@ def test_system_record_model_metadata(self): value = cr.system_options['root']['component_options']['assembled_jac_type'] self.assertEqual(value, 'csc') # quick check only. Too much to check exhaustively - def test_driver_record_model_metadata(self): + def test_record_system_options(self): + # Regardless what object the case recorder is attached to, system options + # should be recorded for all systems in the model + + expected_system_options_keys = ['root', 'px', 'pz', 'd1', 'd2', 'obj_cmp', 'con_cmp1', 'con_cmp2'] + + # Recorder on Driver prob = om.Problem(model=SellarDerivatives()) prob.setup() - - recorder = om.SqliteRecorder("cases.sql") + recorder = om.SqliteRecorder("cases_driver.sql") prob.driver.add_recorder(recorder) - prob.set_solver_print(level=0) prob.run_model() prob.cleanup() - - cr = om.CaseReader("cases.sql") + cr = om.CaseReader("cases_driver.sql") # Quick check to see that keys and values were recorded - for key in ['root', 'px', 'pz', 'd1', 'd2', 'obj_cmp', 'con_cmp1', 'con_cmp2']: - self.assertTrue(key in cr.system_options.keys()) + self.assertEqual(sorted(expected_system_options_keys), sorted(cr.system_options.keys())) + value = cr.system_options['root']['component_options']['assembled_jac_type'] + self.assertEqual('csc', value) # quick check only. Too much to check exhaustively + # Recorder on Problem + prob = om.Problem(model=SellarDerivatives()) + prob.setup() + recorder = om.SqliteRecorder("cases_problem.sql") + prob.add_recorder(recorder) + prob.set_solver_print(level=0) + prob.run_model() + prob.cleanup() + cr = om.CaseReader("cases_problem.sql") + # Quick check to see that keys and values were recorded + self.assertEqual(sorted(expected_system_options_keys), sorted(cr.system_options.keys())) value = cr.system_options['root']['component_options']['assembled_jac_type'] self.assertEqual(value, 'csc') # quick check only. Too much to check exhaustively - def test_driver_record_metadata(self): + # Recorder on a subsystem prob = om.Problem(model=SellarDerivatives()) prob.setup() - - recorder = om.SqliteRecorder("cases.sql") - prob.driver.add_recorder(recorder) - + recorder = om.SqliteRecorder("cases_subsystem.sql") + prob.model.d1.add_recorder(recorder) prob.set_solver_print(level=0) prob.run_model() prob.cleanup() - - cr = om.CaseReader("cases.sql") + cr = om.CaseReader("cases_subsystem.sql") # Quick check to see that keys and values were recorded - for key in ['root', 'px', 'pz', 'd1', 'd2', 'obj_cmp', 'con_cmp1', 'con_cmp2']: - self.assertTrue(key in cr.system_options.keys()) + self.assertEqual(sorted(expected_system_options_keys), sorted(cr.system_options.keys())) + value = cr.system_options['root']['component_options']['assembled_jac_type'] + self.assertEqual(value, 'csc') # quick check only. Too much to check exhaustively + # Recorder on a solver + prob = om.Problem(model=SellarDerivatives()) + prob.setup() + recorder = om.SqliteRecorder("cases_solver.sql") + prob.model.nonlinear_solver.add_recorder(recorder) + prob.set_solver_print(level=0) + prob.run_model() + prob.cleanup() + cr = om.CaseReader("cases_solver.sql") + # Quick check to see that keys and values were recorded + self.assertEqual(sorted(expected_system_options_keys), sorted(cr.system_options.keys())) value = cr.system_options['root']['component_options']['assembled_jac_type'] self.assertEqual(value, 'csc') # quick check only. Too much to check exhaustively