Skip to content

Commit

Permalink
Merge pull request #1635 from DKilkenny/run_driver_case_recorder
Browse files Browse the repository at this point in the history
Fix for CaseRecorder overwriting model options if run driver called twice
  • Loading branch information
swryan committed Sep 4, 2020
2 parents 66d3dc7 + c1c2d9d commit 82a029e
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 48 deletions.
7 changes: 7 additions & 0 deletions openmdao/core/problem.py
Expand Up @@ -117,6 +117,8 @@ class Problem(object):
A flag to indicate whether the system options for all the systems have been recorded
_metadata : dict
Problem level metadata.
_run_counter : int
The number of times run_driver or run_model has been called.
"""

def __init__(self, model=None, driver=None, comm=None, name=None, **options):
Expand Down Expand Up @@ -170,6 +172,7 @@ def __init__(self, model=None, driver=None, comm=None, name=None, **options):
self._initial_condition_cache = {}

self._metadata = None
self._run_counter = -1
self._system_options_recorded = False
self._rec_mgr = RecordingManager()

Expand Down Expand Up @@ -598,6 +601,8 @@ def run_model(self, case_prefix=None, reset_iter_counts=True):
self.driver.iter_count = 0
self.model._reset_iter_counts()

self._run_counter += 1

self.final_setup()
self.model._clear_iprint()
self.model.run_solve_nonlinear()
Expand Down Expand Up @@ -634,6 +639,8 @@ def run_driver(self, case_prefix=None, reset_iter_counts=True):
self.driver.iter_count = 0
self.model._reset_iter_counts()

self._run_counter += 1

self.final_setup()
self.model._clear_iprint()
return self.driver.run()
Expand Down
9 changes: 3 additions & 6 deletions openmdao/docs/features/recording/reading_metadata.rst
Expand Up @@ -24,13 +24,10 @@ 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']`
- :code:`cr.list_model_options()`

This function creates a dictionary of the latest model options available that were set in the model.

The component options include user-defined options that were defined
through the :code:`system.options.declare` method. By default, everything in options is
pickled and recorded. If there are options that cannot be pickled or you simply do not wish
to record, they can be excluded using the 'options_excludes' recording option on the system.

.. embed-code::
openmdao.recorders.tests.test_sqlite_recorder.TestFeatureSqliteRecorder.test_feature_recording_system_options
Expand Down
24 changes: 19 additions & 5 deletions openmdao/recorders/base_case_reader.py
Expand Up @@ -17,7 +17,7 @@ class BaseCaseReader(object):
Metadata about the problem, including the system hierachy and connections.
solver_metadata : dict
The solver options for each solver in the recorded model.
system_options : dict
_system_options : dict
Metadata about each system in the recorded model, including options and scaling factors.
"""

Expand All @@ -35,7 +35,21 @@ def __init__(self, filename, pre_load=False):
self._format_version = None
self.problem_metadata = {}
self.solver_metadata = {}
self.system_options = {}
self._system_options = {}

@property
def system_options(self):
"""
Provide '_system_options' property for backwards compatibility.
Returns
-------
dict
reference to the _system_options attribute.
"""
warn_deprecation("The system_options attribute is deprecated. "
"Use `list_model_options` instead.")
return self._system_options

@property
def system_metadata(self):
Expand All @@ -45,11 +59,11 @@ def system_metadata(self):
Returns
-------
dict
reference to the 'system_options' attribute.
reference to the '_system_options' attribute.
"""
warn_deprecation("The BaseCaseReader.system_metadata attribute is deprecated. "
"Use the BaseCaseReader.system_option attribute instead.")
return self.system_options
"Use `list_model_options` instead.")
return self._system_options

def get_cases(self, source, recurse=True, flat=False):
"""
Expand Down
5 changes: 4 additions & 1 deletion openmdao/recorders/recording_manager.py
Expand Up @@ -270,4 +270,7 @@ def record_system_options(problem):

for recorder in recorders:
for sub in problem.model.system_iter(recurse=True, include_self=True):
recorder.record_metadata_system(sub)
if problem._run_counter >= 1:
recorder.record_metadata_system(sub, problem._run_counter)
else:
recorder.record_metadata_system(sub)
56 changes: 51 additions & 5 deletions openmdao/recorders/sqlite_reader.py
Expand Up @@ -33,7 +33,7 @@ class SqliteCaseReader(BaseCaseReader):
Metadata about the problem, including the system hierachy and connections.
solver_metadata : dict
The solver options for each solver in the recorded model.
system_options : dict
_system_options : dict
Metadata about each system in the recorded model, including options and scaling factors.
_format_version : int
The version of the format assumed when loading the file.
Expand Down Expand Up @@ -107,7 +107,7 @@ def __init__(self, filename, pre_load=False):

# collect data from the system_metadata table. this includes:
# component metadata and scaling factors for each system,
# which is added to system_options
# which is added to _system_options
self._collect_system_metadata(cur)

# collect data from the solver_metadata table. this includes:
Expand Down Expand Up @@ -262,10 +262,10 @@ def _collect_system_metadata(self, cur):
cur.execute("SELECT id, scaling_factors, component_metadata FROM system_metadata")
for row in cur:
id = row[0]
self.system_options[id] = {}
self._system_options[id] = {}

