Beispiel #1
0
def flatten_dae_variables(model, time):
    """
    This function takes in a (hierarchical, block-structured) Pyomo
    model and a `ContinuousSet` and returns two lists of "flattened"
    variables. The first is a list of all `_VarData` that are not
    indexed by the `ContinuousSet` and the second is a list of
    `Reference` components such that each reference is indexed only by
    the specified `ContinuousSet`. This function is convenient for
    identifying variables that are implicitly indexed by the
    `ContinuousSet`, for example, a singleton `Var` living on a `Block`
    that is indexed by the `ContinuousSet`.

    Parameters
    ----------
    model : Concrete Pyomo model

    time : ``pyomo.dae.ContinuousSet``

    Returns
    -------
    Two lists
    """
    assert time.model() is model.model()

    block_queue = [model]
    regular_vars = []
    time_indexed_vars = []
    while block_queue:
        b = block_queue.pop(0)
        b_sets = b.index_set().subsets()
        if time in b_sets:
            for _slice in generate_time_indexed_block_slices(b, time):
                time_indexed_vars.append(Reference(_slice))
            continue
        block_queue.extend(list(b.component_objects(Block,
                                                    descend_into=False)))
        for v in b.component_objects(SubclassOf(Var), descend_into=False):
            v_sets = v.index_set().subsets()
            if time in v_sets:
                for _slice in generate_time_only_slices(v, time):
                    time_indexed_vars.append(Reference(_slice))
            else:
                regular_vars.extend(list(v.values()))

    return regular_vars, time_indexed_vars
Beispiel #2
0
    def test_set_variance(self):
        blk = self.make_block()
        time = blk.time
        t0 = time.first()

        variance_list = [(var[t0], 0.05)
                for var in blk.component_objects(SubclassOf(NmpcVar))]
        blk.set_variance(variance_list)

        for var in blk.DIFFERENTIAL_BLOCK[:].var:
            assert var.variance == 0.05
        for var in blk.INPUT_BLOCK[:].var:
            assert var.variance == 0.05
        for var in blk.ALGEBRAIC_BLOCK[:].var:
            assert var.variance == 0.05
        for var in blk.MEASUREMENT_BLOCK[:].var:
            assert var.variance == 0.05
        for var in blk.FIXED_BLOCK[:].var:
            assert var.variance == 0.05
        for var in blk.DERIVATIVE_BLOCK[:].var:
            assert var.variance == 0.05
Beispiel #3
0
def categorize_variables(model, time):
    assert time.model() is model.model()

    block_queue = [model]
    regular_vars = []
    time_indexed_vars = []
    while block_queue:
        b = block_queue.pop(0)
        b_sets = identify_member_sets(b.index_set())
        if time in b_sets:
            for _slice in generate_time_indexed_block_slices(b, time):
                time_indexed_vars.append(Reference(_slice))
            continue
        block_queue.extend(list(b.component_objects(Block,
                                                    descend_into=False)))
        for v in b.component_objects(SubclassOf(Var), descend_into=False):
            v_sets = identify_member_sets(v.index_set())
            if time in v_sets:
                for _slice in generate_time_only_slices(v, time):
                    time_indexed_vars.append(Reference(_slice))
            else:
                regular_vars.extend(list(v.values()))

    return regular_vars, time_indexed_vars
