From a7c661da111bb3801cab965b99b709035812d4e1 Mon Sep 17 00:00:00 2001 From: Herb Schilling Date: Thu, 11 Jan 2018 09:43:12 -0500 Subject: [PATCH 01/10] Initial version --- openmdao/core/driver.py | 81 ++++++++++ ...test_distrib_driver_debug_print_options.py | 140 ++++++++++++++++++ openmdao/core/tests/test_driver.py | 58 ++++++++ .../debugging/debugging_drivers.rst | 30 ++++ openmdao/drivers/pyoptsparse_driver.py | 13 ++ openmdao/drivers/scipy_optimizer.py | 4 + .../drivers/tests/test_pyoptsparse_driver.py | 50 +++++++ .../drivers/tests/test_scipy_optimizer.py | 86 +++++++++++ 8 files changed, 462 insertions(+) create mode 100644 openmdao/core/tests/test_distrib_driver_debug_print_options.py create mode 100644 openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst diff --git a/openmdao/core/driver.py b/openmdao/core/driver.py index 6b149eb5d6..3ab3fbd65a 100644 --- a/openmdao/core/driver.py +++ b/openmdao/core/driver.py @@ -11,6 +11,7 @@ from openmdao.recorders.recording_iteration_stack import Recording from openmdao.utils.record_util import create_local_meta, check_path from openmdao.utils.mpi import MPI +from openmdao.recorders.recording_iteration_stack import get_formatted_iteration_coordinate from openmdao.utils.options_dictionary import OptionsDictionary @@ -35,6 +36,12 @@ class Driver(object): recording_options['excludes'] : list of strings('') Patterns for variables to exclude in recording (processed after includes). + debug_print + debug_print_desvars + debug_print_nl_con + debug_print_ln_con + debug_print_objective + Attributes ---------- fail : bool @@ -142,6 +149,14 @@ def __init__(self): self.supports.declare('active_set', types=bool, default=False) self.supports.declare('simultaneous_derivatives', types=bool, default=False) + # Debug printing. + self.debug_print = OptionsDictionary() + self.debug_print.declare('debug_print', types=bool, default=False) + self.debug_print.declare('debug_print_desvars', types=bool, default=False) + self.debug_print.declare('debug_print_nl_con', types=bool, default=False) + self.debug_print.declare('debug_print_ln_con', types=bool, default=False) + self.debug_print.declare('debug_print_objective', types=bool, default=False) + self.iter_count = 0 self.metadata = None self._model_viewer_data = None @@ -554,9 +569,14 @@ def run(self): boolean Failure flag; True if failed to converge, False is successful. """ + + self._pre_run_model_debug_print() + with Recording(self._get_name(), self.iter_count, self) as rec: failure_flag = self._problem.model._solve_nonlinear() + self._post_run_model_debug_print() + self.iter_count += 1 return failure_flag @@ -806,3 +826,64 @@ def _setup_simul_coloring(self, mode='fwd'): del dvdict[dv] dv = prom2abs[dv][0] dvdict[dv] = col_dict + + def _pre_run_model_debug_print(self): + """ + Set up metadata for simultaneous derivative solution. + """ + if self.debug_print['debug_print']: + if not MPI or MPI.COMM_WORLD.rank == 0: + header = 'Driver debug print for iter coord: {}'.format( + get_formatted_iteration_coordinate()) + print(header) + print(len(header)*'-') + + if self.debug_print['debug_print_desvars']: + desvar_vals = self.get_design_var_values() + if not MPI or MPI.COMM_WORLD.rank == 0: + print("Design Vars") + if desvar_vals: + for name, value in iteritems(desvar_vals): + print("{}: {}".format(name,value)) + else: + print("None") + print() + + def _post_run_model_debug_print(self): + """ + Set up metadata for simultaneous derivative solution. + """ + if self.debug_print['debug_print']: + if self.debug_print['debug_print_nl_con']: + cons = self.get_constraint_values(lintype='nonlinear') + if not MPI or MPI.COMM_WORLD.rank == 0: + print("Nonlinear constraints") + if cons: + for name, value in iteritems(cons): + print("{}: {}".format(name, value)) + else: + print("None") + print() + + if self.debug_print['debug_print_ln_con']: + cons = self.get_constraint_values(lintype='linear') + if not MPI or MPI.COMM_WORLD.rank == 0: + print("Linear constraints") + if cons: + for name, value in iteritems(cons): + print("{}: {}".format(name, value)) + else: + print("None") + print() + + if self.debug_print['debug_print_objective']: + objs = self.get_objective_values() + if not MPI or MPI.COMM_WORLD.rank == 0: + print("Objectives") + if objs: + for name, value in iteritems(objs): + print("{}: {}".format(name, value)) + else: + print("None") + print() + diff --git a/openmdao/core/tests/test_distrib_driver_debug_print_options.py b/openmdao/core/tests/test_distrib_driver_debug_print_options.py new file mode 100644 index 0000000000..465f533823 --- /dev/null +++ b/openmdao/core/tests/test_distrib_driver_debug_print_options.py @@ -0,0 +1,140 @@ +import os +import sys +import unittest +import numpy as np +from six.moves import cStringIO +from six import StringIO + +from openmdao.api import ExplicitComponent, Problem, Group, IndepVarComp + +from openmdao.utils.mpi import MPI +from openmdao.utils.array_utils import evenly_distrib_idxs + +try: + from openmdao.vectors.petsc_vector import PETScVector +except ImportError: + PETScVector = None + +from openmdao.utils.assert_utils import assert_rel_error + +@unittest.skipIf(PETScVector is None or os.environ.get("TRAVIS"), + "PETSc is required." if PETScVector is None + else "Unreliable on Travis CI.") +class DistributedDriverDebugPrintOptionsTest(unittest.TestCase): + + N_PROCS = 2 + + def test_distributed_driver_debug_print_options(self): + + from openmdao.utils.general_utils import set_pyoptsparse_opt + from openmdao.utils.mpi import MPI + + if MPI: + from openmdao.api import PETScVector + vector_class = PETScVector + else: + PETScVector = None + + # check that pyoptsparse is installed. if it is, try to use SLSQP. + OPT, OPTIMIZER = set_pyoptsparse_opt('SLSQP') + + if OPTIMIZER: + from openmdao.drivers.pyoptsparse_driver import pyOptSparseDriver + + from openmdao.core.parallel_group import ParallelGroup + from openmdao.components.exec_comp import ExecComp + + class Mygroup(Group): + + def setup(self): + self.add_subsystem('indep_var_comp', IndepVarComp('x'), promotes=['*']) + self.add_subsystem('Cy', ExecComp('y=2*x'), promotes=['*']) + self.add_subsystem('Cc', ExecComp('c=x+2'), promotes=['*']) + + self.add_design_var('x') + self.add_constraint('c', lower=-3.) + + prob = Problem() + + prob.model.add_subsystem('par', ParallelGroup()) + + prob.model.par.add_subsystem('G1', Mygroup()) + prob.model.par.add_subsystem('G2', Mygroup()) + + prob.model.add_subsystem('Obj', ExecComp('obj=y1+y2')) + + prob.model.connect('par.G1.y', 'Obj.y1') + prob.model.connect('par.G2.y', 'Obj.y2') + + prob.model.add_objective('Obj.obj') + + prob.driver = pyOptSparseDriver() + prob.driver.options['optimizer'] = 'SLSQP' + prob.driver.options['print_results'] = False + + prob.driver.debug_print['debug_print'] = True + prob.driver.debug_print['debug_print_desvars'] = True + prob.driver.debug_print['debug_print_nl_con'] = True + prob.driver.debug_print['debug_print_ln_con'] = True + prob.driver.debug_print['debug_print_objective'] = True + + prob.setup(vector_class=PETScVector) + + stdout = sys.stdout + strout = StringIO() + sys.stdout = strout + sys.stdout = strout + try: + prob.run_driver() + finally: + sys.stdout = stdout + + # prob.run_driver() + prob.cleanup() + + # stdout = sys.stdout + # strout = StringIO() + # sys.stdout = strout + # try: + # prob.run_driver() + # finally: + # sys.stdout = stdout + + output = strout.getvalue().split('\n') + if MPI.COMM_WORLD.rank == 0: + # Just make sure we have more than one. Not sure we will always have the same number + # of iterations + self.assertTrue(output.count("Design Vars") > 1, + "Should be more than one design vars header printed") + self.assertTrue(output.count("Nonlinear constraints") > 1, + "Should be more than one nonlinear constraint header printed") + self.assertTrue(output.count("Linear constraints") > 1, + "Should be more than one linear constraint header printed") + self.assertTrue(output.count("Objectives") > 1, + "Should be more than one objective header printed") + + self.assertTrue(len([s for s in output if s.startswith('par.G1.indep_var_comp.x')]) > 1, + "Should be more than one par.G1.indep_var_comp.x printed") + self.assertTrue(len([s for s in output if s.startswith('par.G2.indep_var_comp.x')]) > 1, + "Should be more than one par.G2.indep_var_comp.x printed") + self.assertTrue(len([s for s in output if s.startswith('par.G1.Cc.c')]) > 1, + "Should be more than one par.G1.Cc.c printed") + self.assertTrue(len([s for s in output if s.startswith('par.G2.Cc.c')]) > 1, + "Should be more than one par.G2.Cc.c printed") + self.assertTrue(len([s for s in output if s.startswith('None')]) > 1, + "Should be more than one None printed") + self.assertTrue(len([s for s in output if s.startswith('Obj.obj')]) > 1, + "Should be more than one Obj.obj printed") + # self.assertEqual(len([s for s in output if s.startswith('par.G1.indep_var_comp.x')]), 11) + # self.assertEqual(len([s for s in output if s.startswith('par.G2.indep_var_comp.x')]), 11) + # self.assertEqual(len([s for s in output if s.startswith('par.G1.Cc.c')]), 11) + # self.assertEqual(len([s for s in output if s.startswith('par.G2.Cc.c')]), 11) + # self.assertEqual(len([s for s in output if s.startswith('None')]), 11) + # self.assertEqual(len([s for s in output if s.startswith('Obj.obj')]), 11) + else: + self.assertEqual(output, ['']) + + +if __name__ == "__main__": + from openmdao.utils.mpi import mpirun_tests + mpirun_tests() diff --git a/openmdao/core/tests/test_driver.py b/openmdao/core/tests/test_driver.py index 112fbefc06..67d84f55e0 100644 --- a/openmdao/core/tests/test_driver.py +++ b/openmdao/core/tests/test_driver.py @@ -2,6 +2,8 @@ from __future__ import print_function +from six import StringIO +import sys import unittest import numpy as np @@ -190,5 +192,61 @@ def test_vector_scaled_derivs_diff_sizes(self): con_base = np.array([ (prob['comp.y2'][0]-1.2)/(2.0-1.2)]) assert_rel_error(self, con['comp.y2'], con_base, 1.0e-3) + def test_debug_print_option(self): + + prob = Problem() + prob.model = model = SellarDerivatives() + + model.add_design_var('z') + model.add_objective('obj') + model.add_constraint('con1', lower=0) + model.add_constraint('con2', lower=0, linear=True) + prob.set_solver_print(level=0) + + prob.setup(check=False) + + # Make sure nothing prints if debug is not turned on + stdout = sys.stdout + strout = StringIO() + sys.stdout = strout + try: + prob.run_driver() + finally: + sys.stdout = stdout + output = strout.getvalue().split('\n') + self.assertEqual(output, ['']) + + # Make sure nothing prints even if debug print is turned on + # but none of the individual options are turned on + prob.driver.debug_print['debug_print'] = True + stdout = sys.stdout + strout = StringIO() + sys.stdout = strout + try: + prob.run_driver() + finally: + sys.stdout = stdout + output = strout.getvalue().split('\n') + self.assertEqual(output[0], 'Driver debug print for iter coord: rank0:') + + # Make sure everything prints when all options are on + prob.driver.debug_print['debug_print_desvars'] = True + prob.driver.debug_print['debug_print_nl_con'] = True + prob.driver.debug_print['debug_print_ln_con'] = True + prob.driver.debug_print['debug_print_objective'] = True + stdout = sys.stdout + strout = StringIO() + sys.stdout = strout + try: + prob.run_driver() + finally: + sys.stdout = stdout + output = strout.getvalue().split('\n') + self.assertEqual(output.count("Design Vars"), 1) + self.assertEqual(output.count("Nonlinear constraints"), 1) + self.assertEqual(output.count("Linear constraints"), 1) + self.assertEqual(output.count("Objectives"), 1) + + if __name__ == "__main__": unittest.main() diff --git a/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst b/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst new file mode 100644 index 0000000000..5795bfb11f --- /dev/null +++ b/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst @@ -0,0 +1,30 @@ +.. _debugging-drivers: + +************************** +Driver Debug Print Options +************************** + +When working with a model, it may sometimes be helpful to print out the design variables, constraints, and +objectives as the `Driver` iterates. OpenMDAO provides options on the `Driver` to let you do that. + +Usage +----- + +*Debug Print Options* +~~~~~~~~~~~~~~~~~~~~~ + + + .. embed-test:: + openmdao.drivers.tests.test_scipy_optimizer.TestScipyOptimizerFeatures.test_debug_print_option + + +Driver Recording Options +^^^^^^^^^^^^^^^^^^^^^^^^ +.. embed-options:: + openmdao.core.driver + Driver + debug_print + + + + diff --git a/openmdao/drivers/pyoptsparse_driver.py b/openmdao/drivers/pyoptsparse_driver.py index a49b5f3ff9..e74808ba78 100644 --- a/openmdao/drivers/pyoptsparse_driver.py +++ b/openmdao/drivers/pyoptsparse_driver.py @@ -209,13 +209,17 @@ def run(self): # Metadata Setup self.metadata = create_local_meta(self.options['optimizer']) + with Recording(self.options['optimizer'], self.iter_count, self) as rec: # Initial Run + self._pre_run_model_debug_print() model._solve_nonlinear() + self._post_run_model_debug_print() rec.abs = 0.0 rec.rel = 0.0 self.iter_count += 1 + opt_prob = Optimization(self.options['title'], self._objfunc) # Add all design variables @@ -365,12 +369,16 @@ def run(self): val = dv_dict[name] self.set_design_var(name, val) + with Recording(self.options['optimizer'], self.iter_count, self) as rec: + self._pre_run_model_debug_print() model._solve_nonlinear() + self._post_run_model_debug_print() rec.abs = 0.0 rec.rel = 0.0 self.iter_count += 1 + # Save the most recent solution. self.pyopt_solution = sol try: @@ -418,9 +426,11 @@ def _objfunc(self, dv_dict): # print("Setting DV") # print(dv_dict) + # Execute the model with Recording(self.options['optimizer'], self.iter_count, self) as rec: self.iter_count += 1 + self._pre_run_model_debug_print() try: model._solve_nonlinear() @@ -429,6 +439,8 @@ def _objfunc(self, dv_dict): model._clear_iprint() fail = 1 + self._post_run_model_debug_print() + func_dict = self.get_objective_values() func_dict.update(self.get_constraint_values(lintype='nonlinear')) @@ -437,6 +449,7 @@ def _objfunc(self, dv_dict): rec.abs = 0.0 rec.rel = 0.0 + except Exception as msg: tb = traceback.format_exc() diff --git a/openmdao/drivers/scipy_optimizer.py b/openmdao/drivers/scipy_optimizer.py index a69d4c923f..3d9b08a62b 100644 --- a/openmdao/drivers/scipy_optimizer.py +++ b/openmdao/drivers/scipy_optimizer.py @@ -346,10 +346,14 @@ def _objfunc(self, x_new): self.set_design_var(name, x_new[i:i + size]) i += size + self._pre_run_model_debug_print() + with Recording(self.options['optimizer'], self.iter_count, self) as rec: self.iter_count += 1 model._solve_nonlinear() + self._post_run_model_debug_print() + # Get the objective function evaluations for name, obj in iteritems(self.get_objective_values()): f_new = obj diff --git a/openmdao/drivers/tests/test_pyoptsparse_driver.py b/openmdao/drivers/tests/test_pyoptsparse_driver.py index efbe591409..50360f1b15 100644 --- a/openmdao/drivers/tests/test_pyoptsparse_driver.py +++ b/openmdao/drivers/tests/test_pyoptsparse_driver.py @@ -1305,6 +1305,56 @@ def test_raised_error_sensfunc(self): # pyopt's failure message differs by platform and is not informative anyway del prob + def test_debug_print_option(self): + + prob = Problem() + model = prob.model = Group() + + model.add_subsystem('p1', IndepVarComp('x', 50.0), promotes=['*']) + model.add_subsystem('p2', IndepVarComp('y', 50.0), promotes=['*']) + model.add_subsystem('comp', Paraboloid(), promotes=['*']) + model.add_subsystem('con', ExecComp('c = - x + y'), promotes=['*']) + + prob.set_solver_print(level=0) + + prob.driver = pyOptSparseDriver() + prob.driver.options['optimizer'] = OPTIMIZER + if OPTIMIZER == 'SLSQP': + prob.driver.opt_settings['ACC'] = 1e-9 + prob.driver.options['print_results'] = False + + prob.driver.debug_print['debug_print'] = True + prob.driver.debug_print['debug_print_desvars'] = True + prob.driver.debug_print['debug_print_nl_con'] = True + prob.driver.debug_print['debug_print_ln_con'] = True + prob.driver.debug_print['debug_print_objective'] = True + + model.add_design_var('x', lower=-50.0, upper=50.0) + model.add_design_var('y', lower=-50.0, upper=50.0) + model.add_objective('f_xy') + model.add_constraint('c', upper=-15.0) + + prob.setup(check=False) + + stdout = sys.stdout + strout = StringIO() + sys.stdout = strout + try: + prob.run_driver() + finally: + sys.stdout = stdout + + output = strout.getvalue().split('\n') + self.assertEqual(output.count("Design Vars"), 11) + self.assertEqual(output.count("Nonlinear constraints"), 11) + self.assertEqual(output.count("Linear constraints"), 0) + self.assertEqual(output.count("Objectives"), 11) + + self.assertEqual(len([s for s in output if s.startswith('p1.x')]), 11) + self.assertEqual(len([s for s in output if s.startswith('p2.y')]), 11) + self.assertEqual(len([s for s in output if s.startswith('con.c')]), 11) + self.assertEqual(len([s for s in output if s.startswith('comp.f_xy')]), 11) + class TestPyoptSparseFeature(unittest.TestCase): diff --git a/openmdao/drivers/tests/test_scipy_optimizer.py b/openmdao/drivers/tests/test_scipy_optimizer.py index de0a2d6425..68c1140f5f 100644 --- a/openmdao/drivers/tests/test_scipy_optimizer.py +++ b/openmdao/drivers/tests/test_scipy_optimizer.py @@ -1,6 +1,9 @@ """ Unit tests for the ScipyOptimizer Driver.""" import unittest +import sys + +from six import StringIO import numpy as np @@ -745,6 +748,54 @@ def test_sellar_mdf_COBYLA(self): assert_rel_error(self, prob['z'][1], 0.0, 1e-3) assert_rel_error(self, prob['x'], 0.0, 1e-3) + def test_debug_print_option(self): + + prob = Problem() + model = prob.model = Group() + + model.add_subsystem('p1', IndepVarComp('x', 50.0), promotes=['*']) + model.add_subsystem('p2', IndepVarComp('y', 50.0), promotes=['*']) + model.add_subsystem('comp', Paraboloid(), promotes=['*']) + model.add_subsystem('con', ExecComp('c = - x + y'), promotes=['*']) + + prob.set_solver_print(level=0) + + prob.driver = ScipyOptimizer() + prob.driver.options['optimizer'] = 'SLSQP' + prob.driver.options['tol'] = 1e-9 + prob.driver.options['disp'] = False + + prob.driver.debug_print['debug_print'] = True + prob.driver.debug_print['debug_print_desvars'] = True + prob.driver.debug_print['debug_print_nl_con'] = True + prob.driver.debug_print['debug_print_ln_con'] = True + prob.driver.debug_print['debug_print_objective'] = True + + model.add_design_var('x', lower=-50.0, upper=50.0) + model.add_design_var('y', lower=-50.0, upper=50.0) + model.add_objective('f_xy') + model.add_constraint('c', upper=-15.0) + + prob.setup(check=False) + + stdout = sys.stdout + strout = StringIO() + sys.stdout = strout + try: + prob.run_driver() + finally: + sys.stdout = stdout + + output = strout.getvalue().split('\n') + self.assertEqual(output.count("Design Vars"), 4) + self.assertEqual(output.count("Nonlinear constraints"), 4) + self.assertEqual(output.count("Linear constraints"), 0) + self.assertEqual(output.count("Objectives"), 4) + + self.assertEqual(len([s for s in output if s.startswith('p1.x')]), 4) + self.assertEqual(len([s for s in output if s.startswith('p2.y')]), 4) + self.assertEqual(len([s for s in output if s.startswith('con.c')]), 4) + self.assertEqual(len([s for s in output if s.startswith('comp.f_xy')]), 4) class TestScipyOptimizerFeatures(unittest.TestCase): @@ -848,6 +899,41 @@ def test_feature_tol(self): assert_rel_error(self, prob['x'], 6.66666667, 1e-6) assert_rel_error(self, prob['y'], -7.3333333, 1e-6) + def test_debug_print_option(self): + + from openmdao.api import Problem, Group, IndepVarComp, ScipyOptimizer, ExecComp + from openmdao.test_suite.components.paraboloid import Paraboloid + + prob = Problem() + model = prob.model = Group() + + model.add_subsystem('p1', IndepVarComp('x', 50.0), promotes=['*']) + model.add_subsystem('p2', IndepVarComp('y', 50.0), promotes=['*']) + model.add_subsystem('comp', Paraboloid(), promotes=['*']) + model.add_subsystem('con', ExecComp('c = - x + y'), promotes=['*']) + + prob.set_solver_print(level=0) + + prob.driver = ScipyOptimizer() + prob.driver.options['optimizer'] = 'SLSQP' + prob.driver.options['tol'] = 1e-9 + prob.driver.options['disp'] = False + + prob.driver.debug_print['debug_print'] = True + prob.driver.debug_print['debug_print_desvars'] = True + prob.driver.debug_print['debug_print_nl_con'] = True + prob.driver.debug_print['debug_print_ln_con'] = True + prob.driver.debug_print['debug_print_objective'] = True + + model.add_design_var('x', lower=-50.0, upper=50.0) + model.add_design_var('y', lower=-50.0, upper=50.0) + model.add_objective('f_xy') + model.add_constraint('c', upper=-15.0) + + prob.setup(check=False) + + prob.run_driver() + if __name__ == "__main__": unittest.main() From 5f2d6f5be677398584915230525af1c1db9b4aaf Mon Sep 17 00:00:00 2001 From: Herb Schilling Date: Thu, 11 Jan 2018 10:29:44 -0500 Subject: [PATCH 02/10] All tests pass --- openmdao/core/driver.py | 22 ++++++--------- openmdao/core/tests/test_driver.py | 2 +- openmdao/drivers/pyoptsparse_driver.py | 6 ----- .../drivers/tests/test_pyoptsparse_driver.py | 27 ++++++++++++------- .../drivers/tests/test_scipy_optimizer.py | 26 +++++++++++------- 5 files changed, 43 insertions(+), 40 deletions(-) diff --git a/openmdao/core/driver.py b/openmdao/core/driver.py index 3ab3fbd65a..1ad1b82e62 100644 --- a/openmdao/core/driver.py +++ b/openmdao/core/driver.py @@ -36,12 +36,6 @@ class Driver(object): recording_options['excludes'] : list of strings('') Patterns for variables to exclude in recording (processed after includes). - debug_print - debug_print_desvars - debug_print_nl_con - debug_print_ln_con - debug_print_objective - Attributes ---------- fail : bool @@ -54,6 +48,10 @@ class Driver(object): Dictionary with general pyoptsparse options. recording_options : Dictionary with driver recording options. + recording_options : + Dictionary with driver recording options. + debug_print : + Dictionary with debugging printing options. cite : str Listing of relevant citataions that should be referenced when publishing work that uses this class. @@ -569,13 +567,10 @@ def run(self): boolean Failure flag; True if failed to converge, False is successful. """ - - self._pre_run_model_debug_print() - with Recording(self._get_name(), self.iter_count, self) as rec: + self._pre_run_model_debug_print() failure_flag = self._problem.model._solve_nonlinear() - - self._post_run_model_debug_print() + self._post_run_model_debug_print() self.iter_count += 1 return failure_flag @@ -836,7 +831,7 @@ def _pre_run_model_debug_print(self): header = 'Driver debug print for iter coord: {}'.format( get_formatted_iteration_coordinate()) print(header) - print(len(header)*'-') + print(len(header) * '-') if self.debug_print['debug_print_desvars']: desvar_vals = self.get_design_var_values() @@ -844,7 +839,7 @@ def _pre_run_model_debug_print(self): print("Design Vars") if desvar_vals: for name, value in iteritems(desvar_vals): - print("{}: {}".format(name,value)) + print("{}: {}".format(name, value)) else: print("None") print() @@ -886,4 +881,3 @@ def _post_run_model_debug_print(self): else: print("None") print() - diff --git a/openmdao/core/tests/test_driver.py b/openmdao/core/tests/test_driver.py index 67d84f55e0..688e86f21b 100644 --- a/openmdao/core/tests/test_driver.py +++ b/openmdao/core/tests/test_driver.py @@ -227,7 +227,7 @@ def test_debug_print_option(self): finally: sys.stdout = stdout output = strout.getvalue().split('\n') - self.assertEqual(output[0], 'Driver debug print for iter coord: rank0:') + self.assertEqual(output[0], 'Driver debug print for iter coord: rank0:Driver|1') # Make sure everything prints when all options are on prob.driver.debug_print['debug_print_desvars'] = True diff --git a/openmdao/drivers/pyoptsparse_driver.py b/openmdao/drivers/pyoptsparse_driver.py index e74808ba78..4bcc11215a 100644 --- a/openmdao/drivers/pyoptsparse_driver.py +++ b/openmdao/drivers/pyoptsparse_driver.py @@ -209,7 +209,6 @@ def run(self): # Metadata Setup self.metadata = create_local_meta(self.options['optimizer']) - with Recording(self.options['optimizer'], self.iter_count, self) as rec: # Initial Run self._pre_run_model_debug_print() @@ -219,7 +218,6 @@ def run(self): rec.rel = 0.0 self.iter_count += 1 - opt_prob = Optimization(self.options['title'], self._objfunc) # Add all design variables @@ -369,7 +367,6 @@ def run(self): val = dv_dict[name] self.set_design_var(name, val) - with Recording(self.options['optimizer'], self.iter_count, self) as rec: self._pre_run_model_debug_print() model._solve_nonlinear() @@ -378,7 +375,6 @@ def run(self): rec.rel = 0.0 self.iter_count += 1 - # Save the most recent solution. self.pyopt_solution = sol try: @@ -426,7 +422,6 @@ def _objfunc(self, dv_dict): # print("Setting DV") # print(dv_dict) - # Execute the model with Recording(self.options['optimizer'], self.iter_count, self) as rec: self.iter_count += 1 @@ -449,7 +444,6 @@ def _objfunc(self, dv_dict): rec.abs = 0.0 rec.rel = 0.0 - except Exception as msg: tb = traceback.format_exc() diff --git a/openmdao/drivers/tests/test_pyoptsparse_driver.py b/openmdao/drivers/tests/test_pyoptsparse_driver.py index 50360f1b15..ede8f414ac 100644 --- a/openmdao/drivers/tests/test_pyoptsparse_driver.py +++ b/openmdao/drivers/tests/test_pyoptsparse_driver.py @@ -1345,16 +1345,23 @@ def test_debug_print_option(self): sys.stdout = stdout output = strout.getvalue().split('\n') - self.assertEqual(output.count("Design Vars"), 11) - self.assertEqual(output.count("Nonlinear constraints"), 11) - self.assertEqual(output.count("Linear constraints"), 0) - self.assertEqual(output.count("Objectives"), 11) - - self.assertEqual(len([s for s in output if s.startswith('p1.x')]), 11) - self.assertEqual(len([s for s in output if s.startswith('p2.y')]), 11) - self.assertEqual(len([s for s in output if s.startswith('con.c')]), 11) - self.assertEqual(len([s for s in output if s.startswith('comp.f_xy')]), 11) - + self.assertTrue(output.count("Design Vars") > 1, + "Should be more than one design vars header printed") + self.assertTrue(output.count("Nonlinear constraints") > 1, + "Should be more than one nonlinear constraint header printed") + self.assertTrue(output.count("Linear constraints") > 1, + "Should be more than one linear constraint header printed") + self.assertTrue(output.count("Objectives") > 1, + "Should be more than one objective header printed") + + self.assertTrue(len([s for s in output if s.startswith('p1.x')]) > 1, + "Should be more than one p1.x printed") + self.assertTrue(len([s for s in output if s.startswith('p2.y')]) > 1, + "Should be more than one p2.y printed") + self.assertTrue(len([s for s in output if s.startswith('con.c')]) > 1, + "Should be more than one con.c printed") + self.assertTrue(len([s for s in output if s.startswith('comp.f_xy')]) > 1, + "Should be more than one comp.f_xy printed") class TestPyoptSparseFeature(unittest.TestCase): diff --git a/openmdao/drivers/tests/test_scipy_optimizer.py b/openmdao/drivers/tests/test_scipy_optimizer.py index 68c1140f5f..ad9211f08d 100644 --- a/openmdao/drivers/tests/test_scipy_optimizer.py +++ b/openmdao/drivers/tests/test_scipy_optimizer.py @@ -787,15 +787,23 @@ def test_debug_print_option(self): sys.stdout = stdout output = strout.getvalue().split('\n') - self.assertEqual(output.count("Design Vars"), 4) - self.assertEqual(output.count("Nonlinear constraints"), 4) - self.assertEqual(output.count("Linear constraints"), 0) - self.assertEqual(output.count("Objectives"), 4) - - self.assertEqual(len([s for s in output if s.startswith('p1.x')]), 4) - self.assertEqual(len([s for s in output if s.startswith('p2.y')]), 4) - self.assertEqual(len([s for s in output if s.startswith('con.c')]), 4) - self.assertEqual(len([s for s in output if s.startswith('comp.f_xy')]), 4) + self.assertTrue(output.count("Design Vars") > 1, + "Should be more than one design vars header printed") + self.assertTrue(output.count("Nonlinear constraints") > 1, + "Should be more than one nonlinear constraint header printed") + self.assertTrue(output.count("Linear constraints") > 1, + "Should be more than one linear constraint header printed") + self.assertTrue(output.count("Objectives") > 1, + "Should be more than one objective header printed") + + self.assertTrue(len([s for s in output if s.startswith('p1.x')]) > 1, + "Should be more than one p1.x printed") + self.assertTrue(len([s for s in output if s.startswith('p2.y')]) > 1, + "Should be more than one p2.y printed") + self.assertTrue(len([s for s in output if s.startswith('con.c')]) > 1, + "Should be more than one con.c printed") + self.assertTrue(len([s for s in output if s.startswith('comp.f_xy')]) > 1, + "Should be more than one comp.f_xy printed") class TestScipyOptimizerFeatures(unittest.TestCase): From 9638913cfb6a6c9e39f0e1810252b1bdaed7dd63 Mon Sep 17 00:00:00 2001 From: Herb Schilling Date: Thu, 11 Jan 2018 11:13:05 -0500 Subject: [PATCH 03/10] More tweaks to the docs --- openmdao/core/driver.py | 27 +++++++++++++++---- .../debugging/debugging_drivers.rst | 16 ++++++----- .../working_with_models/debugging/index.rst | 1 + 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/openmdao/core/driver.py b/openmdao/core/driver.py index 1ad1b82e62..32bfab90f9 100644 --- a/openmdao/core/driver.py +++ b/openmdao/core/driver.py @@ -36,6 +36,18 @@ class Driver(object): recording_options['excludes'] : list of strings('') Patterns for variables to exclude in recording (processed after includes). + debug_print['debug_print'] : bool(False) + If True, print debugging information every Driver iteration. + debug_print['debug_print_desvars'] : bool(False) + If True and the option debug_print is True, print design variables every Driver iteration. + debug_print['debug_print_nl_con'] : bool(False) + If True and the option debug_print is True, print nonlinear constraints every + Driver iteration. + debug_print['debug_print_ln_con'] : bool(False) + If True and the option debug_print is True, print linear constraints every Driver iteration. + debug_print['debug_print_objective'] : bool(False) + If True and the option debug_print is True, print objectives every Driver iteration. + Attributes ---------- fail : bool @@ -149,11 +161,16 @@ def __init__(self): # Debug printing. self.debug_print = OptionsDictionary() - self.debug_print.declare('debug_print', types=bool, default=False) - self.debug_print.declare('debug_print_desvars', types=bool, default=False) - self.debug_print.declare('debug_print_nl_con', types=bool, default=False) - self.debug_print.declare('debug_print_ln_con', types=bool, default=False) - self.debug_print.declare('debug_print_objective', types=bool, default=False) + self.debug_print.declare('debug_print', types=bool, default=False, + desc='Overall option to turn on Driver debug printing') + self.debug_print.declare('debug_print_desvars', types=bool, default=False, + desc='Print design variables') + self.debug_print.declare('debug_print_nl_con', types=bool, default=False, + desc='Print nonlinear constraints') + self.debug_print.declare('debug_print_ln_con', types=bool, default=False, + desc='Print linear constraints') + self.debug_print.declare('debug_print_objective', types=bool, default=False, + desc='Print objectives') self.iter_count = 0 self.metadata = None diff --git a/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst b/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst index 5795bfb11f..67f1aa770e 100644 --- a/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst +++ b/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst @@ -1,8 +1,8 @@ .. _debugging-drivers: -************************** -Driver Debug Print Options -************************** +********************* +Driver Debug Printing +********************* When working with a model, it may sometimes be helpful to print out the design variables, constraints, and objectives as the `Driver` iterates. OpenMDAO provides options on the `Driver` to let you do that. @@ -10,16 +10,18 @@ objectives as the `Driver` iterates. OpenMDAO provides options on the `Driver` t Usage ----- -*Debug Print Options* -~~~~~~~~~~~~~~~~~~~~~ +This example shows how to use the `Driver` debug printing options. .. embed-test:: openmdao.drivers.tests.test_scipy_optimizer.TestScipyOptimizerFeatures.test_debug_print_option -Driver Recording Options -^^^^^^^^^^^^^^^^^^^^^^^^ +Driver Debug Printing Options +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Here is a summary of the `Driver` `debug_print` options. + .. embed-options:: openmdao.core.driver Driver diff --git a/openmdao/docs/feature_reference/working_with_models/debugging/index.rst b/openmdao/docs/feature_reference/working_with_models/debugging/index.rst index dcc19f596a..8bce7ea2c1 100644 --- a/openmdao/docs/feature_reference/working_with_models/debugging/index.rst +++ b/openmdao/docs/feature_reference/working_with_models/debugging/index.rst @@ -9,4 +9,5 @@ Debugging om_command.rst newton_solver_not_converging.rst listing_variables.rst + debugging_drivers.rst profiling/index.rst From c3a386dc4c4fa1ab48ca6c8a2417b40ec8ac7742 Mon Sep 17 00:00:00 2001 From: Herb Schilling Date: Thu, 11 Jan 2018 11:50:26 -0500 Subject: [PATCH 04/10] fixed some docstrings --- openmdao/core/driver.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openmdao/core/driver.py b/openmdao/core/driver.py index 0ffd300756..9f0fb06489 100644 --- a/openmdao/core/driver.py +++ b/openmdao/core/driver.py @@ -60,8 +60,6 @@ class Driver(object): Dictionary with general pyoptsparse options. recording_options : Dictionary with driver recording options. - recording_options : - Dictionary with driver recording options. debug_print : Dictionary with debugging printing options. cite : str @@ -839,7 +837,7 @@ def _setup_simul_coloring(self, mode='fwd'): def _pre_run_model_debug_print(self): """ - Set up metadata for simultaneous derivative solution. + Optionally print some debugging information before the model runs. """ if self.debug_print['debug_print']: if not MPI or MPI.COMM_WORLD.rank == 0: @@ -861,7 +859,7 @@ def _pre_run_model_debug_print(self): def _post_run_model_debug_print(self): """ - Set up metadata for simultaneous derivative solution. + Optionally print some debugging information after the model runs. """ if self.debug_print['debug_print']: if self.debug_print['debug_print_nl_con']: From 2286d03e49f3d4083b1222c76a3fc5e35f311240 Mon Sep 17 00:00:00 2001 From: Herb Schilling Date: Thu, 11 Jan 2018 17:15:51 -0500 Subject: [PATCH 05/10] Changed format of the option for debug printing in Drivers --- openmdao/core/driver.py | 129 ++++++++++-------- ...test_distrib_driver_debug_print_options.py | 44 +----- openmdao/core/tests/test_driver.py | 27 ++-- .../debugging/debugging_drivers.rst | 5 +- .../drivers/tests/test_pyoptsparse_driver.py | 6 +- .../drivers/tests/test_scipy_optimizer.py | 12 +- 6 files changed, 87 insertions(+), 136 deletions(-) diff --git a/openmdao/core/driver.py b/openmdao/core/driver.py index 9f0fb06489..5b893bef98 100644 --- a/openmdao/core/driver.py +++ b/openmdao/core/driver.py @@ -14,6 +14,15 @@ from openmdao.recorders.recording_iteration_stack import get_formatted_iteration_coordinate from openmdao.utils.options_dictionary import OptionsDictionary +def is_debug_print_opts_valid(opts): + '''Check to see if the options passed in are valid''' + if not isinstance(opts,list): + return False + _valid_opts = ['desvars','nl_cons','ln_cons','objs'] + for opt in opts: + if opt not in _valid_opts: + return False + return True class Driver(object): """ @@ -21,6 +30,9 @@ class Driver(object): Options ------- + options['debug_print'] : list of strings([]) + Indicates what variables to print at each iteration. The valid options are: + 'desvars','ln_cons','nl_cons',and 'objs'. recording_options['record_metadata'] : bool(True) Tells recorder whether to record variable attribute metadata. recording_options['record_desvars'] : bool(True) @@ -36,17 +48,6 @@ class Driver(object): recording_options['excludes'] : list of strings('') Patterns for variables to exclude in recording (processed after includes). - debug_print['debug_print'] : bool(False) - If True, print debugging information every Driver iteration. - debug_print['debug_print_desvars'] : bool(False) - If True and the option debug_print is True, print design variables every Driver iteration. - debug_print['debug_print_nl_con'] : bool(False) - If True and the option debug_print is True, print nonlinear constraints every - Driver iteration. - debug_print['debug_print_ln_con'] : bool(False) - If True and the option debug_print is True, print linear constraints every Driver iteration. - debug_print['debug_print_objective'] : bool(False) - If True and the option debug_print is True, print objectives every Driver iteration. Attributes ---------- @@ -121,6 +122,12 @@ def __init__(self): self.options = OptionsDictionary() self.recording_options = OptionsDictionary() + ########################### + self.options.declare('debug_print', types=list, is_valid=is_debug_print_opts_valid, + desc="List of what Driver variables to print at each iteration. " + "Valid items in list are 'desvars','ln_cons','nl_cons','objs'", + default=[]) + ########################### self.recording_options.declare('record_metadata', types=bool, desc='Record metadata', default=True) @@ -839,58 +846,60 @@ def _pre_run_model_debug_print(self): """ Optionally print some debugging information before the model runs. """ - if self.debug_print['debug_print']: + + if not self.options['debug_print']: + return + + if not MPI or MPI.COMM_WORLD.rank == 0: + header = 'Driver debug print for iter coord: {}'.format( + get_formatted_iteration_coordinate()) + print(header) + print(len(header) * '-') + + if 'desvars' in self.options['debug_print']: + desvar_vals = self.get_design_var_values() if not MPI or MPI.COMM_WORLD.rank == 0: - header = 'Driver debug print for iter coord: {}'.format( - get_formatted_iteration_coordinate()) - print(header) - print(len(header) * '-') - - if self.debug_print['debug_print_desvars']: - desvar_vals = self.get_design_var_values() - if not MPI or MPI.COMM_WORLD.rank == 0: - print("Design Vars") - if desvar_vals: - for name, value in iteritems(desvar_vals): - print("{}: {}".format(name, value)) - else: - print("None") - print() + print("Design Vars") + if desvar_vals: + for name, value in iteritems(desvar_vals): + print("{}: {}".format(name, value)) + else: + print("None") + print() def _post_run_model_debug_print(self): """ Optionally print some debugging information after the model runs. """ - if self.debug_print['debug_print']: - if self.debug_print['debug_print_nl_con']: - cons = self.get_constraint_values(lintype='nonlinear') - if not MPI or MPI.COMM_WORLD.rank == 0: - print("Nonlinear constraints") - if cons: - for name, value in iteritems(cons): - print("{}: {}".format(name, value)) - else: - print("None") - print() - - if self.debug_print['debug_print_ln_con']: - cons = self.get_constraint_values(lintype='linear') - if not MPI or MPI.COMM_WORLD.rank == 0: - print("Linear constraints") - if cons: - for name, value in iteritems(cons): - print("{}: {}".format(name, value)) - else: - print("None") - print() - - if self.debug_print['debug_print_objective']: - objs = self.get_objective_values() - if not MPI or MPI.COMM_WORLD.rank == 0: - print("Objectives") - if objs: - for name, value in iteritems(objs): - print("{}: {}".format(name, value)) - else: - print("None") - print() + if 'nl_cons' in self.options['debug_print']: + cons = self.get_constraint_values(lintype='nonlinear') + if not MPI or MPI.COMM_WORLD.rank == 0: + print("Nonlinear constraints") + if cons: + for name, value in iteritems(cons): + print("{}: {}".format(name, value)) + else: + print("None") + print() + + if 'ln_cons' in self.options['debug_print']: + cons = self.get_constraint_values(lintype='linear') + if not MPI or MPI.COMM_WORLD.rank == 0: + print("Linear constraints") + if cons: + for name, value in iteritems(cons): + print("{}: {}".format(name, value)) + else: + print("None") + print() + + if 'objs' in self.options['debug_print']: + objs = self.get_objective_values() + if not MPI or MPI.COMM_WORLD.rank == 0: + print("Objectives") + if objs: + for name, value in iteritems(objs): + print("{}: {}".format(name, value)) + else: + print("None") + print() diff --git a/openmdao/core/tests/test_distrib_driver_debug_print_options.py b/openmdao/core/tests/test_distrib_driver_debug_print_options.py index 465f533823..94ff477f73 100644 --- a/openmdao/core/tests/test_distrib_driver_debug_print_options.py +++ b/openmdao/core/tests/test_distrib_driver_debug_print_options.py @@ -1,22 +1,17 @@ import os import sys import unittest -import numpy as np -from six.moves import cStringIO from six import StringIO -from openmdao.api import ExplicitComponent, Problem, Group, IndepVarComp - +from openmdao.api import Problem, Group, IndepVarComp, ExecComp, ParallelGroup +from openmdao.utils.general_utils import set_pyoptsparse_opt from openmdao.utils.mpi import MPI -from openmdao.utils.array_utils import evenly_distrib_idxs try: from openmdao.vectors.petsc_vector import PETScVector except ImportError: PETScVector = None -from openmdao.utils.assert_utils import assert_rel_error - @unittest.skipIf(PETScVector is None or os.environ.get("TRAVIS"), "PETSc is required." if PETScVector is None else "Unreliable on Travis CI.") @@ -26,24 +21,12 @@ class DistributedDriverDebugPrintOptionsTest(unittest.TestCase): def test_distributed_driver_debug_print_options(self): - from openmdao.utils.general_utils import set_pyoptsparse_opt - from openmdao.utils.mpi import MPI - - if MPI: - from openmdao.api import PETScVector - vector_class = PETScVector - else: - PETScVector = None - # check that pyoptsparse is installed. if it is, try to use SLSQP. OPT, OPTIMIZER = set_pyoptsparse_opt('SLSQP') if OPTIMIZER: from openmdao.drivers.pyoptsparse_driver import pyOptSparseDriver - from openmdao.core.parallel_group import ParallelGroup - from openmdao.components.exec_comp import ExecComp - class Mygroup(Group): def setup(self): @@ -72,11 +55,7 @@ def setup(self): prob.driver.options['optimizer'] = 'SLSQP' prob.driver.options['print_results'] = False - prob.driver.debug_print['debug_print'] = True - prob.driver.debug_print['debug_print_desvars'] = True - prob.driver.debug_print['debug_print_nl_con'] = True - prob.driver.debug_print['debug_print_ln_con'] = True - prob.driver.debug_print['debug_print_objective'] = True + prob.driver.options['debug_print'] = ['desvars','ln_cons','nl_cons','objs'] prob.setup(vector_class=PETScVector) @@ -89,17 +68,6 @@ def setup(self): finally: sys.stdout = stdout - # prob.run_driver() - prob.cleanup() - - # stdout = sys.stdout - # strout = StringIO() - # sys.stdout = strout - # try: - # prob.run_driver() - # finally: - # sys.stdout = stdout - output = strout.getvalue().split('\n') if MPI.COMM_WORLD.rank == 0: # Just make sure we have more than one. Not sure we will always have the same number @@ -125,12 +93,6 @@ def setup(self): "Should be more than one None printed") self.assertTrue(len([s for s in output if s.startswith('Obj.obj')]) > 1, "Should be more than one Obj.obj printed") - # self.assertEqual(len([s for s in output if s.startswith('par.G1.indep_var_comp.x')]), 11) - # self.assertEqual(len([s for s in output if s.startswith('par.G2.indep_var_comp.x')]), 11) - # self.assertEqual(len([s for s in output if s.startswith('par.G1.Cc.c')]), 11) - # self.assertEqual(len([s for s in output if s.startswith('par.G2.Cc.c')]), 11) - # self.assertEqual(len([s for s in output if s.startswith('None')]), 11) - # self.assertEqual(len([s for s in output if s.startswith('Obj.obj')]), 11) else: self.assertEqual(output, ['']) diff --git a/openmdao/core/tests/test_driver.py b/openmdao/core/tests/test_driver.py index 688e86f21b..ddb45d41ed 100644 --- a/openmdao/core/tests/test_driver.py +++ b/openmdao/core/tests/test_driver.py @@ -205,7 +205,7 @@ def test_debug_print_option(self): prob.setup(check=False) - # Make sure nothing prints if debug is not turned on + # Make sure nothing prints if debug_print is the default of empty list stdout = sys.stdout strout = StringIO() sys.stdout = strout @@ -216,24 +216,8 @@ def test_debug_print_option(self): output = strout.getvalue().split('\n') self.assertEqual(output, ['']) - # Make sure nothing prints even if debug print is turned on - # but none of the individual options are turned on - prob.driver.debug_print['debug_print'] = True - stdout = sys.stdout - strout = StringIO() - sys.stdout = strout - try: - prob.run_driver() - finally: - sys.stdout = stdout - output = strout.getvalue().split('\n') - self.assertEqual(output[0], 'Driver debug print for iter coord: rank0:Driver|1') - # Make sure everything prints when all options are on - prob.driver.debug_print['debug_print_desvars'] = True - prob.driver.debug_print['debug_print_nl_con'] = True - prob.driver.debug_print['debug_print_ln_con'] = True - prob.driver.debug_print['debug_print_objective'] = True + prob.driver.options['debug_print'] = ['desvars','ln_cons','nl_cons','objs'] stdout = sys.stdout strout = StringIO() sys.stdout = strout @@ -242,11 +226,18 @@ def test_debug_print_option(self): finally: sys.stdout = stdout output = strout.getvalue().split('\n') + self.assertEqual(output.count("Driver debug print for iter coord: rank0:Driver|1"), 1) self.assertEqual(output.count("Design Vars"), 1) self.assertEqual(output.count("Nonlinear constraints"), 1) self.assertEqual(output.count("Linear constraints"), 1) self.assertEqual(output.count("Objectives"), 1) + # Check to make sure an invalid debug_print option raises an exception + with self.assertRaises(ValueError) as context: + prob.driver.options['debug_print'] = ['bad_option'] + self.assertEqual(str(context.exception), + "Function is_valid returns False for debug_print.") + if __name__ == "__main__": unittest.main() diff --git a/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst b/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst index 67f1aa770e..b821762bed 100644 --- a/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst +++ b/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst @@ -10,7 +10,8 @@ objectives as the `Driver` iterates. OpenMDAO provides options on the `Driver` t Usage ----- -This example shows how to use the `Driver` debug printing options. +This example shows how to use the `Driver` debug printing options. The `debug_print` option is a list of strings. +The only valid strings are 'desvars','ln_cons','nl_cons',and 'objs'. .. embed-test:: @@ -25,7 +26,7 @@ Here is a summary of the `Driver` `debug_print` options. .. embed-options:: openmdao.core.driver Driver - debug_print + options diff --git a/openmdao/drivers/tests/test_pyoptsparse_driver.py b/openmdao/drivers/tests/test_pyoptsparse_driver.py index ede8f414ac..f1df3ae1ea 100644 --- a/openmdao/drivers/tests/test_pyoptsparse_driver.py +++ b/openmdao/drivers/tests/test_pyoptsparse_driver.py @@ -1323,11 +1323,7 @@ def test_debug_print_option(self): prob.driver.opt_settings['ACC'] = 1e-9 prob.driver.options['print_results'] = False - prob.driver.debug_print['debug_print'] = True - prob.driver.debug_print['debug_print_desvars'] = True - prob.driver.debug_print['debug_print_nl_con'] = True - prob.driver.debug_print['debug_print_ln_con'] = True - prob.driver.debug_print['debug_print_objective'] = True + prob.driver.options['debug_print'] = ['desvars','ln_cons','nl_cons','objs'] model.add_design_var('x', lower=-50.0, upper=50.0) model.add_design_var('y', lower=-50.0, upper=50.0) diff --git a/openmdao/drivers/tests/test_scipy_optimizer.py b/openmdao/drivers/tests/test_scipy_optimizer.py index eec1d089da..c7239da128 100644 --- a/openmdao/drivers/tests/test_scipy_optimizer.py +++ b/openmdao/drivers/tests/test_scipy_optimizer.py @@ -832,11 +832,7 @@ def test_debug_print_option(self): prob.driver.options['tol'] = 1e-9 prob.driver.options['disp'] = False - prob.driver.debug_print['debug_print'] = True - prob.driver.debug_print['debug_print_desvars'] = True - prob.driver.debug_print['debug_print_nl_con'] = True - prob.driver.debug_print['debug_print_ln_con'] = True - prob.driver.debug_print['debug_print_objective'] = True + prob.driver.options['debug_print'] = ['desvars','ln_cons','nl_cons','objs'] model.add_design_var('x', lower=-50.0, upper=50.0) model.add_design_var('y', lower=-50.0, upper=50.0) @@ -993,11 +989,7 @@ def test_debug_print_option(self): prob.driver.options['tol'] = 1e-9 prob.driver.options['disp'] = False - prob.driver.debug_print['debug_print'] = True - prob.driver.debug_print['debug_print_desvars'] = True - prob.driver.debug_print['debug_print_nl_con'] = True - prob.driver.debug_print['debug_print_ln_con'] = True - prob.driver.debug_print['debug_print_objective'] = True + prob.driver.options['debug_print'] = ['desvars','ln_cons','nl_cons','objs'] model.add_design_var('x', lower=-50.0, upper=50.0) model.add_design_var('y', lower=-50.0, upper=50.0) From 46763500ce339bf375bd002d61c720cbd7bd9f77 Mon Sep 17 00:00:00 2001 From: Herb Schilling Date: Thu, 11 Jan 2018 17:50:18 -0500 Subject: [PATCH 06/10] Fixed pep and docstring errors --- openmdao/core/driver.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/openmdao/core/driver.py b/openmdao/core/driver.py index 5b893bef98..a4aa92a4e6 100644 --- a/openmdao/core/driver.py +++ b/openmdao/core/driver.py @@ -14,16 +14,30 @@ from openmdao.recorders.recording_iteration_stack import get_formatted_iteration_coordinate from openmdao.utils.options_dictionary import OptionsDictionary -def is_debug_print_opts_valid(opts): - '''Check to see if the options passed in are valid''' - if not isinstance(opts,list): + +def _is_debug_print_opts_valid(opts): + """ + Check validity of debug_print option for Driver. + + Parameters + ---------- + opts : list + The value of the debug_print option set by the user. + + Returns + ------- + bool + True if the option is valid. Otherwise, False. + """ + if not isinstance(opts, list): return False - _valid_opts = ['desvars','nl_cons','ln_cons','objs'] + _valid_opts = ['desvars', 'nl_cons', 'ln_cons', 'objs'] for opt in opts: if opt not in _valid_opts: return False return True + class Driver(object): """ Top-level container for the systems and drivers. @@ -123,9 +137,10 @@ def __init__(self): self.recording_options = OptionsDictionary() ########################### - self.options.declare('debug_print', types=list, is_valid=is_debug_print_opts_valid, - desc="List of what Driver variables to print at each iteration. " - "Valid items in list are 'desvars','ln_cons','nl_cons','objs'", + self.options.declare('debug_print', types=list, is_valid=_is_debug_print_opts_valid, + desc="List of what type of Driver variables to print at each " + "iteration. Valid items in list are 'desvars','ln_cons'," + "'nl_cons','objs'", default=[]) ########################### @@ -846,7 +861,6 @@ def _pre_run_model_debug_print(self): """ Optionally print some debugging information before the model runs. """ - if not self.options['debug_print']: return From 790796e4759533711541ec45df3c926ea0edcb9c Mon Sep 17 00:00:00 2001 From: Herb Schilling Date: Fri, 12 Jan 2018 16:07:06 -0500 Subject: [PATCH 07/10] nano tweak to docs --- .../debugging/debugging_drivers.rst | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst b/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst index b821762bed..1ec2185d4c 100644 --- a/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst +++ b/openmdao/docs/feature_reference/working_with_models/debugging/debugging_drivers.rst @@ -18,16 +18,3 @@ The only valid strings are 'desvars','ln_cons','nl_cons',and 'objs'. openmdao.drivers.tests.test_scipy_optimizer.TestScipyOptimizerFeatures.test_debug_print_option -Driver Debug Printing Options -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Here is a summary of the `Driver` `debug_print` options. - -.. embed-options:: - openmdao.core.driver - Driver - options - - - - From f1f2d2613125b3381f710aafb9acbf942cee1de4 Mon Sep 17 00:00:00 2001 From: Herb Schilling Date: Fri, 12 Jan 2018 16:46:20 -0500 Subject: [PATCH 08/10] Changed to using an inherited version of Recording context manager for the Driver iterations --- openmdao/core/driver.py | 53 ++++++++++++++++++++++++-- openmdao/drivers/pyoptsparse_driver.py | 22 ++++------- openmdao/drivers/scipy_optimizer.py | 10 +---- 3 files changed, 59 insertions(+), 26 deletions(-) diff --git a/openmdao/core/driver.py b/openmdao/core/driver.py index a4aa92a4e6..7a3694a9df 100644 --- a/openmdao/core/driver.py +++ b/openmdao/core/driver.py @@ -602,10 +602,8 @@ def run(self): boolean Failure flag; True if failed to converge, False is successful. """ - with Recording(self._get_name(), self.iter_count, self) as rec: - self._pre_run_model_debug_print() + with RecordingDebugging(self._get_name(), self.iter_count, self) as rec: failure_flag = self._problem.model._solve_nonlinear() - self._post_run_model_debug_print() self.iter_count += 1 return failure_flag @@ -917,3 +915,52 @@ def _post_run_model_debug_print(self): else: print("None") print() + + +class RecordingDebugging(Recording): + """ + A class that acts as a context manager. + + Handles doing the case recording and also the Driver + debugging printing. + """ + + def __init__(self, name, iter_count, recording_requester): + """ + Initialize RecordingDebugging. + + Parameters + ---------- + name : str + Name of object getting recorded. + iter_count : int + Current counter of iterations completed. + recording_requester : object + The object that wants to be recorded. + """ + super(RecordingDebugging, self).__init__(name, iter_count, recording_requester) + + def __enter__(self): + """ + Do things before the code inside the 'with RecordingDebugging' block. + + Returns + ------- + self : object + self + """ + super(RecordingDebugging, self).__enter__() + self.recording_requester._pre_run_model_debug_print() + return self + + def __exit__(self, *args): + """ + Do things after the code inside the 'with RecordingDebugging' block. + + Parameters + ---------- + *args : array + Solver recording requires extra args. + """ + self.recording_requester._post_run_model_debug_print() + super(RecordingDebugging, self).__exit__() diff --git a/openmdao/drivers/pyoptsparse_driver.py b/openmdao/drivers/pyoptsparse_driver.py index 4e100c028d..a971e34d2d 100644 --- a/openmdao/drivers/pyoptsparse_driver.py +++ b/openmdao/drivers/pyoptsparse_driver.py @@ -7,10 +7,10 @@ """ from __future__ import print_function -from collections import OrderedDict, defaultdict +from collections import OrderedDict import traceback -from six import iteritems, itervalues +from six import iteritems import numpy as np from scipy.sparse import coo_matrix @@ -18,11 +18,10 @@ from pyoptsparse import Optimization from openmdao.core.analysis_error import AnalysisError -from openmdao.core.driver import Driver -from openmdao.jacobians.assembled_jacobian import AssembledJacobian -from openmdao.recorders.recording_iteration_stack import Recording +from openmdao.core.driver import Driver, RecordingDebugging from openmdao.utils.record_util import create_local_meta + # names of optimizers that use gradients grad_drivers = {'CONMIN', 'FSQP', 'IPOPT', 'NLPQLP', 'PSQP', 'SLSQP', 'SNOPT', 'NLPY_AUGLAG'} @@ -214,11 +213,9 @@ def run(self): # Metadata Setup self.metadata = create_local_meta(self.options['optimizer']) - with Recording(self.options['optimizer'], self.iter_count, self) as rec: + with RecordingDebugging(self.options['optimizer'], self.iter_count, self) as rec: # Initial Run - self._pre_run_model_debug_print() model._solve_nonlinear() - self._post_run_model_debug_print() rec.abs = 0.0 rec.rel = 0.0 self.iter_count += 1 @@ -372,10 +369,8 @@ def run(self): val = dv_dict[name] self.set_design_var(name, val) - with Recording(self.options['optimizer'], self.iter_count, self) as rec: - self._pre_run_model_debug_print() + with RecordingDebugging(self.options['optimizer'], self.iter_count, self) as rec: model._solve_nonlinear() - self._post_run_model_debug_print() rec.abs = 0.0 rec.rel = 0.0 self.iter_count += 1 @@ -428,9 +423,8 @@ def _objfunc(self, dv_dict): # print(dv_dict) # Execute the model - with Recording(self.options['optimizer'], self.iter_count, self) as rec: + with RecordingDebugging(self.options['optimizer'], self.iter_count, self) as rec: self.iter_count += 1 - self._pre_run_model_debug_print() try: model._solve_nonlinear() @@ -439,8 +433,6 @@ def _objfunc(self, dv_dict): model._clear_iprint() fail = 1 - self._post_run_model_debug_print() - func_dict = self.get_objective_values() func_dict.update(self.get_constraint_values(lintype='nonlinear')) diff --git a/openmdao/drivers/scipy_optimizer.py b/openmdao/drivers/scipy_optimizer.py index 21bdbb7ca8..aedcf0c4bf 100644 --- a/openmdao/drivers/scipy_optimizer.py +++ b/openmdao/drivers/scipy_optimizer.py @@ -4,7 +4,6 @@ from __future__ import print_function from collections import OrderedDict -import traceback import sys from six import itervalues, iteritems, reraise @@ -13,8 +12,7 @@ import numpy as np from scipy.optimize import minimize -from openmdao.core.driver import Driver -from openmdao.recorders.recording_iteration_stack import Recording +from openmdao.core.driver import Driver, RecordingDebugging _optimizers = ['Nelder-Mead', 'Powell', 'CG', 'BFGS', 'Newton-CG', 'L-BFGS-B', @@ -373,14 +371,10 @@ def _objfunc(self, x_new): self.set_design_var(name, x_new[i:i + size]) i += size - self._pre_run_model_debug_print() - - with Recording(self.options['optimizer'], self.iter_count, self) as rec: + with RecordingDebugging(self.options['optimizer'], self.iter_count, self) as rec: self.iter_count += 1 model._solve_nonlinear() - self._post_run_model_debug_print() - # Get the objective function evaluations for name, obj in iteritems(self.get_objective_values()): f_new = obj From ced6f30db4788ad970c9685647d5d7364a0af7e6 Mon Sep 17 00:00:00 2001 From: Herb Schilling Date: Wed, 17 Jan 2018 11:43:58 -0500 Subject: [PATCH 09/10] removed unneeded __init__ --- openmdao/core/driver.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/openmdao/core/driver.py b/openmdao/core/driver.py index 7a3694a9df..58ad0208d1 100644 --- a/openmdao/core/driver.py +++ b/openmdao/core/driver.py @@ -925,21 +925,6 @@ class RecordingDebugging(Recording): debugging printing. """ - def __init__(self, name, iter_count, recording_requester): - """ - Initialize RecordingDebugging. - - Parameters - ---------- - name : str - Name of object getting recorded. - iter_count : int - Current counter of iterations completed. - recording_requester : object - The object that wants to be recorded. - """ - super(RecordingDebugging, self).__init__(name, iter_count, recording_requester) - def __enter__(self): """ Do things before the code inside the 'with RecordingDebugging' block. From bbae5142fa87c9273e5fab19e9f04aa2e2658438 Mon Sep 17 00:00:00 2001 From: Herb Schilling Date: Wed, 17 Jan 2018 17:21:57 -0500 Subject: [PATCH 10/10] Using repr to print the values --- openmdao/core/driver.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmdao/core/driver.py b/openmdao/core/driver.py index 58ad0208d1..6cc6fa6fdc 100644 --- a/openmdao/core/driver.py +++ b/openmdao/core/driver.py @@ -874,7 +874,7 @@ def _pre_run_model_debug_print(self): print("Design Vars") if desvar_vals: for name, value in iteritems(desvar_vals): - print("{}: {}".format(name, value)) + print("{}: {}".format(name, repr(value))) else: print("None") print() @@ -889,7 +889,7 @@ def _post_run_model_debug_print(self): print("Nonlinear constraints") if cons: for name, value in iteritems(cons): - print("{}: {}".format(name, value)) + print("{}: {}".format(name, repr(value))) else: print("None") print() @@ -900,7 +900,7 @@ def _post_run_model_debug_print(self): print("Linear constraints") if cons: for name, value in iteritems(cons): - print("{}: {}".format(name, value)) + print("{}: {}".format(name, repr(value))) else: print("None") print() @@ -911,7 +911,7 @@ def _post_run_model_debug_print(self): print("Objectives") if objs: for name, value in iteritems(objs): - print("{}: {}".format(name, value)) + print("{}: {}".format(name, repr(value))) else: print("None") print()