Skip to content

Commit

Permalink
Merge pull request #3029 from swryan/3023_n2_submodel
Browse files Browse the repository at this point in the history
Fixed the `openmdao n2` command to work properly with subproblems
  • Loading branch information
swryan committed Sep 27, 2023
2 parents eca4a09 + c69829d commit 2631042
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 129 deletions.
19 changes: 19 additions & 0 deletions openmdao/docs/openmdao_book/other_useful_docs/om_command.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,25 @@
"om.n2(p)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```{note}\n",
"To make use of the `--problem` argument, it is helpful to give your subproblem a meaningful name when it is instantiated, which you can use to identify it on the command line. \n",
"\n",
"For example:\n",
"\n",
" subprob = om.Problem(name='subproblem1')\n",
"\n",
" subcomp = om.SubmodelComp(problem=subprob)\n",
"\n",
"Then:\n",
"\n",
" openmdao n2 --problem subproblem1\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down
8 changes: 0 additions & 8 deletions openmdao/utils/reports_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
_reports_registry = {}
_default_reports = ['scaling', 'total_coloring', 'n2', 'optimizer', 'inputs']
_active_reports = set() # these reports will actually run (assuming their hook funcs are triggered)
_cmdline_reports = set() # cmdline reports registered here so default reports aren't modified
_reports_dir = os.environ.get('OPENMDAO_REPORTS_DIR', './reports') # top dir for the reports
_plugins_loaded = False # use this to ensure plugins only loaded once

Expand Down Expand Up @@ -124,11 +123,6 @@ def __getattr__(self, name):
raise AttributeError(f"Attribute '{name}' not found.")


def _register_cmdline_report(name):
global _cmdline_reports
_cmdline_reports.add(name)


def reports_active():
"""
Return True if reports are active globally.
Expand Down Expand Up @@ -252,8 +246,6 @@ def activate_report(name, instance=None):
if name not in _reports_registry:
issue_warning(f"No report with the name '{name}' is registered.")
return
if name in _cmdline_reports:
return # skip it if it's already being run from the command line

if not reports_active():
return
Expand Down
71 changes: 40 additions & 31 deletions openmdao/visualization/n2_viewer/n2_viewer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Code for generating N2 diagram."""
import inspect
import os
import sys
import pathlib
from operator import itemgetter

Expand Down Expand Up @@ -32,6 +33,7 @@
from openmdao import __version__ as openmdao_version

_MAX_ARRAY_SIZE_FOR_REPR_VAL = 1000 # If var has more elements than this do not pass to N2
_MAX_OPTION_SIZE = int(1e4) # If option value is bigger than this do not pass to N2

_default_n2_filename = 'n2.html'

Expand Down Expand Up @@ -162,11 +164,8 @@ def _get_var_dict(system, typ, name, is_parallel, is_implicit, values):
# which could be remote under MPI
_get_array_info(system, vec, name, prom, var_dict, from_src=False)

else:
var_dict['val'] = None
except Exception as err:
issue_warning(str(err))
var_dict['val'] = None
else: # discrete
meta = system._var_discrete[typ][name]
val = meta['val']
Expand All @@ -179,8 +178,6 @@ def _get_var_dict(system, typ, name, is_parallel, is_implicit, values):
if values:
if MPI is None or isinstance(val, (int, str, list, dict, complex, np.ndarray)):
var_dict['val'] = default_noraise(system.get_val(name))
else:
var_dict['val'] = None

if 'surrogate_name' in meta:
var_dict['surrogate_name'] = meta['surrogate_name']
Expand Down Expand Up @@ -209,9 +206,13 @@ def _serialize_single_option(option):
return 'Not Recordable'

val = option['val']

if val is _UNDEFINED:
return str(val)

if sys.getsizeof(val) > _MAX_OPTION_SIZE:
return 'Too Large to Display'

return default_noraise(val)


