Exemplo n.º 1
0
    def __init__(self,
                 design_vars,
                 cost_comp=None,
                 driver=EasyScipyOptimizeDriver(),
                 constraints=[],
                 plot_comp=NoPlot(),
                 record_id=None,
                 expected_cost=1,
                 ext_vars={},
                 post_constraints=[],
                 approx_totals=False):
        """Initialize TopFarmProblem

        Parameters
        ----------
        design_vars : dict or list of key-initial_value-tuples
            Design variables for the problem.\n
            Ex: {'x': [1,2,3], 'y':([3,2,1],0,1), 'z':([4,5,6],[4,5,4], [6,7,6])}\n
            Ex: [('x', [1,2,3]), ('y',([3,2,1],0,1)), ('z',([4,5,6],[4,5,4], [6,7,6]))]\n
            Ex: [('x', ([1,2,3],0,3,'m')), ('y',([3,2,1],'m')), ('z',([4,5,6],[4,5,4], [6,7,6]))]\n
            Ex: zip('xy', pos.T)\n
            The keys (x, y, z) are the names of the design variable.\n
            The values are either\n
            - the initial value or\n
            - on of the following tuples:
                (initial value, unit)
                (initial value, lower bound, upper bound)
                (initial value, lower bound, upper bound, unit)
        cost_comp : ExplicitComponent or TopFarmProblem or TopFarmGroup
            Component that provides the cost function. It has to be the style
            of an OpenMDAO v2 ExplicitComponent.
            Pure python cost functions can be wrapped using ``CostModelComponent``
            class in ``topfarm.cost_models.cost_model_wrappers``.\n
            ExplicitComponent are wrapped into a TopFarmGroup.\n
            For nested problems, the cost comp_comp is typically a TopFarmProblem
        driver : openmdao Driver, optinal
            Driver used to solve the optimization driver. For an example, see the
            ``EasyScipyOptimizeDriver`` class in ``topfarm.easy_drivers``.
        constraints : list of Constraint-objects
            E.g. XYBoundaryConstraint, SpacingConstraint
        plot_comp : ExplicitComponent, optional
            OpenMDAO ExplicitComponent used to plot the state (during
            optimization).
            For no plotting, pass in the ``topfarm.plotting.NoPlot`` class.
        record_id : string "<record_id>:<case>", optional
            Identifier for the optimization. Allows a user to restart an
            optimization where it left off.\n
            record_id can be name (saves as recordings/<name>.pkl), abs or relative path
            Case can be:\n
            - "", "latest", "-1": Continue from latest\n
            - "best": Continue from best case (minimum cost)\n
            - "0": Start from scratch (initial position)\n
            - "4": Start from case number 4\n
        expected_cost : int, float or None, optional
            Used to scale the cost, default is 1. This has influence on some drivers, e.g.
            SLSQP where it affects the step size\n
            If None, the value is found by evaluating the cost function
        ext_vars : dict or list of key-initial_value tuple
            Used for nested problems to propagate variables from parent problem\n
            Ex. {'type': [1,2,3]}\n
            Ex. [('type', [1,2,3])]\n
        post_constraints : list of Constraint-objects that needs the cost component to be
            evaluated, unlike (pre-)constraints which are evaluated before the cost component.
            E.g. LoadConstraint
        approx_totals : bool or dict
            If True, approximates the total derivative of the cost_comp group,
            skipping the partial ones. If it is a dictionary, it's elements
            are passed to the approx_totals function of an OpenMDAO Group.

        Examples
        --------
        See main() in the bottom of this file
        """
        if mpi.MPI:
            comm = None
        else:
            from openmdao.utils.mpi import FakeComm
            comm = FakeComm()

        Problem.__init__(self, comm=comm)
        if cost_comp:
            if isinstance(cost_comp, TopFarmProblem):
                cost_comp = cost_comp.as_component()
            elif isinstance(cost_comp,
                            ExplicitComponent) and (len(post_constraints) > 0):
                cost_comp = TopFarmGroup([cost_comp])
            cost_comp.parent = self
        self.cost_comp = cost_comp

        if isinstance(driver, list):
            driver = DOEDriver(ListGenerator(driver))
        elif isinstance(driver, DOEGenerator):
            driver = DOEDriver(generator=driver)
        self.driver = driver
        self.driver.recording_options['record_desvars'] = True
        self.driver.recording_options['includes'] = ['*']
        self.driver.recording_options['record_inputs'] = True

        self.plot_comp = plot_comp

        self.record_id = record_id
        self.load_recorder()
        if not isinstance(approx_totals, dict) and approx_totals:
            approx_totals = {'method': 'fd'}

        if not isinstance(design_vars, dict):
            design_vars = dict(design_vars)
        self.design_vars = design_vars
        self.indeps = self.model.add_subsystem('indeps',
                                               IndepVarComp(),
                                               promotes=['*'])
        for k, v in design_vars.items():
            if isinstance(v, tuple):
                if (not isinstance(v[-1], str)) or (not v[-1]):
                    design_vars[k] += (None, )
            else:
                design_vars[k] = (design_vars[k], None)
            v = design_vars[k]
            self.indeps.add_output(k, v[0], units=v[-1])

        for k in [topfarm.x_key, topfarm.y_key, topfarm.type_key]:
            if k in design_vars:
                self.n_wt = len(design_vars[k][0])
                break
        else:
            self.n_wt = 0

        constraints_as_penalty = (
            (not self.driver.supports['inequality_constraints']
             or isinstance(self.driver, SimpleGADriver)
             or isinstance(self.driver, EasySimpleGADriver))
            and len(constraints) + len(post_constraints) > 0)

        if len(constraints) > 0:
            self.model.add_subsystem('pre_constraints',
                                     ParallelGroup(),
                                     promotes=['*'])
            for constr in constraints:
                if constraints_as_penalty:
                    constr.setup_as_penalty(self)
                else:
                    constr.setup_as_constraint(self)
                    # Use the assembled Jacobian.
                    self.model.pre_constraints.options[
                        'assembled_jac_type'] = 'csc'
                    self.model.pre_constraints.linear_solver.assemble_jac = True
            penalty_comp = PenaltyComponent(constraints,
                                            constraints_as_penalty)
            self.model.add_subsystem('penalty_comp',
                                     penalty_comp,
                                     promotes=['*'])

        self.model.constraint_components = [
            constr.constraintComponent for constr in constraints
        ]

        for k, v in design_vars.items():
            if isinstance(driver, EasyDriverBase):
                kwargs = driver.get_desvar_kwargs(self.model, k, v)
            else:
                kwargs = EasyDriverBase.get_desvar_kwargs(
                    None, self.model, k, v)
            self.model.add_design_var(k, **kwargs)

        for k, v in ext_vars.items():
            self.indeps.add_output(k, v)
        self.ext_vars = ext_vars

        if cost_comp:
            self.model.add_subsystem('cost_comp', cost_comp, promotes=['*'])

            if expected_cost is None:
                expected_cost = self.evaluate()[0]
                self._setup_status = 0
            if isinstance(
                    driver,
                    EasyDriverBase) and driver.supports_expected_cost is False:
                expected_cost = 1
            if isinstance(cost_comp, Group) and approx_totals:
                cost_comp.approx_totals(**approx_totals)
            # Use the assembled Jacobian.
            if 'assembled_jac_type' in self.model.cost_comp.options:
                self.model.cost_comp.options['assembled_jac_type'] = 'dense'
                self.model.cost_comp.linear_solver.assemble_jac = True

        else:
            self.indeps.add_output('cost')

        if len(post_constraints) > 0:
            if constraints_as_penalty:
                penalty_comp = PostPenaltyComponent(post_constraints,
                                                    constraints_as_penalty)
                self.model.add_subsystem('post_penalty_comp',
                                         penalty_comp,
                                         promotes=['*'])
            else:
                for constr in post_constraints:
                    self.model.add_constraint(constr[0],
                                              upper=np.full(
                                                  self.n_wt, constr[1]))
                    # Use the assembled Jacobian.


