Example #1
0
    def __init__(self):
        """ Initialize the default supports for nl solvers."""
        super(NonLinearSolver, self).__init__()

        # What this solver supports
        self.supports = OptionsDictionary(read_only=True)
        self.supports.add_option('uses_derivatives', False)
Example #2
0
    def __init__(self):
        self.options = OptionsDictionary()
        self.options.add_option('record_metadata', True)
        self.options.add_option('record_unknowns', True)
        self.options.add_option('record_params', False)
        self.options.add_option('record_resids', False)
        self.options.add_option('record_derivs', True,
                                desc='Set to True to record derivatives at the driver level')
        self.options.add_option('includes', ['*'],
                                desc='Patterns for variables to include in recording')
        self.options.add_option('excludes', [],
                                desc='Patterns for variables to exclude from recording '
                                '(processed after includes)')
        self.out = None

        # This is for drivers to determine if a recorder supports
        # real parallel recording (recording on each process), because
        # if it doesn't, the driver figures out what variables must
        # be gathered to rank 0 if running under MPI.
        #
        # By default, this is False, but it should be set to True
        # if the recorder will record data on each process to avoid
        # unnecessary gathering.
        self._parallel = False

        self._filtered = {}
Example #3
0
class KSComp(Component):
    """Aggregates a number of functions to a single value via the
    Kreisselmeier-Steinhauser Function."""
    def __init__(self, n=2):
        super(KS, self).__init__()

        self.n = n

        # Inputs
        self.add_param('g',
                       np.zeros((n, )),
                       desc="Array of function values to be aggregated")

        # Outputs
        self.add_output('KS', 0.0, desc="Value of the aggregate KS function")

        self.options = OptionsDictionary()
        self.options.add_option(rho,
                                0.1,
                                desc="Hyperparameter for the KS function")

        self._ks = KSfunction()

    def solve_nonlinear(self, params, unknowns, resids):
        """ Calculate output. """

        unknowns['KS'] = self._ks.compute(params['g'], self.options['rho'])

    def linearize(self, params, unknowns, resids):
        """ Calculate and save derivatives. (i.e., Jacobian) """

        #use g_max, exponsnte, summation from last executed point
        J = {}
        J['KS', 'g'] = np.hstack(self._ks.derivatives())
Example #4
0
    def __init__(self):
        super(ExternalCode, self).__init__()

        self.STDOUT   = STDOUT
        self.DEV_NULL = DEV_NULL

        # Input options for this Component
        self.options = OptionsDictionary()
        self.options.add_option('command', [], desc='command to be executed')
        self.options.add_option('env_vars', {}, desc='Environment variables required by the command')
        self.options.add_option('poll_delay', 0.0, lower=0.0,
            desc='Delay between polling for command completion. A value of zero will use an internally computed default')
        self.options.add_option('timeout', 0.0, lower=0.0,
                                desc='Maximum time to wait for command completion. A value of zero implies an infinite wait')
        self.options.add_option('check_external_outputs', True,
            desc='Check that all input or output external files exist')

        self.options.add_option( 'external_input_files', [],
            desc='(optional) list of input file names to check the pressence of before solve_nonlinear')
        self.options.add_option( 'external_output_files', [],
            desc='(optional) list of input file names to check the pressence of after solve_nonlinear')

        # Outputs of the run of the component or items that will not work with the OptionsDictionary
        self.return_code = 0 # Return code from the command
        self.timed_out = False # True if the command timed-out
        self.stdin  = self.DEV_NULL
        self.stdout = None
        self.stderr = "error.out"
Example #5
0
    def __init__(self):
        super(Driver, self).__init__()
        self.recorders = RecordingManager()

        # What this driver supports
        self.supports = OptionsDictionary(read_only=True)
        self.supports.add_option('inequality_constraints', True)
        self.supports.add_option('equality_constraints', True)
        self.supports.add_option('linear_constraints', True)
        self.supports.add_option('multiple_objectives', True)
        self.supports.add_option('two_sided_constraints', True)
        self.supports.add_option('integer_design_vars', True)

        # inheriting Drivers should override this setting and set it to False
        # if they don't use gradients.
        self.supports.add_option('gradients', True)

        # This driver's options
        self.options = OptionsDictionary()

        self._desvars = OrderedDict()
        self._objs = OrderedDict()
        self._cons = OrderedDict()

        self._voi_sets = []
        self._vars_to_record = None

        # We take root during setup
        self.root = None

        self.iter_count = 0
        self.dv_conversions = {}
        self.fn_conversions = {}
Example #6
0
class KSComp(Component):
    """Aggregates a number of functions to a single value via the
    Kreisselmeier-Steinhauser Function."""

    def __init__(self, n=2):
        super(KS, self).__init__()

        self.n = n

        # Inputs
        self.add_param('g', np.zeros((n, )),
                       desc="Array of function values to be aggregated")

        # Outputs
        self.add_output('KS', 0.0,
                        desc="Value of the aggregate KS function")

        self.options = OptionsDictionary()
        self.options.add_option(rho, 0.1,
                                desc="Hyperparameter for the KS function")

        self._ks = KSfunction()

    def solve_nonlinear(self, params, unknowns, resids):
        """ Calculate output. """

        unknowns['KS'] = self._ks.compute(params['g'], self.options['rho'])

    def jacobian(self, params, unknowns, resids):
        """ Calculate and save derivatives. (i.e., Jacobian) """

        #use g_max, exponsnte, summation from last executed point
        J = {}
        J['KS', 'g'] = np.hstack(self._ks.derivatives())
Example #7
0
 def __init__(self):
     self.iter_count = 0
     self.options = OptionsDictionary()
     desc = 'Set to 0 to disable printing, set to 1 to print the ' \
            'residual to stdout each iteration, set to 2 to print ' \
            'subiteration residuals as well.'
     self.options.add_option('iprint', 0, values=[0, 1, 2], desc=desc)
     self.recorders = RecordingManager()
     self.local_meta = None
Example #8
0
class NonLinearSolver(SolverBase):
    """ Base class for all nonlinear solvers. Inherit from this class to create a
    new custom nonlinear solver.

    Options
    -------
    options['iprint'] :  int(0)
        Set to 0 to disable printing, set to 1 to print the residual to stdout
        each iteration, set to 2 to print subiteration residuals as well.
    """

    def __init__(self):
        """ Initialize the default supports for nl solvers."""
        super(NonLinearSolver, self).__init__()

        # What this solver supports
        self.supports = OptionsDictionary(read_only=True)
        self.supports.add_option('uses_derivatives', False)

    def add_recorder(self, recorder):
        """Appends the given recorder to this solver's list of recorders.

        Args
        ----
        recorder: `BaseRecorder`
            A recorder object.
        """
        self.recorders.append(recorder)

    def solve(self, params, unknowns, resids, system, metadata=None):
        """ Drive all residuals in self.system and all subsystems to zero.
        This includes all implicit components. This function must be defined
        when inheriting.

        Args
        ----
        params : `VecWrapper`
            `VecWrapper` containing parameters. (p)

        unknowns : `VecWrapper`
            `VecWrapper` containing outputs and states. (u)

        resids : `VecWrapper`
            `VecWrapper` containing residuals. (r)

        system : `System`
            Parent `System` object.

        metadata : dict, optional
            Dictionary containing execution metadata (e.g. iteration coordinate).
        """
        pass
Example #9
0
class NonLinearSolver(SolverBase):
    """ Base class for all nonlinear solvers. Inherit from this class to create a
    new custom nonlinear solver.

    Options
    -------
    options['iprint'] :  int(0)
        Set to 0 to disable printing, set to 1 to print iteration totals to
        stdout, set to 2 to print the residual each iteration to stdout.
    """
    def __init__(self):
        """ Initialize the default supports for nl solvers."""
        super(NonLinearSolver, self).__init__()

        # What this solver supports
        self.supports = OptionsDictionary(read_only=True)
        self.supports.add_option('uses_derivatives', False)

    def add_recorder(self, recorder):
        """Appends the given recorder to this solver's list of recorders.

        Args
        ----
        recorder: `BaseRecorder`
            A recorder object.
        """
        self.recorders.append(recorder)

    def solve(self, params, unknowns, resids, system, metadata=None):
        """ Drive all residuals in self.system and all subsystems to zero.
        This includes all implicit components. This function must be defined
        when inheriting.

        Args
        ----
        params : `VecWrapper`
            `VecWrapper` containing parameters. (p)

        unknowns : `VecWrapper`
            `VecWrapper` containing outputs and states. (u)

        resids : `VecWrapper`
            `VecWrapper` containing residuals. (r)

        system : `System`
            Parent `System` object.

        metadata : dict, optional
            Dictionary containing execution metadata (e.g. iteration coordinate).
        """
        pass
Example #10
0
 def __init__(self):
     self.iter_count = 0
     self.options = OptionsDictionary()
     desc = 'Set to 0 to disable printing, set to 1 to print the ' \
            'residual to stdout each iteration, set to 2 to print ' \
            'subiteration residuals as well.'
     self.options.add_option('iprint', 0, values=[0, 1, 2], desc=desc)
     self.options.add_option(
         'err_on_maxiter',
         False,
         desc='If True, raise an AnalysisError if not converged at maxiter.'
     )
     self.recorders = RecordingManager()
     self.local_meta = None
Example #11
0
    def __init__(self):
        self.iter_count = 0
        self.options = OptionsDictionary()
        desc =  "Set to 0 to print only failures, set to 1 to print iteration totals to" + \
                "stdout, set to 2 to print the residual each iteration to stdout," + \
                "or -1 to suppress all printing."

        self.options.add_option('iprint', 0, values=[-1, 0, 1, 2], desc=desc)
        self.options.add_option(
            'err_on_maxiter',
            False,
            desc='If True, raise an AnalysisError if not converged at maxiter.'
        )
        self.recorders = RecordingManager()
        self.local_meta = None
Example #12
0
    def __init__(self):
        super(Driver, self).__init__()
        self.recorders = RecordingManager()

        # What this driver supports
        self.supports = OptionsDictionary(read_only=True)
        self.supports.add_option("inequality_constraints", True)
        self.supports.add_option("equality_constraints", True)
        self.supports.add_option("linear_constraints", True)
        self.supports.add_option("multiple_objectives", True)
        self.supports.add_option("two_sided_constraints", True)
        self.supports.add_option("integer_design_vars", True)

        # This driver's options
        self.options = OptionsDictionary()

        self._desvars = OrderedDict()
        self._objs = OrderedDict()
        self._cons = OrderedDict()

        self._voi_sets = []
        self._vars_to_record = None

        # We take root during setup
        self.root = None

        self.iter_count = 0
        self.dv_conversions = {}
        self.fn_conversions = {}
