Skip to content

Commit

Permalink
Merge 7587d0b into fee4510
Browse files Browse the repository at this point in the history
  • Loading branch information
hschilling committed Nov 27, 2023
2 parents fee4510 + 7587d0b commit 36f09e4
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 5 deletions.
8 changes: 8 additions & 0 deletions openmdao/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,11 @@ def __deepcopy__(self, memo):
# Use this as a special value to be able to tell if the caller set a value for the optional
# out_stream argument. We run into problems running testflo if we use a default of sys.stdout.
_DEFAULT_OUT_STREAM = _ReprClass("DEFAULT_OUT_STREAM")

# Used in pyOptSparseDriver and Problem for coloring dir. The default is the reports directory
# which includes the directory
# named after the Problem name. But when the declare method in that class is called, the driver
# does not have a reference to the Problem so can't get the name. This serves as a flag that
# the default directory in the reports directory is what is wanted. Then in the run method,
# the actual default directory is used
_DEFAULT_REPORTS_DIR = _ReprClass("DEFAULT_PYOPT_SPARSE_OUTPUT_DIR")
8 changes: 8 additions & 0 deletions openmdao/core/problem.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Define the Problem class and a FakeComm class for non-MPI users."""

import __main__
import shutil

import sys
import pprint
Expand Down Expand Up @@ -349,6 +350,13 @@ def __init__(self, model=None, driver=None, comm=None, name=None, reports=_UNDEF
desc='Patterns for vars to exclude in recording '
'(processed post-includes). Uses fnmatch wildcards')

# Start a run by deleting any existing reports so that the files
# that are in that directory are all from this run and not a previous run
reports_dirpath = pathlib.Path(get_reports_dir()).joinpath(f'{self._name}')
if self.comm.rank == 0:
if os.path.isdir(reports_dirpath):
shutil.rmtree(reports_dirpath)

# register hooks for any reports
activate_reports(self._reports, self)

Expand Down
2 changes: 1 addition & 1 deletion openmdao/core/tests/test_partial_color.py
Original file line number Diff line number Diff line change
Expand Up @@ -1867,4 +1867,4 @@ def test_simple_partials_explicit(self, method):


if __name__ == '__main__':
unitest.main()
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
" if optimizer == 'IPOPT':\n",
" driver.opt_settings['file_print_level'] = 5\n",
" driver.options['print_results'] = False\n",
" driver.options['output_dir'] = None # will put the optimizer output file in the current directory\n",
"\n",
" # setup problem & initialize values\n",
" prob = om.Problem(model, driver)\n",
Expand Down Expand Up @@ -424,7 +425,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
"version": "3.11.6"
}
},
"nbformat": 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,6 @@
},
"outputs": [],
"source": [
"%%px\n",
"import openmdao.api as om\n",
"om.display_source(\"openmdao.test_suite.components.paraboloid_distributed.DistParabFeature\")"
]
Expand Down
42 changes: 40 additions & 2 deletions openmdao/drivers/pyoptsparse_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
formulating and solving nonlinear constrained optimization problems, with
additional MPI capability.
"""

import pathlib
import sys
import json
import signal
Expand All @@ -22,12 +22,13 @@
except Exception as err:
pyoptsparse = err

from openmdao.core.constants import INT_DTYPE
from openmdao.core.constants import INT_DTYPE, _DEFAULT_REPORTS_DIR, _ReprClass
from openmdao.core.analysis_error import AnalysisError
from openmdao.core.driver import Driver, RecordingDebugging
from openmdao.utils.class_util import WeakMethodWrapper
from openmdao.utils.mpi import FakeComm, MPI
from openmdao.utils.om_warnings import issue_warning, warn_deprecation
from openmdao.utils.reports_system import get_reports_dir

# what version of pyoptspare are we working with
if pyoptsparse and hasattr(pyoptsparse, '__version__'):
Expand Down Expand Up @@ -268,6 +269,10 @@ def _declare_options(self):
self.options.declare('hotstart_file', types=str, default=None, allow_none=True,
desc='File location of a pyopt_sparse optimization history to use '
'to hot start the optimization. Default is None.')
self.options.declare('output_dir', types=(str, _ReprClass), default=_DEFAULT_REPORTS_DIR,
allow_none=True,
desc='Directory location of pyopt_sparse output files.'
'Default is ./reports_directory/problem_name.')

