Skip to content

Commit

Permalink
Merge pull request #2834 from nsteffen/i2822
Browse files Browse the repository at this point in the history
Added capability to `list_problem_vars` to return problem var data and suppress `out_stream`
  • Loading branch information
swryan committed Mar 15, 2023
2 parents cc7f739 + 8e0223b commit d0aabfb
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 35 deletions.
92 changes: 70 additions & 22 deletions openmdao/core/problem.py
Expand Up @@ -13,7 +13,7 @@
from fnmatch import fnmatchcase
from itertools import product

from io import StringIO
from io import StringIO, TextIOBase

import numpy as np
import scipy.sparse as sparse
Expand Down Expand Up @@ -1825,6 +1825,7 @@ def list_problem_vars(self,
desvar_opts=[],
cons_opts=[],
objs_opts=[],
out_stream=_DEFAULT_OUT_STREAM
):
"""
Print all design variables and responses (objectives and constraints).
Expand Down Expand Up @@ -1858,6 +1859,15 @@ def list_problem_vars(self,
Allowed values are:
['ref', 'ref0', 'indices', 'adder', 'scaler', 'units',
'parallel_deriv_color', 'cache_linear_solution'].
out_stream : file-like object
Where to send human readable output. Default is sys.stdout.
Set to None to suppress.
Returns
-------
dict
Name, size, val, and other requested parameters of design variables, constraints,
and objectives.
"""
if self._metadata['setup_status'] < _SetupStatus.POST_FINAL_SETUP:
raise RuntimeError(f"{self.msginfo}: Problem.list_problem_vars() cannot be called "
Expand All @@ -1873,10 +1883,17 @@ def list_problem_vars(self,
def_desvar_opts = [opt for opt in ('indices',) if opt not in desvar_opts and
_find_dict_meta(desvars, opt)]
col_names = default_col_names + def_desvar_opts + desvar_opts
self._write_var_info_table(header, col_names, desvars, vals,
show_promoted_name=show_promoted_name,
print_arrays=print_arrays,
col_spacing=2)
if out_stream:
self._write_var_info_table(header, col_names, desvars, vals,
show_promoted_name=show_promoted_name,
print_arrays=print_arrays,
col_spacing=2)

des_vars = [[i, j] for i, j in desvars.items()]
for d in des_vars:
d[1] = {i: j for i, j in d[1].items() if i in col_names}
d[1]['val'] = vals[d[0]]
des_vars = [tuple(d) for d in des_vars]

# Constraints
cons = self.driver._cons
Expand All @@ -1886,24 +1903,45 @@ def list_problem_vars(self,
def_cons_opts = [opt for opt in ('indices', 'alias') if opt not in cons_opts and
_find_dict_meta(cons, opt)]
col_names = default_col_names + def_cons_opts + cons_opts
self._write_var_info_table(header, col_names, cons, vals,
show_promoted_name=show_promoted_name,
print_arrays=print_arrays,
col_spacing=2)
if out_stream:
self._write_var_info_table(header, col_names, cons, vals,
show_promoted_name=show_promoted_name,
print_arrays=print_arrays,
col_spacing=2)

cons_vars = [[i, j] for i, j in cons.items()]
for c in cons_vars:
c[1] = {i: j for i, j in c[1].items() if i in col_names}
c[1]['val'] = vals[c[0]]
cons_vars = [tuple(c) for c in cons_vars]

objs = self.driver._objs
vals = self.driver.get_objective_values(driver_scaling=driver_scaling)
header = "Objectives"
def_obj_opts = [opt for opt in ('indices',) if opt not in objs_opts and
_find_dict_meta(objs, opt)]
col_names = default_col_names + def_obj_opts + objs_opts
self._write_var_info_table(header, col_names, objs, vals,
show_promoted_name=show_promoted_name,
print_arrays=print_arrays,
col_spacing=2)
if out_stream:
self._write_var_info_table(header, col_names, objs, vals,
show_promoted_name=show_promoted_name,
print_arrays=print_arrays,
col_spacing=2)

obj_vars = [[i, j] for i, j in objs.items()]
for o in obj_vars:
o[1] = {i: j for i, j in o[1].items() if i in col_names}
o[1]['val'] = vals[o[0]]
obj_vars = [tuple(o) for o in obj_vars]

prob_vars = {'design_vars': des_vars,
'constraints': cons_vars,
'objectives': obj_vars}

return prob_vars

def _write_var_info_table(self, header, col_names, meta, vals, print_arrays=False,
show_promoted_name=True, col_spacing=1):
show_promoted_name=True, col_spacing=1,
out_stream=_DEFAULT_OUT_STREAM):
"""
Write a table of information for the problem variable in meta and vals.
Expand All @@ -1927,7 +1965,17 @@ def _write_var_info_table(self, header, col_names, meta, vals, print_arrays=Fals
If True, then show the promoted names of the variables.
col_spacing : int
Number of spaces between columns in the table.
out_stream : file-like object
Where to send human readable output. Default is sys.stdout.
Set to None to suppress.
"""
if out_stream is None:
return
elif out_stream is _DEFAULT_OUT_STREAM:
out_stream = sys.stdout
elif not isinstance(out_stream, TextIOBase):
raise TypeError("Invalid output stream specified for 'out_stream'")

abs2prom = self.model._var_abs2prom

# Gets the current numpy print options for consistent decimal place
Expand Down Expand Up @@ -1970,7 +2018,7 @@ def _write_var_info_table(self, header, col_names, meta, vals, print_arrays=Fals
rows.append(row)

col_space = ' ' * col_spacing
print(add_border(header, '-'))
print(add_border(header, '-'), file=out_stream)

# loop through the rows finding the max widths
max_width = {}
Expand All @@ -1992,8 +2040,8 @@ def _write_var_info_table(self, header, col_names, meta, vals, print_arrays=Fals
for col_name in col_names:
header_div += '-' * max_width[col_name] + col_space
header_col_names += pad_name(col_name, max_width[col_name], quotes=False) + col_space
print(header_col_names)
print(header_div[:-1])
print(header_col_names, file=out_stream)
print(header_div[:-1], file=out_stream)

# print rows with var info
for row in rows:
Expand All @@ -2008,16 +2056,16 @@ def _write_var_info_table(self, header, col_names, meta, vals, print_arrays=Fals
else:
out = str(cell)
row_string += pad_name(out, max_width[col_name], quotes=False) + col_space
print(row_string)
print(row_string, file=out_stream)

if print_arrays:
spaces = (max_width['name'] + col_spacing) * ' '
for col_name in have_array_values:
print(f"{spaces}{col_name}:")
print(textwrap.indent(pprint.pformat(row[col_name]), spaces))
print()
print(f"{spaces}{col_name}:", file=out_stream)
print(textwrap.indent(pprint.pformat(row[col_name]), spaces), file=out_stream)
print(file=out_stream)

print()
print(file=out_stream)

def load_case(self, case):
"""
Expand Down
59 changes: 46 additions & 13 deletions openmdao/core/tests/test_problem.py
Expand Up @@ -1808,19 +1808,19 @@ def test_list_problem_vars(self):
strout = StringIO()
sys.stdout = strout
try:
prob.list_problem_vars(print_arrays=True,
desvar_opts=['lower', 'upper', 'ref', 'ref0',
'indices', 'adder', 'scaler',
'parallel_deriv_color',
'cache_linear_solution'],
cons_opts=['lower', 'upper', 'equals', 'ref', 'ref0',
'indices', 'adder', 'scaler', 'linear',
'parallel_deriv_color',
'cache_linear_solution'],
objs_opts=['ref', 'ref0',
'indices', 'adder', 'scaler',
'parallel_deriv_color',
'cache_linear_solution'],
l = prob.list_problem_vars(print_arrays=True,
desvar_opts=['lower', 'upper', 'ref', 'ref0',
'indices', 'adder', 'scaler',
'parallel_deriv_color',
'cache_linear_solution'],
cons_opts=['lower', 'upper', 'equals', 'ref', 'ref0',
'indices', 'adder', 'scaler', 'linear',
'parallel_deriv_color',
'cache_linear_solution'],
objs_opts=['ref', 'ref0',
'indices', 'adder', 'scaler',
'parallel_deriv_color',
'cache_linear_solution'],
)
finally:
sys.stdout = stdout
Expand All @@ -1832,6 +1832,39 @@ def test_list_problem_vars(self):
self.assertRegex(output[12], r'^\s+upper:')
self.assertRegex(output[13], r'^\s+array+\(+\[[0-9., e+-]+\]+\)')

# design vars
self.assertEquals(l['design_vars'][0][1]['name'], 'z')
self.assertEquals(l['design_vars'][0][1]['size'], 2)
assert(all(l['design_vars'][0][1]['val'] == prob.get_val('z')))
self.assertEquals(l['design_vars'][0][1]['scaler'], None)
self.assertEquals(l['design_vars'][0][1]['adder'], None)

self.assertEquals(l['design_vars'][1][1]['name'], 'x')
self.assertEquals(l['design_vars'][1][1]['size'], 1)
assert(all(l['design_vars'][1][1]['val'] == prob.get_val('x')))
self.assertEquals(l['design_vars'][1][1]['scaler'], None)
self.assertEquals(l['design_vars'][1][1]['adder'], None)

# constraints
self.assertEquals(l['constraints'][0][1]['name'], 'con1')
self.assertEquals(l['constraints'][0][1]['size'], 1)
assert(all(l['constraints'][0][1]['val'] == prob.get_val('con1')))
self.assertEquals(l['constraints'][0][1]['scaler'], None)
self.assertEquals(l['constraints'][0][1]['adder'], None)

self.assertEquals(l['constraints'][1][1]['name'], 'con2')
self.assertEquals(l['constraints'][1][1]['size'], 1)
assert(all(l['constraints'][1][1]['val'] == prob.get_val('con2')))
self.assertEquals(l['constraints'][1][1]['scaler'], None)
self.assertEquals(l['constraints'][1][1]['adder'], None)

# objectives
self.assertEquals(l['objectives'][0][1]['name'], 'obj')
self.assertEquals(l['objectives'][0][1]['size'], 1)
assert(all(l['objectives'][0][1]['val'] == prob.get_val('obj')))
self.assertEquals(l['objectives'][0][1]['scaler'], None)
self.assertEquals(l['objectives'][0][1]['adder'], None)

def test_list_problem_vars_before_final_setup(self):
prob = om.Problem()
prob.model.add_subsystem('parab', Paraboloid(), promotes_inputs=['x', 'y'])
Expand Down

0 comments on commit d0aabfb

Please sign in to comment.