Expand Down Expand Up @@ -462,7 +463,7 @@ def set_values(children, stack, case):
stack.pop()
elif child['type'] == 'input':
if case is None:
child['val'] = None
child.pop('val')
for key in ['val_min', 'val_max', 'val_min_indices', 'val_max_indices']:
del child[key]
elif case.inputs is None:
Expand All @@ -472,7 +473,7 @@ def set_values(children, stack, case):
child['val'] = case.inputs[path]
elif child['type'] == 'output':
if case is None:
child['val'] = None
child.pop('val')
for key in ['val_min', 'val_max', 'val_min_indices', 'val_max_indices']:
del child[key]
elif case.outputs is None:
Expand Down Expand Up @@ -732,8 +733,8 @@ def _n2_setup_parser(parser):
"""
parser.add_argument('file', nargs=1,
help='Python script or recording containing the model. '
'If metadata from a parallel run was recorded in a separate file, '
'specify both database filenames delimited with a comma.')
'If metadata from a parallel run was recorded in a separate file, '
'specify both database filenames delimited with a comma.')
parser.add_argument('-o', default=_default_n2_filename, action='store', dest='outfile',
help='html output file.')
parser.add_argument('--no_values', action='store_true', dest='no_values',
Expand All @@ -742,10 +743,12 @@ def _n2_setup_parser(parser):
help="don't display in a browser.")
parser.add_argument('--embed', action='store_true', dest='embeddable',
help="create embeddable version.")
parser.add_argument('--title', default=None,
action='store', dest='title', help='diagram title.')
parser.add_argument('--path', default=None,
action='store', dest='path', help='initial path to zoom into.')
parser.add_argument('--title', default=None, action='store', dest='title',
help='diagram title.')
parser.add_argument('--path', default=None, action='store', dest='path',
help='initial system path to zoom into.')
parser.add_argument('--problem', default=None, action='store', dest='problem_name',
help='name of sub-problem, if target is a sub-problem')


def _n2_cmd(options, user_args):
Expand All @@ -760,29 +763,35 @@ def _n2_cmd(options, user_args):
Command line options after '--' (if any). Passed to user script.
"""
filename = _to_filename(options.file[0])
probname = options.problem_name

if filename.endswith('.py'):
# disable the reports system, we only want the N2 report and then we exit
os.environ['OPENMDAO_REPORTS'] = '0'

def _view_model_w_errors(prob):
errs = prob._metadata['saved_errors']
if errs:
# only run the n2 here if we've had setup errors. Normally we'd wait until
# after final_setup in order to have correct values for all of the I/O variables.
n2(prob, outfile=options.outfile, show_browser=not options.no_browser,
values=not options.no_values, title=options.title, path=options.path,
embeddable=options.embeddable)
# errors will result in exit at the end of the _check_collected_errors method

def _view_model_no_errors(prob):
n2(prob, outfile=options.outfile, show_browser=not options.no_browser,
values=not options.no_values, title=options.title, path=options.path,
embeddable=options.embeddable)
# if problem name is not specified, use top-level problem (no delimiter in pathname)
pathname = prob._metadata['pathname']
if (probname is None and '/' not in pathname) or (probname == prob._name):
errs = prob._metadata['saved_errors']
if errs:
# only run the n2 here if we've had setup errors. Normally we'd wait until
# after final_setup in order to have correct values for all of the variables.
n2(prob, outfile=options.outfile, show_browser=not options.no_browser,
values=not options.no_values, title=options.title, path=options.path,
embeddable=options.embeddable)
# errors will result in exit at the end of the _check_collected_errors method
else:
# no errors, generate n2 after final_setup
def _view_model_no_errors(prob):
n2(prob, outfile=options.outfile, show_browser=not options.no_browser,
values=not options.no_values, title=options.title, path=options.path,
embeddable=options.embeddable)
hooks._register_hook('final_setup', 'Problem',
post=_view_model_no_errors, exit=True)
hooks._setup_hooks(prob)

hooks._register_hook('_check_collected_errors', 'Problem', pre=_view_model_w_errors)
hooks._register_hook('final_setup', 'Problem', post=_view_model_no_errors, exit=True)

from openmdao.utils.reports_system import _register_cmdline_report
# tell report system not to duplicate effort
_register_cmdline_report('n2')

_load_and_exec(options.file[0], user_args)
else:
Expand Down
5 changes: 0 additions & 5 deletions openmdao/visualization/n2_viewer/tests/gen_gui_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@

from openmdao.utils.gui_testing_utils import _GuiTestCase

try:
from parameterized import parameterized
except ImportError:
from openmdao.utils.assert_utils import SkipParameterized as parameterized

# set DEBUG to True if you want to view the generated HTML file
GUI_DIAG_SUFFIX = '_GEN_TEST.html'
GUI_TEST_SUBDIR = 'gui_test_models'
Expand Down

0 comments on commit 2631042

Please sign in to comment.