Example #13
0
    def __init__(self, name, s, P, Cx, scaler=1.):
        super(FFDSpline, self).__init__()

        self._name = name

        opt = self.spline_options = OptionsDictionary()
        opt.add_option('spline_type', 'bezier', values=['pchip', 'bezier'],\
                       desc='spline type used in FFD')
        self.nC = Cx.shape[0]
        self.Cx = Cx
        self.s = s
        self.Pinit = P
        self._size = P.shape[0]
        self.scaler = scaler

        self.add_param(name + '_C',
                       np.zeros(self.nC),
                       desc='spline control points')
        self.add_output(name, np.zeros(self._size))
        self.add_output(name + '_curv', np.zeros(self._size))

        self._init_called = False
        self.spline = None

        self.set_spline(self.spline_options['spline_type'])
Example #14
0
    def __init__(self):
        """ Initialize the default supports for nl solvers."""
        super(NonLinearSolver, self).__init__()

        # What this solver supports
        self.supports = OptionsDictionary(read_only=True)
        self.supports.add_option('uses_derivatives', False)
Example #15
0
    def __init__(self):
        super(MySimpleDriver, self).__init__()

        # What we support
        self.supports['inequality_constraints'] = True
        self.supports['equality_constraints'] = False
        self.supports['linear_constraints'] = False
        self.supports['multiple_objectives'] = False

        # My driver options
        self.options = OptionsDictionary()
        self.options.add_option('tol', 1e-4)
        self.options.add_option('maxiter', 10)

        self.alpha = .01
        self.violated = []
Example #16
0
 def __init__(self):
     self.iter_count = 0
     self.options = OptionsDictionary()
     desc = 'Set to 0 to disable printing, set to 1 to print the ' \
            'residual to stdout each iteration, set to 2 to print ' \
            'subiteration residuals as well.'
     self.options.add_option('iprint', 0, values=[0, 1, 2], desc=desc)
     self.recorders = RecordingManager()
     self.local_meta = None
Example #17
0
    def __init__(self, n=2):
        super(KS, self).__init__()

        self.n = n

        # Inputs
        self.add_param('g',
                       np.zeros((n, )),
                       desc="Array of function values to be aggregated")

        # Outputs
        self.add_output('KS', 0.0, desc="Value of the aggregate KS function")

        self.options = OptionsDictionary()
        self.options.add_option(rho,
                                0.1,
                                desc="Hyperparameter for the KS function")

        self._ks = KSfunction()
Example #18
0
    def __init__(self):
        self.name = ''
        self.pathname = ''

        self._subsystems = OrderedDict()

        self._params_dict = OrderedDict()
        self._unknowns_dict = OrderedDict()

        # specify which variables are promoted up to the parent.  Wildcards
        # are allowed.
        self._promotes = ()

        self.comm = None

        # for those Systems that perform file I/O
        self.directory = ''

        # if True, create any directories needed by this System that don't exist
        self.create_dirs = False

        # create placeholders for all of the vectors
        self.unknowns = _PlaceholderVecWrapper('unknowns')
        self.resids = _PlaceholderVecWrapper('resids')
        self.params = _PlaceholderVecWrapper('params')
        self.dunknowns = _PlaceholderVecWrapper('dunknowns')
        self.dresids = _PlaceholderVecWrapper('dresids')

        opt = self.fd_options = OptionsDictionary()
        opt.add_option('force_fd',
                       False,
                       desc="Set to True to finite difference this system.")
        opt.add_option(
            'form',
            'forward',
            values=['forward', 'backward', 'central', 'complex_step'],
            desc="Finite difference mode. (forward, backward, central) "
            "You can also set to 'complex_step' to peform the complex "
            "step method if your components support it.")
        opt.add_option("step_size",
                       1.0e-6,
                       lower=0.0,
                       desc="Default finite difference stepsize")
        opt.add_option("step_type",
                       'absolute',
                       values=['absolute', 'relative'],
                       desc='Set to absolute, relative')

        self._impl = None

        self._num_par_fds = 1  # this will be >1 for ParallelFDGroup
        self._par_fd_id = 0  # for ParallelFDGroup, this will be >= 0 and
        # <= the number of parallel FDs

        self._reset()  # initialize some attrs that are set during setup
Example #19
0
 def __init__(self):
     self.iter_count = 0
     self.options = OptionsDictionary()
     desc = 'Set to 0 to disable printing, set to 1 to print the ' \
            'residual to stdout each iteration, set to 2 to print ' \
            'subiteration residuals as well.'
     self.options.add_option('iprint', 0, values=[0, 1, 2], desc=desc)
     self.options.add_option('err_on_maxiter', False,
         desc='If True, raise an AnalysisError if not converged at maxiter.')
     self.recorders = RecordingManager()
     self.local_meta = None
Example #20
0
    def __init__(self):
        self.iter_count = 0
        self.options = OptionsDictionary()
        desc =  "Set to 0 to print only failures, set to 1 to print iteration totals to" + \
                "stdout, set to 2 to print the residual each iteration to stdout," + \
                "or -1 to suppress all printing."

        self.options.add_option('iprint', 0, values=[-1, 0, 1, 2], desc=desc)
        self.options.add_option('err_on_maxiter', False,
            desc='If True, raise an AnalysisError if not converged at maxiter.')
        self.recorders = RecordingManager()
        self.local_meta = None
    def __init__(self):
        super(MySimpleDriver, self).__init__()

        # What we support
        self.supports["inequality_constraints"] = True
        self.supports["equality_constraints"] = False
        self.supports["linear_constraints"] = False
        self.supports["multiple_objectives"] = False

        # My driver options
        self.options = OptionsDictionary()
        self.options.add_option("tol", 1e-4)
        self.options.add_option("maxiter", 10)

        self.alpha = 0.01
        self.violated = []
    def __init__(self):
        super(MySimpleDriver, self).__init__()

        # What we support
        self.supports['inequality_constraints'] = True
        self.supports['equality_constraints'] = False
        self.supports['linear_constraints'] = False
        self.supports['multiple_objectives'] = False

        # My driver options
        self.options = OptionsDictionary()
        self.options.add_option('tol', 1e-4)
        self.options.add_option('maxiter', 10)

        self.alpha = .01
        self.violated = []
Example #23
0
    def __init__(self, n=2):
        super(KS, self).__init__()

        self.n = n

        # Inputs
        self.add_param('g', np.zeros((n, )),
                       desc="Array of function values to be aggregated")

        # Outputs
        self.add_output('KS', 0.0,
                        desc="Value of the aggregate KS function")

        self.options = OptionsDictionary()
        self.options.add_option(rho, 0.1,
                                desc="Hyperparameter for the KS function")

        self._ks = KSfunction()
Example #24
0
    def __init__(self, name, s, P, Cx, scaler=1.):
        super(FFDSpline, self).__init__()

        self._name = name

        opt = self.spline_options = OptionsDictionary()
        opt.add_option('spline_type', 'bezier', values=['linear',
                                                        'pchip',
                                                        'bezier'],\
                       desc='spline type used in FFD')
        self.nC = Cx.shape[0]
        self.Cx = Cx
        self.s = s
        self._iCx0 = 0
        self._iCx1 = None
        if self.Cx[0] > 0.:
            self._iCx0 = np.where(
                np.abs(self.s - self.Cx[0]) == np.abs(self.s -
                                                      self.Cx[0]).min())[0]
            if isinstance(self._iCx0, np.ndarray):
                self._iCx0 = self._iCx0[0]
        if self.Cx[-1] < 1.:
            self._iCx1 = np.where(
                np.abs(self.s -
                       self.Cx[-1]) == np.abs(self.s -
                                              self.Cx[-1]).min())[0] + 1
            if isinstance(self._iCx1, np.ndarray):
                self._iCx1 = self._iCx1[0]
        self.Pinit = P
        self._size = P.shape[0]
        self.scaler = scaler

        self.add_param(name + '_C',
                       np.zeros(self.nC),
                       desc='spline control points')
        self.add_output(name, np.zeros(self._size))
        self.add_output(name + '_curv', np.zeros(self._size))

        self._init_called = False
        self.spline = None

        self.set_spline(self.spline_options['spline_type'])
Example #25
0
    def __init__(self, n=2, h=.01):
        super(RK4, self).__init__()

        self.h = h

        # Inputs
        # All inputs are defined in subclasses.

        # Options
        self.options = opt = OptionsDictionary()
        opt.add_option('state_var', '',
                       desc="Name of the variable to be used for time "
                       "integration")
        opt.add_option('init_state_var', '',
                       desc="Name of the variable to be used for initial "
                       "conditions")
        opt.add_option('external_vars', [],
                       desc="List of names of variables that are external "
                       "to the system but DO vary with time.")
        opt.add_option('fixed_external_vars', [],
                       desc="List of names of variables that are "
                       "external to the system but DO NOT "
                       "vary with time.")