Beispiel #4
0
    def exitNode(self, node, data):
        if isinstance(node, ProductExpression):
            ans = self.mcpp.multiply(data[0], data[1])
        elif isinstance(node, SumExpression):
            ans = data[0]
            for arg in data[1:]:
                ans = self.mcpp.add(ans, arg)
        elif isinstance(node, PowExpression):
            if type(node.arg(1)) == int:
                ans = self.mcpp.try_binary_fcn(self.mcpp.power, data[0],
                                               data[1])
            elif type(node.arg(1)) == float:
                ans = self.mcpp.try_binary_fcn(self.mcpp.powerf, data[0],
                                               data[1])
            else:
                ans = self.mcpp.try_binary_fcn(self.mcpp.powerx, data[0],
                                               data[1])
        elif isinstance(node, ReciprocalExpression):
            # Note: unreachable after ReciprocalExpression was removed
            ans = self.mcpp.try_unary_fcn(self.mcpp.reciprocal, data[0])
        elif isinstance(node, DivisionExpression):
            ans = self.mcpp.try_binary_fcn(self.mcpp.divide, data[0], data[1])
        elif isinstance(node, NegationExpression):
            ans = self.mcpp.negation(data[0])
        elif isinstance(node, AbsExpression):
            ans = self.mcpp.try_unary_fcn(self.mcpp.mc_abs, data[0])
        elif isinstance(node, LinearExpression):
            raise NotImplementedError(
                'Quicksum has bugs that prevent proper usage of MC++.')
            # ans = self.mcpp.newConstant(node.constant)
            # for coef, var in zip(node.linear_coefs, node.linear_vars):
            #     ans = self.mcpp.add(
            #         ans,
            #         self.mcpp.multiply(
            #             self.mcpp.newConstant(coef),
            #             self.register_num(var)))
        elif isinstance(node, UnaryFunctionExpression):
            if node.name == "exp":
                ans = self.mcpp.try_unary_fcn(self.mcpp.exponential, data[0])
            elif node.name == "log":
                ans = self.mcpp.try_unary_fcn(self.mcpp.logarithm, data[0])
            elif node.name == "sin":
                ans = self.mcpp.try_unary_fcn(self.mcpp.trigSin, data[0])
            elif node.name == "cos":
                ans = self.mcpp.try_unary_fcn(self.mcpp.trigCos, data[0])
            elif node.name == "tan":
                ans = self.mcpp.try_unary_fcn(self.mcpp.trigTan, data[0])
            elif node.name == "asin":
                ans = self.mcpp.try_unary_fcn(self.mcpp.atrigSin, data[0])
            elif node.name == "acos":
                ans = self.mcpp.try_unary_fcn(self.mcpp.atrigCos, data[0])
            elif node.name == "atan":
                ans = self.mcpp.try_unary_fcn(self.mcpp.atrigTan, data[0])
            elif node.name == "sqrt":
                ans = self.mcpp.try_unary_fcn(self.mcpp.mc_sqrt, data[0])
            else:
                raise NotImplementedError("Unknown unary function: %s" %
                                          (node.name, ))
        elif isinstance(node, NPV_expressions):
            ans = self.mcpp.newConstant(value(data[0]))
        elif type(node) in nonpyomo_leaf_types:
            ans = self.mcpp.newConstant(node)
        elif not node.is_expression_type():
            ans = self.register_num(node)
        elif type(node) in SubclassOf(Expression) or isinstance(
                node, _ExpressionData):
            ans = data[0]
        else:
            raise RuntimeError("Unhandled expression type: %s" % (type(node)))

        if ans is None:
            msg = self.mcpp.get_last_exception_message()
            msg = msg.decode("utf-8")
            raise MCPP_Error(msg)

        return ans
Beispiel #5
0
    def exitNode(self, node, data):
        if isinstance(node, ProductExpression):
            ans = self.mcpp.new_mult(data[0], data[1])
        elif isinstance(node, SumExpression):
            ans = data[0]
            for arg in data[1:]:
                ans = self.mcpp.new_add(ans, arg)
        elif isinstance(node, PowExpression):
            if type(node.arg(1)) == int:
                ans = self.mcpp.new_power(data[0], data[1])
            else:
                # Non-integer exponent. Must reformulate.
                # We use x^n = exp(n*log(x))
                ans = self.mcpp.new_exponential(
                    self.mcpp.new_mult(data[1], self.mcpp.new_logarithm(data[0])))
        elif isinstance(node, ReciprocalExpression):
            ans = self.mcpp.new_reciprocal(
                self.mcpp.new_createConstant(1), data[0])
        elif isinstance(node, NegationExpression):
            ans = self.mcpp.new_negation(data[0])
        elif isinstance(node, AbsExpression):
            ans = self.mcpp.new_abs(data[0])
        elif isinstance(node, LinearExpression):
            raise NotImplementedError(
                'Quicksum has bugs that prevent proper usage of MC++.')
            # ans = self.mcpp.new_createConstant(node.constant)
            # for coef, var in zip(node.linear_coefs, node.linear_vars):
            #     ans = self.mcpp.new_add(
            #         ans,
            #         self.mcpp.new_mult(
            #             self.mcpp.new_createConstant(coef),
            #             self.register_num(var)))
        elif isinstance(node, UnaryFunctionExpression):
            if node.name == "exp":
                ans = self.mcpp.new_exponential(data[0])
            elif node.name == "log":
                ans = self.mcpp.new_logarithm(data[0])
            elif node.name == "sin":
                ans = self.mcpp.new_trigSin(data[0])
            elif node.name == "cos":
                ans = self.mcpp.new_trigCos(data[0])
            elif node.name == "tan":
                ans = self.mcpp.new_trigTan(data[0])
            elif node.name == "asin":
                ans = self.mcpp.new_atrigSin(data[0])
            elif node.name == "acos":
                ans = self.mcpp.new_atrigCos(data[0])
            elif node.name == "atan":
                ans = self.mcpp.new_atrigTan(data[0])
            elif node.name == "sqrt":
                ans = self.mcpp.new_sqrt(data[0])
            else:
                raise NotImplementedError("Unknown unary function: %s" % (node.name,))
        elif any(isinstance(node, npv) for npv in NPV_expressions):
            ans = self.mcpp.new_NPV(value(data[0]))
        elif type(node) in nonpyomo_leaf_types:
            ans = self.mcpp.new_createConstant(node)
        elif not node.is_expression_type():
            ans = self.register_num(node)
        elif type(node) in SubclassOf(Expression):
            ans = data[0]
        else:
            raise RuntimeError("Unhandled expression type: %s" % (type(node)))

        return ans
