Example #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()
Example #2
0
    def __init__(self,
                 design_vars,
                 cost_comp,
                 driver=EasyScipyOptimizeDriver(),
                 constraints=[],
                 plot_comp=NoPlot(),
                 record_id=None,
                 expected_cost=1,
                 ext_vars={}):
        """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: 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
            - a tuple of (initial value, lower bound, upper bound)
        cost_comp : ExplicitComponent or TopFarmProblem
            A cost component in 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
            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 or float
            Used to scale the cost. This has influence on some drivers, e.g.
            SLSQP where it affects the step size
        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

        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 isinstance(cost_comp, TopFarmProblem):
            cost_comp = cost_comp.as_component()
        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.plot_comp = plot_comp

        self.record_id = record_id
        self.load_recorder()

        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 in [topfarm.x_key, topfarm.y_key, topfarm.type_key]:
            if k in design_vars:
                if isinstance(design_vars[k], tuple):
                    self.n_wt = len(design_vars[k][0])
                else:
                    self.n_wt = len(design_vars[k])
                break
        else:
            self.n_wt = 0

        for constr in constraints:
            if self.driver.supports['inequality_constraints']:
                if isinstance(self.driver, SimpleGADriver):
                    constr.setup_as_penalty(self)
                else:
                    constr.setup_as_constraint(self)
            else:
                constr.setup_as_penalty(self)
        self.model.constraint_components = [
            constr.constraintComponent for constr in constraints
        ]

        do = self.driver.options
        for k, v in design_vars.items():
            if isinstance(v, tuple):
                assert len(
                    v
                ) == 3, "Design_vars values must be either value or (value, lower, upper)"
                self.indeps.add_output(k, v[0])

                if ('optimizer' in do and do['optimizer'] == 'COBYLA'):
                    ref0 = np.min(v[1])
                    ref1 = np.max(v[2])
                    l, u = [lu * (ref1 - ref0) + ref0 for lu in [v[1], v[2]]]
                    kwargs = {
                        'ref0': ref0,
                        'ref': ref1,
                        'lower': l,
                        'upper': u
                    }
                else:
                    kwargs = {'lower': v[1], 'upper': v[2]}
            else:
                self.indeps.add_output(k, v)
                kwargs = {}

            if 'optimizer' in do and do['optimizer'] == 'SLSQP':
                # Upper and lower disturbs SLSQP when running with constraints. Add limits as constraints
                self.model.add_constraint(k, kwargs.get('lower', None),
                                          kwargs.get('upper', None))
                kwargs = {
                    'lower': np.nan,
                    'upper': np.nan
                }  # Default +/- sys.float_info.max does not work for SLSQP
            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

        self.model.add_subsystem('cost_comp', cost_comp, promotes=['*'])
        self.model.add_objective('cost', scaler=1 / abs(expected_cost))

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

        self.setup()