Example #26
0
class BaseRecorder(object):
    """ This is a base class for all case recorders and is not a functioning
    case recorder on its own.

    Options
    -------
    options['record_metadata'] :  bool(True)
        Tells recorder whether to record variable attribute metadata.
    options['record_unknowns'] :  bool(True)
        Tells recorder whether to record the unknowns vector.
    options['record_params'] :  bool(False)
        Tells recorder whether to record the params vector.
    options['record_resids'] :  bool(False)
        Tells recorder whether to record the ressiduals vector.
    options['record_derivs'] :  bool(True)
        Tells recorder whether to record derivatives that are requested by a `Driver`.
    options['includes'] :  list of strings
        Patterns for variables to include in recording.
    options['excludes'] :  list of strings
        Patterns for variables to exclude in recording (processed after includes).
    """

    def __init__(self):
        self.options = OptionsDictionary()
        self.options.add_option('record_metadata', True)
        self.options.add_option('record_unknowns', True)
        self.options.add_option('record_params', False)
        self.options.add_option('record_resids', False)
        self.options.add_option('record_derivs', True,
                                desc='Set to True to record derivatives at the driver level')
        self.options.add_option('includes', ['*'],
                                desc='Patterns for variables to include in recording')
        self.options.add_option('excludes', [],
                                desc='Patterns for variables to exclude from recording '
                                '(processed after includes)')
        self.out = None

        # This is for drivers to determine if a recorder supports
        # real parallel recording (recording on each process), because
        # if it doesn't, the driver figures out what variables must
        # be gathered to rank 0 if running under MPI.
        #
        # By default, this is False, but it should be set to True
        # if the recorder will record data on each process to avoid
        # unnecessary gathering.
        self._parallel = False

        self._filtered = {}
        # TODO: System specific includes/excludes

    def startup(self, group):
        """ Prepare for a new run.

        Args
        ----
        group : `Group`
            Group that owns this recorder.
        """

        myparams = myunknowns = myresids = set()

        if MPI:
            rank = group.comm.rank
            owned = group._owning_ranks

        # Compute the inclusion lists for recording
        if self.options['record_params']:
            myparams = set(filter(self._check_path, group.params))
        if self.options['record_unknowns']:
            myunknowns = set(filter(self._check_path, group.unknowns))
        if self.options['record_resids']:
            myresids = set(filter(self._check_path, group.resids))

        self._filtered[group.pathname] = {
            'p': myparams,
            'u': myunknowns,
            'r': myresids
        }

    def _check_path(self, path):
        """ Return True if `path` should be recorded. """

        excludes = self.options['excludes']

        # First see if it's included
        for pattern in self.options['includes']:
            if fnmatch(path, pattern):
                # We found a match. Check to see if it is excluded.
                for ex_pattern in excludes:
                    if fnmatch(path, ex_pattern):
                        return False
                return True

        # Did not match anything in includes.
        return False

    def _get_pathname(self, iteration_coordinate):
        '''
        Converts an iteration coordinate to key to index
        `_filtered` to retrieve names of variables to be recorded.
        '''
        return '.'.join(iteration_coordinate[5::2])

    def _filter_vector(self, vecwrapper, key, iteration_coordinate):
        '''
        Returns a dict that is a subset of the given vecwrapper
        to be recorded.
        '''
        if not vecwrapper:
            return vecwrapper

        pathname = self._get_pathname(iteration_coordinate)
        filt = self._filtered[pathname][key]
        return {k: vecwrapper[k] for k in filt}

    def record_metadata(self, group):
        """Writes the metadata of the given group

        Args
        ----
        group : `System`
            `System` containing vectors
        """
        raise NotImplementedError()

    def record_iteration(self, params, unknowns, resids, metadata):
        """
        Writes the provided data.

        Args
        ----
        params : dict
            Dictionary containing parameters. (p)

        unknowns : dict
            Dictionary containing outputs and states. (u)

        resids : dict
            Dictionary containing residuals. (r)

        metadata : dict, optional
            Dictionary containing execution metadata (e.g. iteration coordinate).
        """
        raise NotImplementedError()

    def record_derivatives(self, derivs, metadata):
        """Writes the metadata of the given group

        Args
        ----
        derivs : dict
            Dictionary containing derivatives

        metadata : dict, optional
            Dictionary containing execution metadata (e.g. iteration coordinate).
        """
        raise NotImplementedError()

    def close(self):
        """Closes `out` unless it's ``sys.stdout``, ``sys.stderr``, or StringIO.
        Note that a closed recorder will do nothing in :meth:`record`, and
        closing a closed recorder also does nothing.
        """
        # Closing a StringIO deletes its contents.
        if self.out not in (None, sys.stdout, sys.stderr):
            if not isinstance(self.out, StringIO):
                self.out.close()
            self.out = None
Example #27
0
class SolverBase(object):
    """ Common base class for Linear and Nonlinear solver. Should not be used
    by users. Always inherit from `LinearSolver` or `NonlinearSolver`."""
    def __init__(self):
        self.iter_count = 0
        self.options = OptionsDictionary()
        desc = 'Set to 0 to disable printing, set to 1 to print the ' \
               'residual to stdout each iteration, set to 2 to print ' \
               'subiteration residuals as well.'
        self.options.add_option('iprint', 0, values=[0, 1, 2], desc=desc)
        self.recorders = RecordingManager()
        self.local_meta = None

    def setup(self, sub):
        """ Solvers override to define post-setup initiailzation.

        Args
        ----
        sub: `System`
            System that owns this solver.
        """
        pass

    def cleanup(self):
        """ Clean up resources prior to exit. """
        self.recorders.close()

    def print_norm(self,
                   solver_string,
                   pathname,
                   iteration,
                   res,
                   res0,
                   msg=None,
                   indent=0,
                   solver='NL'):
        """ Prints out the norm of the residual in a neat readable format.

        Args
        ----
        solver_string: string
            Unique string to identify your solver type (e.g., 'LN_GS' or
            'NEWTON').

        pathname: dict
            Parent system pathname.

        iteration: int
            Current iteration number

        res: float
            Absolute residual value.

        res0: float
            Baseline initial residual for relative comparison.

        msg: string, optional
            Message that indicates convergence.

        ident: int, optional
            Additional indentation levels for subiterations.

        solver: string, optional
            Solver type if not LN or NL (mostly for line search operations.)
        """
        if pathname == '':
            name = 'root'
        else:
            name = 'root.' + pathname

        # Find indentation level
        level = pathname.count('.')
        # No indentation for driver; top solver is no indentation.
        level = level + indent

        indent = '   ' * level
        if msg is not None:
            form = indent + '[%s] %s: %s   %d | %s'
            print(form % (name, solver, solver_string, iteration, msg))
            return

        form = indent + '[%s] %s: %s   %d | %.9g %.9g'
        print(form % (name, solver, solver_string, iteration, res, res / res0))

    def print_all_convergence(self):
        """ Turns on iprint for this solver and all subsolvers. Override if
        your solver has subsolvers."""
        self.options['iprint'] = 1

    def generate_docstring(self):
        """
        Generates a numpy-style docstring for a user-created System class.

        Returns
        -------
        docstring : str
                string that contains a basic numpy docstring.

        """
        #start the docstring off
        docstring = '    \"\"\"\n'

        #Put options into docstring
        firstTime = 1
        #for py3.4, items from vars must come out in same order.
        from collections import OrderedDict
        v = OrderedDict(sorted(vars(self).items()))
        for key, value in v.items():
            if type(value) == OptionsDictionary:
                if firstTime:  #start of Options docstring
                    docstring += '\n    Options\n    -------\n'
                    firstTime = 0
                for (name, val) in sorted(value.items()):
                    docstring += "    " + key + "['"
                    docstring += name + "']"
                    docstring += " :  " + type(val).__name__
                    docstring += "("
                    if type(val).__name__ == 'str': docstring += "'"
                    docstring += str(val)
                    if type(val).__name__ == 'str': docstring += "'"
                    docstring += ")\n"

                    desc = value._options[name]['desc']
                    if (desc):
                        docstring += "        " + desc + "\n"

        #finish up docstring
        docstring += '\n    \"\"\"\n'
        return docstring