self.system_options[id]['scaling_factors'] = pickle.loads(row[1])
self.system_options[id]['component_options'] = pickle.loads(row[2])
self._system_options[id]['scaling_factors'] = pickle.loads(row[1])
self._system_options[id]['component_options'] = pickle.loads(row[2])

def _collect_solver_metadata(self, cur):
"""
Expand Down Expand Up @@ -409,6 +409,52 @@ def list_source_vars(self, source, out_stream=_DEFAULT_OUT_STREAM):

return dct

def list_model_options(self, run_counter=None, out_stream=_DEFAULT_OUT_STREAM):
"""
List of all model options.
Parameters
----------
run_counter : int or None
Run_driver or run_model iteration to inspect
out_stream : file-like object
Where to send human readable output. Default is sys.stdout.
Set to None to suppress.
Returns
-------
dict
{'root':{key val}}
"""
if out_stream:
if out_stream is _DEFAULT_OUT_STREAM:
out_stream = sys.stdout

dct = {}

for i in self._system_options:
if '_' in i:
subsys, num = i.rsplit('_', 1)
else:
subsys = i
num = 0

if (run_counter is not None and run_counter == int(num) and subsys == 'root') or \
(subsys == 'root' and run_counter is None):

out_stream.write(
'Run Number: {}\n Subsystem: {}'.format(num, subsys))

for j in self._system_options[i]['component_options']:
option = "{0} : {1}".format(
j, self._system_options[i]['component_options'][j])
out_stream.write('\n {}\n'.format(option))

dct[subsys] = {}
dct[subsys][j] = self._system_options[i]['component_options'][j]

return dct

def list_cases(self, source=None, recurse=True, flat=True, out_stream=_DEFAULT_OUT_STREAM):
"""
Iterate over Driver, Solver and System cases in order.
Expand Down
12 changes: 10 additions & 2 deletions openmdao/recorders/sqlite_recorder.py
Expand Up @@ -622,16 +622,19 @@ def record_viewer_data(self, model_viewer_data, key='Driver'):
except sqlite3.IntegrityError:
print("Model viewer data has already has already been recorded for %s." % key)

def record_metadata_system(self, recording_requester):
def record_metadata_system(self, recording_requester, run_counter=None):
"""
Record system metadata.
Parameters
----------
recording_requester : System
The System that would like to record its metadata.
run_counter : int or None
The number of times run_driver or run_model has been called.
"""
if self.connection:

scaling_vecs, user_options = self._get_metadata_system(recording_requester)

if scaling_vecs is None:
Expand Down Expand Up @@ -663,10 +666,15 @@ def record_metadata_system(self, recording_requester):
# the current OpenMDAO code will call this function each time and there will be
# SQL errors for "UNIQUE constraint failed: system_metadata.id"
# Future versions of OpenMDAO will handle this better.
if run_counter is None:
name = path
else:
name = "{}_{}".format(path, str(run_counter))
with self.connection as c:
c.execute("INSERT OR IGNORE INTO system_metadata"
"(id, scaling_factors, component_metadata) "
"VALUES(?,?,?)", (path, scaling_factors, pickled_metadata))
"VALUES(?,?,?)", (name, scaling_factors,
pickled_metadata))

def record_metadata_solver(self, recording_requester):
"""
Expand Down
21 changes: 17 additions & 4 deletions openmdao/recorders/tests/test_sqlite_reader.py
Expand Up @@ -2006,7 +2006,7 @@ def test_system_options_pickle_fail(self):

prob.cleanup()
cr = om.CaseReader(self.filename)
subs_options = cr.system_options['subs']['component_options']
subs_options = cr._system_options['subs']['component_options']

# no options should have been recorded for d1
self.assertEqual(len(subs_options._dict), 0)
Expand Down Expand Up @@ -2042,7 +2042,7 @@ def test_pre_load(self):

self.assertEqual(cr._format_version, format_version)

self.assertEqual(set(cr.system_options.keys()),
self.assertEqual(set(cr._system_options.keys()),
set(['root'] + [sys.name for sys in prob.model._subsystems_allprocs]))

self.assertEqual(set(cr.problem_metadata.keys()), {
Expand Down Expand Up @@ -2070,7 +2070,7 @@ def test_pre_load(self):

self.assertEqual(cr._format_version, format_version)

self.assertEqual(set(cr.system_options.keys()),
self.assertEqual(set(cr._system_options.keys()),
set(['root'] + [sys.name for sys in prob.model._subsystems_allprocs]))

self.assertEqual(set(cr.problem_metadata.keys()), {
Expand Down Expand Up @@ -2854,10 +2854,23 @@ def test_system_metadata_attribute_deprecated(self):

cr = om.CaseReader(self.filename)
msg = "The BaseCaseReader.system_metadata attribute is deprecated. " \
"Use the BaseCaseReader.system_option attribute instead."
"Use `list_model_options` instead."
with assert_warning(DeprecationWarning, msg):
options = cr.system_metadata

def test_system_options_attribute_deprecated(self):
model = om.Group()
model.add_recorder(self.recorder)
prob = om.Problem(model)
prob.setup()
prob.run_model()
prob.cleanup()

cr = om.CaseReader(self.filename)
msg = "The system_options attribute is deprecated. Use `list_model_options` instead."
with assert_warning(DeprecationWarning, msg):
options = cr.system_options

def test_sqlite_reader_problem_derivatives(self):

class UglyParaboloid(om.ExplicitComponent):
Expand Down

0 comments on commit 82a029e

Please sign in to comment.