@property
def hist_file(self):
Expand Down Expand Up @@ -374,6 +379,39 @@ def run(self):
self._quantities = []

optimizer = self.options['optimizer']

# Need to tell optimizer where to put its .out files
if self.options['output_dir'] is None:
self.options['output_dir'] = "."
elif self.options['output_dir'] == _DEFAULT_REPORTS_DIR:
problem = self._problem()
default_output_dir = pathlib.Path(get_reports_dir()).joinpath(problem._name)
pathlib.Path(default_output_dir).mkdir(parents=True, exist_ok=True)
self.options['output_dir'] = str(default_output_dir)
output_dir = self.options['output_dir']

if optimizer == 'ALPSO': # Actually, this is the root of two files generated
if problem.driver.opt_settings.get('filename') is None:
problem.driver.opt_settings['filename'] = f'{output_dir}/ALPSO.out'
elif optimizer == 'CONMIN':
if problem.driver.opt_settings.get('IFILE') is None:
problem.driver.opt_settings['IFILE'] = f'{output_dir}/CONMIN.out'
elif optimizer == 'IPOPT':
if problem.driver.opt_settings.get('output_file') is None:
problem.driver.opt_settings['output_file'] = f'{output_dir}/IPOPT.out'
# Nothing for NSGA2
elif optimizer == 'PSQP':
if problem.driver.opt_settings.get('IFILE') is None:
problem.driver.opt_settings['IFILE'] = f'{output_dir}/PSQP.out'
elif optimizer == 'SLSQP':
if problem.driver.opt_settings.get('IFILE') is None:
problem.driver.opt_settings['IFILE'] = f'{output_dir}/SLSQP.out'
elif optimizer == 'SNOPT':
if problem.driver.opt_settings.get('Print file') is None:
problem.driver.opt_settings['Print file'] = f'{output_dir}/SNOPT_print.out'
if problem.driver.opt_settings.get('Summary file') is None:
problem.driver.opt_settings['Summary file'] = f'{output_dir}/SNOPT_summary.out'

self._fill_NANs = not respects_fail_flag[self.options['optimizer']]

self._check_for_missing_objective()
Expand Down
2 changes: 2 additions & 0 deletions openmdao/drivers/tests/test_analysis_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ def setup_problem(self, optimizer, func=None):
if optimizer in self.opt_settings:
driver.opt_settings = self.opt_settings[optimizer]
driver.options['print_results'] = False
driver.options['output_dir'] = None # So output goes in current working directory
# that was the location when this test was written

# setup problem & initialize values
prob = om.Problem(model, driver)
Expand Down
106 changes: 106 additions & 0 deletions openmdao/drivers/tests/test_pyoptsparse_driver.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
""" Unit tests for the Pyoptsparse Driver."""

import copy
import pathlib
import unittest
import os.path

Expand All @@ -16,6 +17,7 @@
from openmdao.test_suite.components.sellar import SellarDerivativesGrouped
from openmdao.utils.assert_utils import assert_near_equal, assert_warning, assert_check_totals
from openmdao.utils.general_utils import set_pyoptsparse_opt, run_driver
from openmdao.utils.reports_system import get_reports_dir
from openmdao.utils.testing_utils import use_tempdirs, require_pyoptsparse
from openmdao.utils.om_warnings import OMDeprecationWarning
from openmdao.utils.mpi import MPI
Expand Down Expand Up @@ -3288,6 +3290,110 @@ def test_resize(self):
p.run_driver()
p.compute_totals()

@unittest.skipIf(OPT is None or OPTIMIZER is None, "only run if pyoptsparse is installed.")
@use_tempdirs
class TestPyoptSparseOutputFiles(unittest.TestCase):
# dict of optimizers with the values being a list of tuples of ( option_name, output_file_name )
optimizers_and_output_files = {
# ALPSO uses a single option `filename` to determine name of both output files
'ALPSO': [('filename', 'ALPSO_print.out'), ('filename', 'ALPSO_summary.out')],
'CONMIN': [('IFILE', 'CONMIN.out')],
'IPOPT': [('output_file', 'IPOPT.out')],
'PSQP': [('IFILE', 'PSQP.out')],
'SLSQP': [('IFILE', 'SLSQP.out')],
'SNOPT': [('Print file', 'SNOPT_print.out'), ('Summary file', 'SNOPT_summary.out')]
}