Example #28
0
class Driver(object):
    """ Base class for drivers in OpenMDAO. Drivers can only be placed in a
    Problem, and every problem has a Driver. Driver is the simplest driver that
    runs (solves using solve_nonlinear) a problem once.
    """

    def __init__(self):
        super(Driver, self).__init__()
        self.recorders = RecordingManager()

        # What this driver supports
        self.supports = OptionsDictionary(read_only=True)
        self.supports.add_option('inequality_constraints', True)
        self.supports.add_option('equality_constraints', True)
        self.supports.add_option('linear_constraints', True)
        self.supports.add_option('multiple_objectives', True)
        self.supports.add_option('two_sided_constraints', True)
        self.supports.add_option('integer_design_vars', True)

        # inheriting Drivers should override this setting and set it to False
        # if they don't use gradients.
        self.supports.add_option('gradients', True)

        # This driver's options
        self.options = OptionsDictionary()

        self._desvars = OrderedDict()
        self._objs = OrderedDict()
        self._cons = OrderedDict()

        self._voi_sets = []
        self._vars_to_record = None

        # We take root during setup
        self.root = None

        self.iter_count = 0
        self.dv_conversions = {}
        self.fn_conversions = {}

    def _setup(self):
        """ Updates metadata for params, constraints and objectives, and
        check for errors. Also determines all variables that need to be
        gathered for case recording.
        """
        root = self.root
        desvars = OrderedDict()
        objs = OrderedDict()
        cons = OrderedDict()

        if self.__class__ is Driver:
            has_gradients = False
        else:
            has_gradients = self.supports['gradients']

        item_tups = [
            ('Parameter', self._desvars, desvars),
            ('Objective', self._objs, objs),
            ('Constraint', self._cons, cons)
        ]

        for item_name, item, newitem in item_tups:
            for name, meta in iteritems(item):

                # Check validity of variable
                if name not in root.unknowns:
                    msg = "{} '{}' not found in unknowns."
                    msg = msg.format(item_name, name)
                    raise ValueError(msg)

                rootmeta = root.unknowns.metadata(name)
                if name in self._desvars:
                    rootmeta['is_desvar'] = True
                if name in self._objs:
                    rootmeta['is_objective'] = True
                if name in self._cons:
                    rootmeta['is_constraint'] = True

                if MPI and 'src_indices' in rootmeta:
                    raise ValueError("'%s' is a distributed variable and may "
                                     "not be used as a design var, objective, "
                                     "or constraint." % name)

                if has_gradients and rootmeta.get('pass_by_obj'):
                    if 'optimizer' in self.options:
                        oname = self.options['optimizer']
                    else:
                        oname = self.__class__.__name__
                    raise RuntimeError("%s '%s' is a 'pass_by_obj' variable "
                                       "and can't be used with a gradient "
                                       "based driver of type '%s'." %
                                       (item_name, name, oname))

                # Size is useful metadata to save
                if 'indices' in meta:
                    meta['size'] = len(meta['indices'])
                else:
                    meta['size'] = rootmeta['size']
                newitem[name] = meta

        self._desvars = desvars
        self._objs = objs
        self._cons = cons

        # Cache scalers for derivative calculation

        self.dv_conversions = OrderedDict()
        for name, meta in iteritems(desvars):
            scaler = meta.get('scaler')
            if isinstance(scaler, np.ndarray):
                if all(scaler == 1.0):
                    continue
            elif scaler == 1.0:
                continue

            self.dv_conversions[name] = np.reciprocal(scaler)

        self.fn_conversions = OrderedDict()
        for name, meta in chain(iteritems(objs), iteritems(cons)):
            scaler = meta.get('scaler')
            if isinstance(scaler, np.ndarray):
                if all(scaler == 1.0):
                    continue
            elif scaler == 1.0:
                continue

            self.fn_conversions[name] = scaler

    def _setup_communicators(self, comm, parent_dir):
        """
        Assign a communicator to the root `System`.

        Args
        ----
        comm : an MPI communicator (real or fake)
            The communicator being offered by the Problem.

        parent_dir : str
            Absolute directory of the Problem.
        """
        self.root._setup_communicators(comm, parent_dir)

    def get_req_procs(self):
        """
        Returns
        -------
        tuple
            A tuple of the form (min_procs, max_procs), indicating the
            min and max processors usable by this `Driver`.
        """
        return self.root.get_req_procs()

    def cleanup(self):
        """ Clean up resources prior to exit. """
        self.recorders.close()

    def _map_voi_indices(self):
        poi_indices = OrderedDict()
        qoi_indices = OrderedDict()
        for name, meta in chain(iteritems(self._cons), iteritems(self._objs)):
            # set indices of interest
            if 'indices' in meta:
                qoi_indices[name] = meta['indices']

        for name, meta in iteritems(self._desvars):
            # set indices of interest
            if 'indices' in meta:
                poi_indices[name] = meta['indices']

        return poi_indices, qoi_indices

    def _of_interest(self, voi_list):
        """Return a list of tuples, with the given voi_list organized
        into tuples based on the previously defined grouping of VOIs.
        """
        vois = []
        remaining = set(voi_list)
        for voi_set in self._voi_sets:
            vois.append([])

        for i, voi_set in enumerate(self._voi_sets):
            for v in voi_list:
                if v in voi_set:
                    vois[i].append(v)
                    remaining.remove(v)

        vois = [tuple(x) for x in vois if x]

        for v in voi_list:
            if v in remaining:
                vois.append((v,))

        return vois

    def desvars_of_interest(self):
        """
        Returns
        -------
        list of tuples of str
            The list of design vars, organized into tuples according to
            previously defined VOI groups.
        """
        return self._of_interest(self._desvars)

    def outputs_of_interest(self):
        """
        Returns
        -------
        list of tuples of str
            The list of constraints and objectives, organized into tuples
            according to previously defined VOI groups.
        """
        return self._of_interest(list(chain(self._objs, self._cons)))

    def parallel_derivs(self, vnames):
        """
        Specifies that the named variables of interest are to be grouped
        together so that their derivatives can be solved for concurrently.

        Args
        ----
        vnames : iter of str
            The names of variables of interest that are to be grouped.
        """
        #make sure all vnames are desvars, constraints, or objectives
        for n in vnames:
            if not (n in self._desvars or n in self._objs or n in self._cons):
                raise RuntimeError("'%s' is not a param, objective, or "
                                   "constraint" % n)
        for grp in self._voi_sets:
            for vname in vnames:
                if vname in grp:
                    msg = "'%s' cannot be added to VOI set %s because it " + \
                          "already exists in VOI set: %s"
                    raise RuntimeError(msg % (vname, tuple(vnames), grp))

        param_intsect = set(vnames).intersection(self._desvars.keys())

        if param_intsect and len(param_intsect) != len(vnames):
            raise RuntimeError("%s cannot be grouped because %s are design "
                               "vars and %s are not." %
                               (vnames, list(param_intsect),
                                list(set(vnames).difference(param_intsect))))

        if MPI:
            self._voi_sets.append(tuple(vnames))
        else:
            warnings.warn("parallel derivs %s specified but not running under MPI")

    def add_recorder(self, recorder):
        """
        Adds a recorder to the driver.

        Args
        ----
        recorder : BaseRecorder
           A recorder instance.
        """
        self.recorders.append(recorder)

    def add_desvar(self, name, lower=None, upper=None,
                   low=None, high=None,
                   indices=None, adder=0.0, scaler=1.0):
        """
        Adds a design variable to this driver.

        Args
        ----
        name : string
           Name of the design variable in the root system.

        lower : float or ndarray, optional
            Lower boundary for the param

        upper : upper or ndarray, optional
            Upper boundary for the param

        indices : iter of int, optional
            If a param is an array, these indicate which entries are of
            interest for derivatives.

        adder : float or ndarray, optional
            Value to add to the model value to get the scaled value. Adder
            is first in precedence.

        scaler : float or ndarray, optional
            value to multiply the model value to get the scaled value. Scaler
            is second in precedence.
        """

        if name in self._desvars:
            msg = "Desvar '{}' already exists."
            raise RuntimeError(msg.format(name))

        if low is not None or high is not None:
            warnings.simplefilter('always', DeprecationWarning)
            warnings.warn("'low' and 'high' are deprecated. "
                          "Use 'lower' and 'upper' instead.",
                          DeprecationWarning,stacklevel=2)
            warnings.simplefilter('ignore', DeprecationWarning)
            if low is not None and lower is None:
                lower = low
            if high is not None and upper is None:
                upper = high

        if isinstance(lower, np.ndarray):
            lower = lower.flatten()
        elif lower is None or lower == -float('inf'):
            lower = -sys.float_info.max

        if isinstance(upper, np.ndarray):
            upper = upper.flatten()
        elif upper is None or upper == float('inf'):
            upper = sys.float_info.max

        if isinstance(adder, np.ndarray):
            adder = adder.flatten().astype('float')
        else:
            adder = float(adder)

        if isinstance(scaler, np.ndarray):
            scaler = scaler.flatten().astype('float')
        else:
            scaler = float(scaler)

        # Scale the lower and upper values
        lower = (lower + adder)*scaler
        upper = (upper + adder)*scaler

        param = OrderedDict()
        param['lower'] = lower
        param['upper'] = upper
        param['adder'] = adder
        param['scaler'] = scaler
        if indices:
            param['indices'] = np.array(indices, dtype=int)

        self._desvars[name] = param

    def add_param(self, name, lower=None, upper=None, indices=None, adder=0.0,
                  scaler=1.0):
        """
        Deprecated.  Use ``add_desvar`` instead.
        """
        warnings.simplefilter('always', DeprecationWarning)
        warnings.warn("Driver.add_param() is deprecated. Use add_desvar() instead.",
                      DeprecationWarning,stacklevel=2)
        warnings.simplefilter('ignore', DeprecationWarning)

        self.add_desvar(name, lower=lower, upper=upper, indices=indices, adder=adder,
                        scaler=scaler)

    def get_desvars(self):
        """ Returns a dict of possibly distributed design variables.

        Returns
        -------
        dict
            Keys are the param object names, and the values are the param
            values.
        """
        desvars = OrderedDict()

        for key, meta in iteritems(self._desvars):
            desvars[key] = self._get_distrib_var(key, meta, 'design var')

        return desvars

    def _get_distrib_var(self, name, meta, voi_type):
        uvec = self.root.unknowns
        comm = self.root.comm
        nproc = comm.size
        iproc = comm.rank

        if nproc > 1:
            owner = self.root._owning_ranks[name]
            if iproc == owner:
                flatval = uvec._dat[name].val
            else:
                flatval = None
        else:
            owner = 0
            flatval = uvec._dat[name].val

        if 'indices' in meta and not (nproc > 1 and owner != iproc):
            # Make sure our indices are valid
            try:
                flatval = flatval[meta['indices']]
            except IndexError:
                msg = "Index for {} '{}' is out of bounds. "
                msg += "Requested index: {}, "
                msg += "shape: {}."
                raise IndexError(msg.format(voi_type, name, meta['indices'],
                                            uvec.metadata(name)['shape']))

        if nproc > 1:
            # TODO: use Bcast for improved performance
            if trace:
                debug("%s.driver._get_distrib_var bcast: val=%s" % (self.root.pathname, flatval))
            flatval = comm.bcast(flatval, root=owner)
            if trace:
                debug("%s.driver._get_distrib_var bcast DONE" % self.root.pathname)

        scaler = meta['scaler']
        adder = meta['adder']

        if isinstance(scaler, np.ndarray) or isinstance(adder, np.ndarray) \
           or scaler != 1.0 or adder != 0.0:
            return (flatval + adder)*scaler
        else:
            return flatval

    def get_desvar_metadata(self):
        """ Returns a dict of design variable metadata.

        Returns
        -------
        dict
            Keys are the param object names, and the values are the param
            values.
        """
        return self._desvars

    def set_desvar(self, name, value):
        """ Sets a design variable.

        Args
        ----
        name : string
            Name of the design variable in the root system.

        val : ndarray or float
            value to assign to the design variable.
        """
        val = self.root.unknowns._dat[name].val
        if not isinstance(val, _ByObjWrapper) and \
                       self.root.unknowns._dat[name].val.size == 0:
            return

        meta = self._desvars[name]
        scaler = meta['scaler']
        adder = meta['adder']
        if isinstance(scaler, np.ndarray) or isinstance(adder, np.ndarray) \
           or scaler != 1.0 or adder != 0.0:
            value = value/scaler - adder

        # Only set the indices we requested when we set the design variable.
        idx = meta.get('indices')
        if idx is not None:
            self.root.unknowns[name][idx] = value
        else:
            self.root.unknowns[name] = value

    def add_objective(self, name, indices=None, adder=0.0, scaler=1.0):
        """ Adds an objective to this driver.

        Args
        ----
        name : string
            Promoted pathname of the output that will serve as the objective.

        indices : iter of int, optional
            If an objective is an array, these indicate which entries are of
            interest for derivatives.

        adder : float or ndarray, optional
            Value to add to the model value to get the scaled value. Adder
            is first in precedence.

        scaler : float or ndarray, optional
            value to multiply the model value to get the scaled value. Scaler
            is second in precedence.
        """
        if len(self._objs) > 0 and not self.supports["multiple_objectives"]:
            raise RuntimeError("Attempted to add multiple objectives to a "
                               "driver that does not support multiple "
                               "objectives.")

        if name in self._objs:
            msg = "Objective '{}' already exists."
            raise RuntimeError(msg.format(name))

        if isinstance(adder, np.ndarray):
            adder = adder.flatten().astype('float')
        else:
            adder = float(adder)

        if isinstance(scaler, np.ndarray):
            scaler = scaler.flatten().astype('float')
        else:
            scaler = float(scaler)

        obj = OrderedDict()
        obj['adder'] = adder
        obj['scaler'] = scaler
        if indices:
            obj['indices'] = indices
            if len(indices) > 1 and not self.supports['multiple_objectives']:
                raise RuntimeError("Multiple objective indices specified for "
                                   "variable '%s', but driver '%s' doesn't "
                                   "support multiple objectives." %
                                   (name, self.pathname))
        self._objs[name] = obj

    def get_objectives(self, return_type='dict'):
        """ Gets all objectives of this driver.

        Args
        ----
        return_type : string
            Set to 'dict' to return a dictionary, or set to 'array' to return a
            flat ndarray.

        Returns
        -------
        dict (for return_type 'dict')
            Key is the objective name string, value is an ndarray with the values.

        ndarray (for return_type 'array')
            Array containing all objective values in the order they were added.
        """
        objs = OrderedDict()

        for key, meta in iteritems(self._objs):
            objs[key] = self._get_distrib_var(key, meta, 'objective')

        return objs

    def add_constraint(self, name, lower=None, upper=None, equals=None,
                       linear=False, jacs=None, indices=None, adder=0.0,
                       scaler=1.0):
        """ Adds a constraint to this driver. For inequality constraints,
        `lower` or `upper` must be specified. For equality constraints, `equals`
        must be specified.

        Args
        ----
        name : string
            Promoted pathname of the output that will serve as the quantity to
            constrain.

        lower : float or ndarray, optional
             Constrain the quantity to be greater than or equal to this value.

        upper : float or ndarray, optional
             Constrain the quantity to be less than or equal to this value.

        equals : float or ndarray, optional
             Constrain the quantity to be equal to this value.

        linear : bool, optional
            Set to True if this constraint is linear with respect to all design
            variables so that it can be calculated once and cached.

        jacs : dict of functions, optional
            Dictionary of user-defined functions that return the flattened
            Jacobian of this constraint with repsect to the design vars of
            this driver, as indicated by the dictionary keys. Default is None
            to let OpenMDAO calculate all derivatives. Note, this is currently
            unsupported

        indices : iter of int, optional
            If a constraint is an array, these indicate which entries are of
            interest for derivatives.

        adder : float or ndarray, optional
            Value to add to the model value to get the scaled value. Adder
            is first in precedence.

        scaler : float or ndarray, optional
            value to multiply the model value to get the scaled value. Scaler
            is second in precedence.
        """

        if name in self._cons:
            msg = "Constraint '{}' already exists."
            raise RuntimeError(msg.format(name))

        if equals is not None and (lower is not None or upper is not None):
            msg = "Constraint '{}' cannot be both equality and inequality."
            raise RuntimeError(msg.format(name))
        if equals is not None and self.supports['equality_constraints'] is False:
            msg = "Driver does not support equality constraint '{}'."
            raise RuntimeError(msg.format(name))
        if equals is None and self.supports['inequality_constraints'] is False:
            msg = "Driver does not support inequality constraint '{}'."
            raise RuntimeError(msg.format(name))
        if lower is not None and upper is not None and self.supports['two_sided_constraints'] is False:
            msg = "Driver does not support 2-sided constraint '{}'."
            raise RuntimeError(msg.format(name))
        if lower is None and upper is None and equals is None:
            msg = "Constraint '{}' needs to define lower, upper, or equals."
            raise RuntimeError(msg.format(name))

        if isinstance(scaler, np.ndarray):
            scaler = scaler.flatten().astype('float')
        else:
            scaler = float(scaler)

        if isinstance(adder, np.ndarray):
            adder = adder.flatten().astype('float')
        else:
            adder = float(adder)

        if isinstance(lower, np.ndarray):
            lower = lower.flatten()
        if isinstance(upper, np.ndarray):
            upper = upper.flatten()
        if isinstance(equals, np.ndarray):
            equals = equals.flatten()

        # Scale the lower and upper values
        if lower is not None:
            lower = (lower + adder)*scaler
        if upper is not None:
            upper = (upper + adder)*scaler
        if equals is not None:
            equals = (equals + adder)*scaler

        con = OrderedDict()
        con['lower'] = lower
        con['upper'] = upper
        con['equals'] = equals
        con['linear'] = linear
        con['adder'] = adder
        con['scaler'] = scaler
        con['jacs'] = jacs

        if indices:
            con['indices'] = indices
        self._cons[name] = con

    def get_constraints(self, ctype='all', lintype='all'):
        """ Gets all constraints for this driver.

        Args
        ----
        ctype : string
            Default is 'all'. Optionally return just the inequality constraints
            with 'ineq' or the equality constraints with 'eq'.

        lintype : string
            Default is 'all'. Optionally return just the linear constraints
            with 'linear' or the nonlinear constraints with 'nonlinear'.

        Returns
        -------
        dict
            Key is the constraint name string, value is an ndarray with the values.
        """
        cons = OrderedDict()

        for key, meta in iteritems(self._cons):

            if lintype == 'linear' and meta['linear'] is False:
                continue

            if lintype == 'nonlinear' and meta['linear']:
                continue

            if ctype == 'eq' and meta['equals'] is None:
                continue

            if ctype == 'ineq' and meta['equals'] is not None:
                continue

            cons[key] = self._get_distrib_var(key, meta, 'constraint')

        return cons

    def get_constraint_metadata(self):
        """ Returns a dict of constraint metadata.

        Returns
        -------
        dict
            Keys are the constraint object names, and the values are the param
            values.
        """
        return self._cons

    def run(self, problem):
        """ Runs the driver. This function should be overridden when inheriting.

        Args
        ----
        problem : `Problem`
            Our parent `Problem`.
        """
        self.run_once(problem)

    def run_once(self, problem):
        """ Runs root's solve_nonlinear one time

        Args
        ----
        problem : `Problem`
            Our parent `Problem`.
        """
        system = problem.root

        # Metadata Setup
        self.iter_count += 1
        metadata = self.metadata = create_local_meta(None, 'Driver')
        system.ln_solver.local_meta = metadata
        update_local_meta(metadata, (self.iter_count,))

        # Solve the system once and record results.
        with system._dircontext:
            system.solve_nonlinear(metadata=metadata)

        self.recorders.record_iteration(system, metadata)

    def calc_gradient(self, indep_list, unknown_list, mode='auto',
                      return_format='array', sparsity=None):
        """ Returns the scaled gradient for the system that is contained in
        self.root, scaled by all scalers that were specified when the desvars
        and constraints were added.

        Args
        ----
        indep_list : list of strings
            List of independent variable names that derivatives are to
            be calculated with respect to. All params must have a IndepVarComp.

        unknown_list : list of strings
            List of output or state names that derivatives are to
            be calculated for. All must be valid unknowns in OpenMDAO.

        mode : string, optional
            Deriviative direction, can be 'fwd', 'rev', 'fd', or 'auto'.
            Default is 'auto', which uses mode specified on the linear solver
            in root.

        return_format : string, optional
            Format for the derivatives, can be 'array' or 'dict'.

        sparsity : dict, optional
            Dictionary that gives the relevant design variables for each
            constraint. This option is only supported in the `dict` return
            format.

        Returns
        -------
        ndarray or dict
            Jacobian of unknowns with respect to params.
        """

        J = self._problem.calc_gradient(indep_list, unknown_list, mode=mode,
                                        return_format=return_format,
                                        dv_scale=self.dv_conversions,
                                        cn_scale=self.fn_conversions,
                                        sparsity=sparsity)

        self.recorders.record_derivatives(J, self.metadata)
        return J

    def generate_docstring(self):
        """
        Generates a numpy-style docstring for a user-created Driver class.

        Returns
        -------
        docstring : str
                string that contains a basic numpy docstring.
        """
        #start the docstring off
        docstring = '    \"\"\"\n'

        #Put options into docstring
        firstTime = 1

        for key, value in sorted(vars(self).items()):
            if type(value)==OptionsDictionary:
                if key == "supports":
                    continue
                if firstTime:  #start of Options docstring
                    docstring += '\n    Options\n    -------\n'
                    firstTime = 0
                docstring += value._generate_docstring(key)

        #finish up docstring
        docstring += '\n    \"\"\"\n'
        return docstring