Beispiel #6
0
    def _construct(self):
        """ Generates time-indexed references and categorizes them. """
        model = self.mod
        time = self.time
        try:
            inputs = self._inputs
        except AttributeError:
            inputs = self._inputs = None
        try:
            measurements = self._measurements
        except AttributeError:
            measurements = self._measurements = None

        # TODO: Give the user the option to provide their own
        # category_dict (they know the structure of their model
        # better than I do...)
        if self._category_dict is not None:
            category_dict = self.category_dict = self._category_dict
            if (VC.INPUT not in category_dict and inputs is not None):
                self.category_dict[VC.INPUT] = inputs
            if (VC.MEASUREMENT not in category_dict
                    and measurements is not None):
                self.category_dict[VC.MEASUREMENT] = measurements
            self.dae_vars = []
            for categ, varlist in category_dict.items():
                if categ is not VC.MEASUREMENT:
                    # Assume that measurements are duplicates
                    self.dae_vars.extend(varlist)

        else:
            scalar_vars, dae_vars = flatten_dae_components(
                model,
                time,
                ctype=Var,
            )
            self.scalar_vars = scalar_vars
            self.dae_vars = dae_vars
            category_dict = categorize_dae_variables(
                dae_vars,
                time,
                inputs,
                measurements=measurements,
            )
            self.category_dict = category_dict

        keys = list(category_dict)
        for categ in keys:
            if not category_dict[categ]:
                # Empty categories cause problems for us down the road
                # due empty (unknown dimension) slices.
                category_dict.pop(categ)

        self._add_category_blocks()
        self._add_category_references()

        self.categories = set(category_dict)

        # Now that we don't rely on knowing what the categories
        # will be, we do not need these attributes. They are, however,
        # used in the tests, so for now, we add them if possible.
        if VC.DIFFERENTIAL in category_dict:
            self.differential_vars = category_dict[VC.DIFFERENTIAL]
        if VC.ALGEBRAIC in category_dict:
            self.algebraic_vars = category_dict[VC.ALGEBRAIC]
        if VC.DERIVATIVE in category_dict:
            self.derivative_vars = category_dict[VC.DERIVATIVE]
        if VC.INPUT in category_dict:
            self.input_vars = category_dict[VC.INPUT]
        if VC.FIXED in category_dict:
            self.fixed_vars = category_dict[VC.FIXED]
        if VC.MEASUREMENT in category_dict:
            self.measurement_vars = category_dict.pop(VC.MEASUREMENT)

        # The categories in category_dict now form a partition of the
        # time-indexed variables. This is necessary to have a well-defined
        # vardata map, which maps each vardata to a unique component indexed
        # only by time.

        # Maps each vardata (of a time-indexed var) to the NmpcVar
        # that contains it.
        self.vardata_map = ComponentMap(
            (var[t], var)
            for var in self.component_objects(SubclassOf(NmpcVar))
            #for varlist in category_dict.values()
            #for var in varlist
            for t in time if var.ctype is not MeasuredVar)
        # NOTE: looking up var[t] instead of iterating over values()
        # appears to be ~ 5x faster

        # These should be overridden by a call to `set_sample_time`
        # The defaults assume that the entire model is one sample.
        self.sample_points = [time.first(), time.last()]
        self.sample_point_indices = [1, len(time)]