#                    self.model.cost_comp.post_constraints.options['assembled_jac_type'] = 'csc'
#                    self.model.cost_comp.post_constraints.linear_solver.assemble_jac = True

        aggr_comp = AggregatedCost(constraints_as_penalty, constraints,
                                   post_constraints)
        self.model.add_subsystem('aggr_comp', aggr_comp, promotes=['*'])
        self.model.add_objective('aggr_cost', scaler=1 / abs(expected_cost))

        if plot_comp and not isinstance(plot_comp, NoPlot):
            self.model.add_subsystem('plot_comp', plot_comp, promotes=['*'])
            plot_comp.problem = self
            plot_comp.n_wt = self.n_wt

        self.setup()
Exemplo n.º 2
0
    def driver(self):
        # type: () -> Driver
        """Method to return a preconfigured driver.

        Returns
        -------
            Driver
                A preconfigured driver element to be used for the Problem instance.

        Raises
        ------
            ValueError
                Value error are raised if unsupported settings are encountered.
        """
        if self.driver_type == 'optimizer':
            # Find optimizer element in CMDOWS file
            opt_uid = self.driver_uid
            opt_elem = get_element_by_uid(self.elem_cmdows, opt_uid)
            # Load settings from CMDOWS file
            opt_package = get_opt_setting_safe(opt_elem, 'package', 'SciPy')
            opt_algo = get_opt_setting_safe(opt_elem, 'algorithm', 'SLSQP')
            opt_maxiter = get_opt_setting_safe(opt_elem,
                                               'maximumIterations',
                                               50,
                                               expected_type='int')
            opt_convtol = get_opt_setting_safe(opt_elem,
                                               'convergenceTolerance',
                                               1e-6,
                                               expected_type='float')

            # Apply settings to the driver
            # driver
            if opt_package == 'SciPy':
                driver = ScipyOptimizeDriver()
            elif opt_package == 'pyOptSparse':
                try:
                    from openmdao.api import pyOptSparseDriver
                except ImportError:
                    raise PyOptSparseImportError()
                driver = pyOptSparseDriver()
            else:
                raise ValueError(
                    'Unsupported package {} encountered in CMDOWS file for "{}".'
                    .format(opt_package, opt_uid))

            # optimization algorithm
            if opt_algo == 'SLSQP':
                driver.options['optimizer'] = 'SLSQP'
            elif opt_algo == 'COBYLA':
                driver.options['optimizer'] = 'COBYLA'
            elif opt_algo == 'L-BFGS-B':
                driver.options['optimizer'] = 'L-BFGS-B'
            else:
                raise ValueError(
                    'Unsupported algorithm {} encountered in CMDOWS file for "{}".'
                    .format(opt_algo, opt_uid))

            # maximum iterations and tolerance
            if isinstance(driver, ScipyOptimizeDriver):
                driver.options['maxiter'] = opt_maxiter
                driver.options['tol'] = opt_convtol
            elif isinstance(driver, pyOptSparseDriver):
                driver.opt_settings['MAXIT'] = opt_maxiter
                driver.opt_settings['ACC'] = opt_convtol

            # Set default display and output settings
            if isinstance(driver, ScipyOptimizeDriver):
                driver.options['disp'] = False  # Print the result
            return driver
        elif self.driver_type == 'doe':
            # Find DOE element in CMDOWS file
            doe_uid = self.driver_uid
            doe_elem = get_element_by_uid(self.elem_cmdows, doe_uid)
            # Load settings from CMDOWS file
            doe_method = get_doe_setting_safe(doe_elem, 'method',
                                              'Uniform design')  # type: str
            doe_runs = get_doe_setting_safe(doe_elem,
                                            'runs',
                                            5,
                                            expected_type='int',
                                            doe_method=doe_method,
                                            required_for_doe_methods=[
                                                'Latin hypercube design',
                                                'Uniform design',
                                                'Monte Carlo design'
                                            ])
            doe_center_runs = get_doe_setting_safe(
                doe_elem,
                'centerRuns',
                2,
                expected_type='int',
                doe_method=doe_method,
                required_for_doe_methods=['Box-Behnken design'])
            doe_seed = get_doe_setting_safe(doe_elem,
                                            'seed',
                                            None,
                                            expected_type='int',
                                            doe_method=doe_method,
                                            required_for_doe_methods=[
                                                'Latin hypercube design',
                                                'Uniform design',
                                                'Monte Carlo design'
                                            ])
            doe_levels = get_doe_setting_safe(
                doe_elem,
                'levels',
                2,
                expected_type='int',
                doe_method=doe_method,
                required_for_doe_methods=['Full factorial design'])

            # table
            doe_data = []
            if isinstance(doe_elem.find('settings/table'), _Element):
                doe_table = doe_elem.find('settings/table')
                doe_table_rows = [row for row in doe_table.iterchildren()]
                n_samples = len(
                    [exp for exp in doe_table_rows[0].iterchildren()])
                for idx in range(n_samples):
                    data_sample = []
                    for row_elem in doe_table_rows:
                        value = float(
                            row_elem.find(
                                'tableElement[@experimentID="{}"]'.format(
                                    idx)).text)
                        data_sample.append(
                            [row_elem.attrib['relatedParameterUID'], value])
                    doe_data.append(data_sample)
            else:
                if doe_method in ['Custom design table']:
                    raise ValueError(
                        'Table element with data for custom design table missing in '
                        'CMDOWS file.')

            # Apply settings to the driver
            # define driver
            driver = DOEDriver()

            # define generator
            if doe_method in ['Uniform design', 'Monte Carlo design']:
                driver.options['generator'] = UniformGenerator(
                    num_samples=doe_runs, seed=doe_seed)
            elif doe_method == 'Full factorial design':
                driver.options['generator'] = FullFactorialGenerator(
                    levels=doe_levels)
            elif doe_method == 'Box-Behnken design':
                driver.options['generator'] = BoxBehnkenGenerator(
                    center=doe_center_runs)
            elif doe_method == 'Latin hypercube design':
                driver.options['generator'] = LatinHypercubeGenerator(
                    samples=doe_runs, criterion='maximin', seed=doe_seed)
            elif doe_method == 'Custom design table':
                driver.options['generator'] = ListGenerator(data=doe_data)
            else:
                raise ValueError(
                    'Could not match the doe_method {} with methods from OpenMDAO.'
                    .format(doe_method))
            return driver
        else:
            return Driver()