Example #29
0
class SolverBase(object):
    """ Common base class for Linear and Nonlinear solver. Should not be used
    by users. Always inherit from `LinearSolver` or `NonlinearSolver`."""
    def __init__(self):
        self.iter_count = 0
        self.options = OptionsDictionary()
        desc =  "Set to 0 to print only failures, set to 1 to print iteration totals to" + \
                "stdout, set to 2 to print the residual each iteration to stdout," + \
                "or -1 to suppress all printing."

        self.options.add_option('iprint', 0, values=[-1, 0, 1, 2], desc=desc)
        self.options.add_option(
            'err_on_maxiter',
            False,
            desc='If True, raise an AnalysisError if not converged at maxiter.'
        )
        self.recorders = RecordingManager()
        self.local_meta = None

    def setup(self, sub):
        """ Solvers override to define post-setup initiailzation.

        Args
        ----
        sub: `System`
            System that owns this solver.
        """
        pass

    def cleanup(self):
        """ Clean up resources prior to exit. """
        self.recorders.close()

    def print_norm(self,
                   solver_string,
                   system,
                   iteration,
                   res,
                   res0,
                   msg=None,
                   indent=0,
                   solver='NL',
                   u_norm=None):
        """ Prints out the norm of the residual in a neat readable format.

        Args
        ----
        solver_string: string
            Unique string to identify your solver type (e.g., 'LN_GS' or
            'NEWTON').

        system: system
            Parent system, which contains pathname and the preconditioning flag.

        iteration: int
            Current iteration number

        res: float
            Norm of the absolute residual value.

        res0: float
            Norm of the baseline initial residual for relative comparison.

        msg: string, optional
            Message that indicates convergence.

        ident: int, optional
            Additional indentation levels for subiterations.

        solver: string, optional
            Solver type if not LN or NL (mostly for line search operations.)

        u_norm: float, optional
            Norm of the u vector, if applicable.
        """

        pathname = system.pathname
        if pathname == '':
            name = 'root'
        else:
            name = 'root.' + pathname

        # Find indentation level
        level = name.count('.')
        # No indentation for driver; top solver is no indentation.
        level = level + indent

        indent = '   ' * level

        if system._probdata.precon_level > 0:
            solver_string = 'PRECON:' + solver_string
            indent += '  ' * system._probdata.precon_level

        if msg is not None:
            form = indent + '[%s] %s: %s   %d | %s'

            if u_norm:
                form += ' (%s)' % u_norm

            print(form % (name, solver, solver_string, iteration, msg))
            return

        form = indent + '[%s] %s: %s   %d | %.9g %.9g'

        if u_norm:
            form += ' (%s)' % u_norm

        print(form % (name, solver, solver_string, iteration, res, res / res0))

    def print_all_convergence(self, level=2):
        """ Turns on iprint for this solver and all subsolvers. Override if
        your solver has subsolvers.

        Args
        ----
        level : int(2)
            iprint level. Set to 2 to print residuals each iteration; set to 1
            to print just the iteration totals.
        """
        self.options['iprint'] = level

    def generate_docstring(self):
        """
        Generates a numpy-style docstring for a user-created System class.

        Returns
        -------
        docstring : str
                string that contains a basic numpy docstring.

        """
        #start the docstring off
        docstrings = ['    \"\"\"']

        #Put options into docstring
        firstTime = 1

        for key, value in sorted(vars(self).items()):
            if type(value) == OptionsDictionary:
                if firstTime:  #start of Options docstring
                    docstrings.extend(['', '    Options', '    -------'])
                    firstTime = 0
                docstrings.append(value._generate_docstring(key))

        #finish up docstring
        docstrings.extend(['    \"\"\"', ''])
        return '\n'.join(docstrings)