def createParaboloidProblem(self):
prob = om.Problem()
model = prob.model
model.add_subsystem('p1', om.IndepVarComp('x', 50.0), promotes=['*'])
model.add_subsystem('p2', om.IndepVarComp('y', 50.0), promotes=['*'])
model.add_subsystem('comp', Paraboloid(), promotes=['*'])
model.add_subsystem('con', om.ExecComp('c = - x + y'), promotes=['*'])
prob.set_solver_print(level=0)
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()
return prob

def run_and_test_default_output_dir(self, optimizer, output_file_names):
# default is to put the files in the reports directory under the problem name folder
# output_file_names is a list of tuples of setting name and output file name
prob = self.createParaboloidProblem()
prob.driver = pyOptSparseDriver(optimizer=optimizer, print_results=False)

if optimizer == 'ALPSO':
prob.driver.opt_settings['fileout'] = 3 # need this to be 3 to get the output files

prob.run_driver()
default_output_dir = pathlib.Path(get_reports_dir()).joinpath(prob._name)
for opt_setting_name, output_file_name in output_file_names:
output_file = default_output_dir.joinpath(output_file_name)
self.assertTrue(output_file.is_file(),
f"{output_file_name} output file not found at {str(output_file)}")

def run_and_test_previous_behavior_output_dir(self, optimizer, output_file_names):
# Previous behavior is to put files in current working directory. Indicated with None
# output_file_names is a list of tuples of setting name and output file name
prob = self.createParaboloidProblem()
prob.driver = pyOptSparseDriver(optimizer=optimizer, print_results=False)

if optimizer == 'ALPSO':
prob.driver.opt_settings['fileout'] = 3

prob.driver.options['output_dir'] = None

prob.run_driver()
for opt_setting_name, output_file_name in output_file_names:
output_file = pathlib.Path(output_file_name)
self.assertTrue(output_file.is_file(),
f"{output_file_name} output file not found at {str(output_file)}")

def run_and_test_user_set_output_dir(self, optimizer, output_file_names):
# output_file_names is a list of tuples of setting name and output file name

user_directory_name = 'user_reports_dir'
pathlib.Path(user_directory_name).mkdir(exist_ok=True)

prob = self.createParaboloidProblem()
prob.driver = pyOptSparseDriver(optimizer=optimizer, print_results=False)

if optimizer == 'ALPSO':
prob.driver.opt_settings['fileout'] = 3


prob.driver.options['output_dir'] = user_directory_name

prob.run_driver()

for opt_setting_name, output_file_name in output_file_names:
output_file_path = pathlib.Path(user_directory_name).joinpath(output_file_name)
self.assertTrue(output_file_path.is_file(),
f"{str(output_file_path)} output file not found at {str(output_file_path)}")

def test_default_output_dir(self):
for optimizer, output_files in self.optimizers_and_output_files.items():
_, loc_opt = set_pyoptsparse_opt(optimizer)
if loc_opt == optimizer: # Only do optimizers that are installed
self.run_and_test_default_output_dir(optimizer, output_files)

def test_previous_behavior_output_dir(self):
for optimizer, output_files in self.optimizers_and_output_files.items():
_, loc_opt = set_pyoptsparse_opt(optimizer)
if loc_opt == optimizer: # Only do optimizers that are installed
self.run_and_test_previous_behavior_output_dir(optimizer, output_files)

def test_user_set_output_dir(self):
for optimizer, output_files in self.optimizers_and_output_files.items():
_, loc_opt = set_pyoptsparse_opt(optimizer)
if loc_opt == optimizer: # Only do optimizers that are installed
self.run_and_test_user_set_output_dir(optimizer, output_files)




if __name__ == "__main__":
unittest.main()

0 comments on commit 36f09e4

Please sign in to comment.