Example #30
0
class ExternalCode(Component):
    """
    Run an external code as a component

    Default stdin is the 'null' device, default stdout is the console, and
    default stderr is ``error.out``.

    Options
    -------
    fd_options['force_fd'] :  bool(False)
        Set to True to finite difference this system.
    fd_options['form'] :  str('forward')
        Finite difference mode. (forward, backward, central) You can also set to 'complex_step' to peform the complex step method if your components support it.
    fd_options['step_size'] :  float(1e-06)
        Default finite difference stepsize
    fd_options['step_type'] :  str('absolute')
        Set to absolute, relative
    options['check_external_outputs'] :  bool(True)
        Check that all input or output external files exist
    options['command'] :  list([])
        command to be executed
    options['env_vars'] :  dict({})
        Environment variables required by the command
    options['external_input_files'] :  list([])
        (optional) list of input file names to check the pressence of before solve_nonlinear
    options['external_output_files'] :  list([])
        (optional) list of input file names to check the pressence of after solve_nonlinear
    options['poll_delay'] :  float(0.0)
        Delay between polling for command completion. A value of zero will use an internally computed default
    options['timeout'] :  float(0.0)
        Maximum time to wait for command completion. A value of zero implies an infinite wait

    """

    def __init__(self):
        super(ExternalCode, self).__init__()

        self.STDOUT   = STDOUT
        self.DEV_NULL = DEV_NULL

        # Input options for this Component
        self.options = OptionsDictionary()
        self.options.add_option('command', [], desc='command to be executed')
        self.options.add_option('env_vars', {}, desc='Environment variables required by the command')
        self.options.add_option('poll_delay', 0.0, lower=0.0,
            desc='Delay between polling for command completion. A value of zero will use an internally computed default')
        self.options.add_option('timeout', 0.0, lower=0.0,
                                desc='Maximum time to wait for command completion. A value of zero implies an infinite wait')
        self.options.add_option('check_external_outputs', True,
            desc='Check that all input or output external files exist')

        self.options.add_option( 'external_input_files', [],
            desc='(optional) list of input file names to check the pressence of before solve_nonlinear')
        self.options.add_option( 'external_output_files', [],
            desc='(optional) list of input file names to check the pressence of after solve_nonlinear')

        # Outputs of the run of the component or items that will not work with the OptionsDictionary
        self.return_code = 0 # Return code from the command
        self.timed_out = False # True if the command timed-out
        self.stdin  = self.DEV_NULL
        self.stdout = None
        self.stderr = "error.out"

    def check_setup(self, out_stream=sys.stdout):
        """Write a report to the given stream indicating any potential problems found
        with the current configuration of this ``Problem``.

        Args
        ----
        out_stream : a file-like object, optional
        """

        # check for the command
        if not self.options['command']:
            out_stream.write( "The command cannot be empty")
        else:
            if isinstance(self.options['command'], str):
                program_to_execute = self.options['command']
            else:
                program_to_execute = self.options['command'][0]
            command_full_path = find_executable( program_to_execute )

            if not command_full_path:
                msg = "The command to be executed, '%s', cannot be found" % program_to_execute
                out_stream.write(msg)

        # Check for missing input files
        missing_files = self._check_for_files(input=True)
        for iotype, path in missing_files:
            msg = "The %s file %s is missing" % ( iotype, path )
            out_stream.write(msg)

    def solve_nonlinear(self, params, unknowns, resids):
        """Runs the component
        """

        self.return_code = -12345678
        self.timed_out = False

        if not self.options['command']:
            raise ValueError('Empty command list')

        # self.check_files(inputs=True)

        return_code = None
        error_msg = ''
        try:
            return_code, error_msg = self._execute_local()

            if return_code is None:
                # if self._stop:
                #     raise RuntimeError('Run stopped')
                # else:
                self.timed_out = True
                raise RuntimeError('Timed out')

            elif return_code:
                if isinstance(self.stderr, str):
                    if os.path.exists(self.stderr):
                        stderrfile = open(self.stderr, 'r')
                        error_desc = stderrfile.read()
                        stderrfile.close()
                        err_fragment = "\nError Output:\n%s" % error_desc
                    else:
                        err_fragment = "\n[stderr %r missing]" % self.stderr
                else:
                    err_fragment = error_msg

                raise RuntimeError('return_code = %d%s' % (return_code, err_fragment))

            if self.options['check_external_outputs']:
                missing_files = self._check_for_files(input=False)
                msg = ""
                for iotype, path in missing_files:
                    msg +=  "%s file %s is missing\n" % (iotype, path)

                if msg:
                    raise RuntimeError( "Missing files: %s" % msg )
                # self.check_files(inputs=False)
        finally:
            self.return_code = -999999 if return_code is None else return_code

    def _check_for_files(self, input=True):
        """
        Check that all 'specific' input external files exist.

        input: bool
            If True, check inputs. Else check outputs
        """

        missing_files = []

        if input:
            files = self.options['external_input_files']
        else:
            files = self.options['external_output_files']

        for path in files:
            if not os.path.exists(path):
                missing_files.append(('input', path))

        return missing_files

    def _execute_local(self):
        """ Run command. """
        #self._logger.info('executing %s...', self.options['command'])
        # start_time = time.time()

        # check to make sure command exists
        if isinstance(self.options['command'], str):
            program_to_execute = self.options['command']
        else:
            program_to_execute = self.options['command'][0]

        # suppress message from find_executable function, we'll handle it
        numpy.distutils.log.set_verbosity(-1)

        command_full_path = find_executable( program_to_execute )
        if not command_full_path:
            raise ValueError("The command to be executed, '%s', cannot be found" % program_to_execute)

        command_for_shell_proc = self.options['command']
        if sys.platform == 'win32':
            command_for_shell_proc = ['cmd.exe', '/c' ] + command_for_shell_proc

        self._process = \
            ShellProc(command_for_shell_proc, self.stdin,
                      self.stdout, self.stderr, self.options['env_vars'])
        #self._logger.debug('PID = %d', self._process.pid)

        try:
            return_code, error_msg = \
                self._process.wait(self.options['poll_delay'], self.options['timeout'])
        finally:
            self._process.close_files()
            self._process = None

        # et = time.time() - start_time
        #if et >= 60:  #pragma no cover
            #self._logger.info('elapsed time: %.1f sec.', et)

        return (return_code, error_msg)
Example #31
0
class MySimpleDriver(Driver):

    def __init__(self):
        super(MySimpleDriver, self).__init__()

        # What we support
        self.supports['inequality_constraints'] = True
        self.supports['equality_constraints'] = False
        self.supports['linear_constraints'] = False
        self.supports['multiple_objectives'] = False

        # My driver options
        self.options = OptionsDictionary()
        self.options.add_option('tol', 1e-4)
        self.options.add_option('maxiter', 10)

        self.alpha = .01
        self.violated = []

    def run(self, problem):
        """ Mimic a very simplistic unconstrained optimization."""

        # Get dicts with pointers to our vectors
        params = self.get_desvars()
        objective = self.get_objectives()
        constraints = self.get_constraints()

        indep_list = params.keys()
        objective_names = list(objective.keys())
        constraint_names = list(constraints.keys())
        unknown_list = objective_names + constraint_names

        itercount = 0
        while itercount < self.options['maxiter']:

            # Run the model
            problem.root.solve_nonlinear()
            #print('z1: %f, z2: %f, x1: %f, y1: %f, y2: %f' % (problem['z'][0],
                                                              #problem['z'][1],
                                                              #problem['x'],
                                                              #problem['y1'],
                                                              #problem['y2']))
            #print('obj: %f, con1: %f, con2: %f' % (problem['obj'], problem['con1'],
                                                   #problem['con2']))

            # Calculate gradient
            J = problem.calc_gradient(indep_list, unknown_list, return_format='dict')

            objective = self.get_objectives()
            constraints = self.get_constraints()

            for key1 in objective_names:
                for key2 in indep_list:

                    grad = J[key1][key2] * objective[key1]
                    new_val = params[key2] - self.alpha*grad

                    # Set parameter
                    self.set_desvar(key2, new_val)

            self.violated = []
            for name, val in constraints.items():
                if np.linalg.norm(val) > 0.0:
                    self.violated.append(name)

            itercount += 1
Example #32
0
class ExternalCode(Component):
    """
    Run an external code as a component

    Default stdin is the 'null' device, default stdout is the console, and
    default stderr is ``error.out``.

    Options
    -------
    deriv_options['type'] :  str('user')
        Derivative calculation type ('user', 'fd', 'cs')
        Default is 'user', where derivative is calculated from
        user-supplied derivatives. Set to 'fd' to finite difference
        this system. Set to 'cs' to perform the complex step
        if your components support it.
    deriv_options['form'] :  str('forward')
        Finite difference mode. (forward, backward, central)
    deriv_options['step_size'] :  float(1e-06)
        Default finite difference stepsize
    deriv_options['step_calc'] :  str('absolute')
        Set to absolute, relative
    deriv_options['check_type'] :  str('fd')
        Type of derivative check for check_partial_derivatives. Set
        to 'fd' to finite difference this system. Set to
        'cs' to perform the complex step method if
        your components support it.
    deriv_options['check_form'] :  str('forward')
        Finite difference mode: ("forward", "backward", "central")
        During check_partial_derivatives, the difference form that is used
        for the check.
    deriv_options['check_step_calc'] : str('absolute',)
        Set to 'absolute' or 'relative'. Default finite difference
        step calculation for the finite difference check in check_partial_derivatives.
    deriv_options['check_step_size'] :  float(1e-06)
        Default finite difference stepsize for the finite difference check
        in check_partial_derivatives"
    deriv_options['linearize'] : bool(False)
        Set to True if you want linearize to be called even though you are using FD.

    options['command'] :  list([])
        Command to be executed. Command must be a list of command line args.
    options['env_vars'] :  dict({})
        Environment variables required by the command
    options['external_input_files'] :  list([])
        (optional) list of input file names to check the existence of before solve_nonlinear
    options['external_output_files'] :  list([])
        (optional) list of input file names to check the existence of after solve_nonlinear
    options['poll_delay'] :  float(0.0)
        Delay between polling for command completion. A value of zero will use
        an internally computed default.
    options['timeout'] :  float(0.0)
        Maximum time in seconds to wait for command completion. A value of zero
        implies an infinite wait. If the timeout interval is exceeded, an
        AnalysisError will be raised.
    options['fail_hard'] :  bool(True)
        Behavior on error returned from code, either raise a 'hard' error (RuntimeError) if True
        or a 'soft' error (AnalysisError) if False.


    """

    def __init__(self):
        super(ExternalCode, self).__init__()

        self.STDOUT   = STDOUT
        self.DEV_NULL = DEV_NULL

        # Input options for this Component
        self.options = OptionsDictionary()
        self.options.add_option('command', [], desc='command to be executed')
        self.options.add_option('env_vars', {},
                           desc='Environment variables required by the command')
        self.options.add_option('poll_delay', 0.0, lower=0.0,
            desc='Delay between polling for command completion. A value of zero will use an internally computed default')
        self.options.add_option('timeout', 0.0, lower=0.0,
                                desc='Maximum time to wait for command completion. A value of zero implies an infinite wait')
        self.options.add_option( 'external_input_files', [],
            desc='(optional) list of input file names to check the existence of before solve_nonlinear')
        self.options.add_option( 'external_output_files', [],
            desc='(optional) list of input file names to check the existence of after solve_nonlinear')
        self.options.add_option('fail_hard', True,
            desc="If True, external code errors raise a 'hard' exception (RuntimeError).  Otherwise raise a 'soft' exception (AnalysisError).")

        # Outputs of the run of the component or items that will not work with the OptionsDictionary
        self.return_code = 0 # Return code from the command
        self.stdin  = self.DEV_NULL
        self.stdout = None
        self.stderr = "error.out"

    def check_setup(self, out_stream=sys.stdout):
        """Write a report to the given stream indicating any potential problems found
        with the current configuration of this ``Problem``.

        Args
        ----
        out_stream : a file-like object, optional
        """

        # check for the command
        cmd = [c for c in self.options['command'] if c.strip()]
        if not cmd:
            out_stream.write( "The command cannot be empty")
        else:
            program_to_execute = self.options['command'][0]
            command_full_path = find_executable( program_to_execute )

            if not command_full_path:
                out_stream.write("The command to be executed, '%s', "
                                 "cannot be found" % program_to_execute)

        # Check for missing input files
        missing = self._check_for_files(self.options['external_input_files'])
        if missing:
            out_stream.write("The following input files are missing at setup "
                             " time: %s" % missing)

    def solve_nonlinear(self, params, unknowns, resids):
        """Runs the component
        """

        self.return_code = -12345678

        if not self.options['command']:
            raise ValueError('Empty command list')

        if self.options['fail_hard']:
            err_class = RuntimeError
        else:
            err_class = AnalysisError

        return_code = None

        try:
            missing = self._check_for_files(self.options['external_input_files'])
            if missing:
                raise err_class("The following input files are missing: %s"
                                % sorted(missing))
            return_code, error_msg = self._execute_local()

            if return_code is None:
                raise AnalysisError('Timed out after %s sec.' %
                                     self.options['timeout'])

            elif return_code:
                if isinstance(self.stderr, str):
                    if os.path.exists(self.stderr):
                        stderrfile = open(self.stderr, 'r')
                        error_desc = stderrfile.read()
                        stderrfile.close()
                        err_fragment = "\nError Output:\n%s" % error_desc
                    else:
                        err_fragment = "\n[stderr %r missing]" % self.stderr
                else:
                    err_fragment = error_msg

                raise err_class('return_code = %d%s' % (return_code,
                                                        err_fragment))

            missing = self._check_for_files(self.options['external_output_files'])
            if missing:
                raise err_class("The following output files are missing: %s"
                                % sorted(missing))

        finally:
            self.return_code = -999999 if return_code is None else return_code

    def _check_for_files(self, files):
        """ Check that specified files exist. """
        return [path for path in files if not os.path.exists(path)]

    def _execute_local(self):
        """ Run command. """

        # check to make sure command exists
        if isinstance(self.options['command'], str):
            program_to_execute = self.options['command']
        else:
            program_to_execute = self.options['command'][0]

        # suppress message from find_executable function, we'll handle it
        numpy.distutils.log.set_verbosity(-1)

        command_full_path = find_executable( program_to_execute )
        if not command_full_path:
            raise ValueError("The command to be executed, '%s', cannot be found" % program_to_execute)

        command_for_shell_proc = self.options['command']
        if sys.platform == 'win32':
            command_for_shell_proc = ['cmd.exe', '/c' ] + command_for_shell_proc

        self._process = \
            ShellProc(command_for_shell_proc, self.stdin,
                      self.stdout, self.stderr, self.options['env_vars'])

        try:
            return_code, error_msg = \
                self._process.wait(self.options['poll_delay'], self.options['timeout'])
        finally:
            self._process.close_files()
            self._process = None

        return (return_code, error_msg)
Example #33
0
class SolverBase(object):
    """ Common base class for Linear and Nonlinear solver. Should not be used
    by users. Always inherit from `LinearSolver` or `NonlinearSolver`."""

    def __init__(self):
        self.iter_count = 0
        self.options = OptionsDictionary()
        desc = 'Set to 0 to disable printing, set to 1 to print the ' \
               'residual to stdout each iteration, set to 2 to print ' \
               'subiteration residuals as well.'
        self.options.add_option('iprint', 0, values=[0, 1, 2], desc=desc)
        self.options.add_option('err_on_maxiter', False,
            desc='If True, raise an AnalysisError if not converged at maxiter.')
        self.recorders = RecordingManager()
        self.local_meta = None

    def setup(self, sub):
        """ Solvers override to define post-setup initiailzation.

        Args
        ----
        sub: `System`
            System that owns this solver.
        """
        pass

    def cleanup(self):
        """ Clean up resources prior to exit. """
        self.recorders.close()

    def print_norm(self, solver_string, pathname, iteration, res, res0,
                   msg=None, indent=0, solver='NL', u_norm=None):
        """ Prints out the norm of the residual in a neat readable format.

        Args
        ----
        solver_string: string
            Unique string to identify your solver type (e.g., 'LN_GS' or
            'NEWTON').

        pathname: dict
            Parent system pathname.

        iteration: int
            Current iteration number

        res: float
            Norm of the absolute residual value.

        res0: float
            Norm of the baseline initial residual for relative comparison.

        msg: string, optional
            Message that indicates convergence.

        ident: int, optional
            Additional indentation levels for subiterations.

        solver: string, optional
            Solver type if not LN or NL (mostly for line search operations.)

        u_norm: float, optional
            Norm of the u vector, if applicable.
        """
        if pathname=='':
            name = 'root'
        else:
            name = 'root.' + pathname

        # Find indentation level
        level = pathname.count('.')
        # No indentation for driver; top solver is no indentation.
        level = level + indent

        indent = '   ' * level
        if msg is not None:
            form = indent + '[%s] %s: %s   %d | %s'

            if u_norm:
                form += ' (%s)' % u_norm

            print(form % (name, solver, solver_string, iteration, msg))
            return

        form = indent + '[%s] %s: %s   %d | %.9g %.9g'

        if u_norm:
            form += ' (%s)' % u_norm

        print(form % (name, solver, solver_string, iteration, res, res/res0))

    def print_all_convergence(self):
        """ Turns on iprint for this solver and all subsolvers. Override if
        your solver has subsolvers."""
        self.options['iprint'] = 1

    def generate_docstring(self):
        """
        Generates a numpy-style docstring for a user-created System class.

        Returns
        -------
        docstring : str
                string that contains a basic numpy docstring.

        """
        #start the docstring off
        docstrings = ['    \"\"\"']

        #Put options into docstring
        firstTime = 1

        for key, value in sorted(vars(self).items()):
            if type(value)==OptionsDictionary:
                if firstTime:  #start of Options docstring
                    docstrings.extend(['','    Options','    -------'])
                    firstTime = 0
                docstrings.append(value._generate_docstring(key))

        #finish up docstring
        docstrings.extend(['    \"\"\"',''])
        return '\n'.join(docstrings)
Example #34
0
class ExternalCode(Component):
    """
    Run an external code as a component

    Default stdin is the 'null' device, default stdout is the console, and
    default stderr is ``error.out``.

    Options
    -------
    fd_options['force_fd'] :  bool(False)
        Set to True to finite difference this system.
    fd_options['form'] :  str('forward')
        Finite difference mode. (forward, backward, central) You can also set
        to 'complex_step' to peform the complex step method if your components
        support it.
    fd_options['step_size'] :  float(1e-06)
        Default finite difference stepsize
    fd_options['step_type'] :  str('absolute')
        Set to absolute, relative
    fd_options['extra_check_partials_form'] :  None or str
        Finite difference mode: ("forward", "backward", "central", "complex_step")
        During check_partial_derivatives, you can optionally do a
        second finite difference with a different mode.
    fd_options['linearize'] : bool(False)
        Set to True if you want linearize to be called even though you are using FD.

    options['command'] :  list([])
        Command to be executed. Command must be a list of command line args.
    options['env_vars'] :  dict({})
        Environment variables required by the command
    options['external_input_files'] :  list([])
        (optional) list of input file names to check the existence of before solve_nonlinear
    options['external_output_files'] :  list([])
        (optional) list of input file names to check the existence of after solve_nonlinear
    options['poll_delay'] :  float(0.0)
        Delay between polling for command completion. A value of zero will use
        an internally computed default.
    options['timeout'] :  float(0.0)
        Maximum time in seconds to wait for command completion. A value of zero
        implies an infinite wait. If the timeout interval is exceeded, an
        AnalysisError will be raised.
    options['fail_hard'] :  bool(True)
        Behavior on error returned from code, either raise a 'hard' error (RuntimeError) if True
        or a 'soft' error (AnalysisError) if False.


    """
    def __init__(self):
        super(ExternalCode, self).__init__()

        self.STDOUT = STDOUT
        self.DEV_NULL = DEV_NULL

        # Input options for this Component
        self.options = OptionsDictionary()
        self.options.add_option('command', [], desc='command to be executed')
        self.options.add_option(
            'env_vars', {},
            desc='Environment variables required by the command')
        self.options.add_option(
            'poll_delay',
            0.0,
            lower=0.0,
            desc=
            'Delay between polling for command completion. A value of zero will use an internally computed default'
        )
        self.options.add_option(
            'timeout',
            0.0,
            lower=0.0,
            desc=
            'Maximum time to wait for command completion. A value of zero implies an infinite wait'
        )
        self.options.add_option(
            'external_input_files', [],
            desc=
            '(optional) list of input file names to check the existence of before solve_nonlinear'
        )
        self.options.add_option(
            'external_output_files', [],
            desc=
            '(optional) list of input file names to check the existence of after solve_nonlinear'
        )
        self.options.add_option(
            'fail_hard',
            True,
            desc=
            "If True, external code errors raise a 'hard' exception (RuntimeError).  Otherwise raise a 'soft' exception (AnalysisError)."
        )

        # Outputs of the run of the component or items that will not work with the OptionsDictionary
        self.return_code = 0  # Return code from the command
        self.stdin = self.DEV_NULL
        self.stdout = None
        self.stderr = "error.out"

    def check_setup(self, out_stream=sys.stdout):
        """Write a report to the given stream indicating any potential problems found
        with the current configuration of this ``Problem``.

        Args
        ----
        out_stream : a file-like object, optional
        """

        # check for the command
        cmd = [c for c in self.options['command'] if c.strip()]
        if not cmd:
            out_stream.write("The command cannot be empty")
        else:
            program_to_execute = self.options['command'][0]
            command_full_path = find_executable(program_to_execute)

            if not command_full_path:
                out_stream.write("The command to be executed, '%s', "
                                 "cannot be found" % program_to_execute)

        # Check for missing input files
        missing = self._check_for_files(self.options['external_input_files'])
        if missing:
            out_stream.write("The following input files are missing at setup "
                             " time: %s" % missing)

    def solve_nonlinear(self, params, unknowns, resids):
        """Runs the component
        """

        self.return_code = -12345678

        if not self.options['command']:
            raise ValueError('Empty command list')

        if self.options['fail_hard']:
            err_class = RuntimeError
        else:
            err_class = AnalysisError

        return_code = None

        try:
            missing = self._check_for_files(
                self.options['external_input_files'])
            if missing:
                raise err_class("The following input files are missing: %s" %
                                sorted(missing))
            return_code, error_msg = self._execute_local()

            if return_code is None:
                raise AnalysisError('Timed out after %s sec.' %
                                    self.options['timeout'])

            elif return_code:
                if isinstance(self.stderr, str):
                    if os.path.exists(self.stderr):
                        stderrfile = open(self.stderr, 'r')
                        error_desc = stderrfile.read()
                        stderrfile.close()
                        err_fragment = "\nError Output:\n%s" % error_desc
                    else:
                        err_fragment = "\n[stderr %r missing]" % self.stderr
                else:
                    err_fragment = error_msg

                raise err_class('return_code = %d%s' %
                                (return_code, err_fragment))

            missing = self._check_for_files(
                self.options['external_output_files'])
            if missing:
                raise err_class("The following output files are missing: %s" %
                                sorted(missing))

        finally:
            self.return_code = -999999 if return_code is None else return_code

    def _check_for_files(self, files):
        """ Check that specified files exist. """
        return [path for path in files if not os.path.exists(path)]

    def _execute_local(self):
        """ Run command. """

        # check to make sure command exists
        if isinstance(self.options['command'], str):
            program_to_execute = self.options['command']
        else:
            program_to_execute = self.options['command'][0]

        # suppress message from find_executable function, we'll handle it
        numpy.distutils.log.set_verbosity(-1)

        command_full_path = find_executable(program_to_execute)
        if not command_full_path:
            raise ValueError(
                "The command to be executed, '%s', cannot be found" %
                program_to_execute)

        command_for_shell_proc = self.options['command']
        if sys.platform == 'win32':
            command_for_shell_proc = ['cmd.exe', '/c'] + command_for_shell_proc

        self._process = \
            ShellProc(command_for_shell_proc, self.stdin,
                      self.stdout, self.stderr, self.options['env_vars'])

        try:
            return_code, error_msg = \
                self._process.wait(self.options['poll_delay'], self.options['timeout'])
        finally:
            self._process.close_files()
            self._process = None

        return (return_code, error_msg)
class MySimpleDriver(Driver):

    def __init__(self):
        super(MySimpleDriver, self).__init__()

        # What we support
        self.supports['inequality_constraints'] = True
        self.supports['equality_constraints'] = False
        self.supports['linear_constraints'] = False
        self.supports['multiple_objectives'] = False

        # My driver options
        self.options = OptionsDictionary()
        self.options.add_option('tol', 1e-4)
        self.options.add_option('maxiter', 10)

        self.alpha = .01
        self.violated = []

    def run(self, problem):
        """ Mimic a very simplistic unconstrained optimization."""

        # Get dicts with pointers to our vectors
        params = self.get_desvars()
        objective = self.get_objectives()
        constraints = self.get_constraints()

        indep_list = params.keys()
        objective_names = list(objective.keys())
        constraint_names = list(constraints.keys())
        unknown_list = objective_names + constraint_names

        itercount = 0
        while itercount < self.options['maxiter']:

            # Run the model
            problem.root.solve_nonlinear()
            #print('z1: %f, z2: %f, x1: %f, y1: %f, y2: %f' % (problem['z'][0],
                                                              #problem['z'][1],
                                                              #problem['x'],
                                                              #problem['y1'],
                                                              #problem['y2']))
            #print('obj: %f, con1: %f, con2: %f' % (problem['obj'], problem['con1'],
                                                   #problem['con2']))

            # Calculate gradient
            J = problem.calc_gradient(indep_list, unknown_list, return_format='dict')

            objective = self.get_objectives()
            constraints = self.get_constraints()

            for key1 in objective_names:
                for key2 in indep_list:

                    grad = J[key1][key2] * objective[key1]
                    new_val = params[key2] - self.alpha*grad

                    # Set parameter
                    self.set_desvar(key2, new_val)

            self.violated = []
            for name, val in constraints.items():
                if np.linalg.norm(val) > 0.0:
                    self.violated.append(name)

            itercount += 1
Example #36
0
class BaseRecorder(object):
    """ Base class for all case recorders. """

    def __init__(self):
        self.options = OptionsDictionary()
        self.options.add_option('record_metadata', True)
        self.options.add_option('record_unknowns', True)
        self.options.add_option('record_params', False)
        self.options.add_option('record_resids', False)
        self.options.add_option('includes', ['*'],
                                desc='Patterns for variables to include in recording')
        self.options.add_option('excludes', [],
                                desc='Patterns for variables to exclude from recording '
                                '(processed after includes)')
        self.out = None

        # This is for drivers to determine if a recorder supports
        # real parallel recording (recording on each process), because
        # if it doesn't, the driver figures out what variables must
        # be gathered to rank 0 if running under MPI.
        #
        # By default, this is False, but it should be set to True
        # if the recorder will record data on each process to avoid
        # unnecessary gathering.
        self._parallel = False

        self._filtered = {}
        # TODO: System specific includes/excludes

    def startup(self, group):
        """ Prepare for a new run.

        Args
        ----
        group : `Group`
            Group that owns this recorder.
        """

        # Compute the inclusion lists for recording
        params = list(filter(self._check_path, group.params))
        unknowns = list(filter(self._check_path, group.unknowns))
        resids = list(filter(self._check_path, group.resids))

        self._filtered[group.pathname] = (params, unknowns, resids)

    def _check_path(self, path):
        """ Return True if `path` should be recorded. """

        includes = self.options['includes']
        excludes = self.options['excludes']

        # First see if it's included
        for pattern in includes:
            if fnmatch(path, pattern):
                # We found a match. Check to see if it is excluded.
                for ex_pattern in excludes:
                    if fnmatch(path, ex_pattern):
                        return False
                return True

        # Did not match anything in includes.
        return False

    def _get_pathname(self, iteration_coordinate):
        '''
        Converts an iteration coordinate to key to index
        `_filtered` to retrieve names of variables to be recorder
        '''
        return '.'.join(iteration_coordinate[4::2])

    def _filter_vectors(self, params, unknowns, resids, iteration_coordinate):
        '''
        Returns subset of `params`, `unknowns` and `resids` to be recoder
        '''
        pathname = self._get_pathname(iteration_coordinate)
        pnames, unames, rnames = self._filtered[pathname]

        params = {key: params[key] for key in pnames}
        unknowns = {key: unknowns[key] for key in unames}
        resids = {key: resids[key] for key in rnames}

        return params, unknowns, resids

    def record_iteration(self, params, unknowns, resids, metadata):
        """
        Writes the provided data.

        Args
        ----
        params : dict
            Dictionary containing parameters. (p)

        unknowns : dict
            Dictionary containing outputs and states. (u)

        resids : dict
            Dictionary containing residuals. (r)

        metadata : dict, optional
            Dictionary containing execution metadata (e.g. iteration coordinate).
        """
        raise NotImplementedError()

    def record_metadata(self, group):
        """Writes the metadata of the given group

        Args
        ----
        group : `System`
            `System` containing vectors
        """
        raise NotImplementedError()

    def close(self):
        """Closes `out` unless it's ``sys.stdout``, ``sys.stderr``, or StringIO.
        Note that a closed recorder will do nothing in :meth:`record`."""
        # Closing a StringIO deletes its contents.
        if self.out not in (None, sys.stdout, sys.stderr):
            if not isinstance(self.out, StringIO):
                self.out.close()
            self.out = None
Example #37
0
class SolverBase(object):
    """ Common base class for Linear and Nonlinear solver. Should not be used
    by users. Always inherit from `LinearSolver` or `NonlinearSolver`."""

    def __init__(self):
        self.iter_count = 0
        self.options = OptionsDictionary()
        desc = 'Set to 0 to disable printing, set to 1 to print the ' \
               'residual to stdout each iteration, set to 2 to print ' \
               'subiteration residuals as well.'
        self.options.add_option('iprint', 0, values=[0, 1, 2], desc=desc)
        self.recorders = RecordingManager()
        self.local_meta = None

    def setup(self, sub):
        """ Solvers override to define post-setup initiailzation.

        Args
        ----
        sub: `System`
            System that owns this solver.
        """
        pass

    def print_norm(self, solver_string, pathname, iteration, res, res0,
                   msg=None, indent=0, solver='NL'):
        """ Prints out the norm of the residual in a neat readable format.

        Args
        ----
        solver_string: string
            Unique string to identify your solver type (e.g., 'LN_GS' or
            'NEWTON').

        pathname: dict
            Parent system pathname.

        iteration: int
            Current iteration number

        res: float
            Absolute residual value.

        res0: float
            Baseline initial residual for relative comparison.

        msg: string, optional
            Message that indicates convergence.

        ident: int, optional
            Additional indentation levels for subiterations.

        solver: string, optional
            Solver type if not LN or NL (mostly for line search operations.)
        """
        if pathname=='':
            name = 'root'
        else:
            name = 'root.' + pathname

        # Find indentation level
        level = pathname.count('.')
        # No indentation for driver; top solver is no indentation.
        level = level + indent

        indent = '   ' * level
        if msg is not None:
            form = indent + '[%s] %s: %s   %d | %s'
            print(form % (name, solver, solver_string, iteration, msg))
            return

        form = indent + '[%s] %s: %s   %d | %.9g %.9g'
        print(form % (name, solver, solver_string, iteration, res, res/res0))

    def print_all_convergence(self):
        """ Turns on iprint for this solver and all subsolvers. Override if
        your solver has subsolvers."""
        self.options['iprint'] = 1

    def generate_docstring(self):
        """
        Generates a numpy-style docstring for a user-created System class.

        Returns
        -------
        docstring : str
                string that contains a basic numpy docstring.

        """
        #start the docstring off
        docstring = '    \"\"\"\n'

        #Put options into docstring
        firstTime = 1
        #for py3.4, items from vars must come out in same order.
        from collections import OrderedDict
        v = OrderedDict(sorted(vars(self).items()))
        for key, value in v.items():
            if type(value)==OptionsDictionary:
                if firstTime:  #start of Options docstring
                    docstring += '\n    Options\n    -------\n'
                    firstTime = 0
                for (name, val) in sorted(value.items()):
                    docstring += "    " + key + "['"
                    docstring += name + "']"
                    docstring += " :  " + type(val).__name__
                    docstring += "("
                    if type(val).__name__ == 'str': docstring += "'"
                    docstring += str(val)
                    if type(val).__name__ == 'str': docstring += "'"
                    docstring += ")\n"

                    desc = value._options[name]['desc']
                    if(desc):
                        docstring += "        " + desc + "\n"

        #finish up docstring
        docstring += '\n    \"\"\"\n'
        return docstring