示例#1
0
def add_indx_nonlinear_eq(constr,
                          indx,
                          pyo_vars,
                          eq_expr,
                          calc_jacs=True,
                          jacs=None):
    """Adds a nonlinear equation with the given index (tuple) to the model constraint constr, generating and storing a corresponding CasADi jacobian in a dictionary for cut generation"""
    raise DeprecationWarning(
        'This function is deprecated in preference of a new idiom given below. Comment out this line to continue using it.'
    )
    name = constr.local_name

    def eq_rule():
        # combines the LHS expression generated by eq_expr with a zero RHS to form either a Pyomo or CasADi equality expression.
        LHS = eq_expr(*indx, **pyo_vars) if isinstance(
            indx, tuple) else eq_expr(indx, **pyo_vars)
        if LHS is Constraint.NoConstraint or LHS is Constraint.Skip:
            return Constraint.NoConstraint
        else:
            return LHS == 0

    eq_expression = eq_rule()
    if eq_expression is Constraint.NoConstraint:
        # Checks to see if the expression returned by the equation rule calls for skipping the constraint. If so, do that.
        return
    else:
        # Otherwise, add the expression to the indexed constraint
        constr.add(indx, expr=eq_rule())

    if not calc_jacs:
        # If we should add the constraint but not calculate the jacobian, then simply exit at this point.
        return

    new_constr = constr[indx]
    # Create dictionaries for CasADi variables and flattened variables
    cas_vars = {}
    flat_vars = {}

    for var_name, pyo_var in pyo_vars.iteritems():
        if isinstance(pyo_var, Mapping):
            # The passed value is not a variable, but rather a dictionary of variables. Flatten this dictionary appropriately.
            raise NotImplementedError()
        elif pyo_var.is_indexed():
            # if the pyomo variable is indexed, generate a dictionary of CasADi variables
            cas_vars[var_name] = {
                indx: SX.sym(var_name + '[' + str(indx) + ']')
                for indx in pyo_var._index
            }
            # At the same time, generate a dictionary of flattened variables (with the indices enumerated) for future use
            flat_vars.update({
                var_name + '[' + str(indx) + ']':
                (cas_vars[var_name][indx], pyo_var[indx])
                for indx in pyo_var._index
            })
        else:
            # If the pyomo variable is not indexed, generate a single corresponding CasADi variable and add both to the flat variables dictionary
            cas_vars[var_name] = SX.sym(var_name)
            flat_vars[var_name] = (cas_vars[var_name], pyo_var)
    # Using the flat variables dictionary, generate a corresponding lists of CasADi variable names, CasADi variables and Pyomo (flattened) variables
    cas_var_name_list, var_list = zip(*flat_vars.iteritems())
    cas_var_list, pyo_var_list = zip(*var_list)

    f = eq_expr(*indx, **cas_vars) if isinstance(indx, tuple) else eq_expr(
        indx, **cas_vars)
    if f is Constraint.NoConstraint:
        # This would be a weird situation, where a non-indexed constraint returns a value indicating that no constraint is defined. In this case, nothing needs to be added to the jacobians dictionary.
        return
    # The options dictionary allows for more intuitive function calls later on. Note that the ordering of the CasADi variable name list matching the CasADi variable list is important.
    opt = {'input_scheme': cas_var_name_list, 'output_scheme': ['f']}
    # Generate a new CasADi function
    eqn = Function(name, cas_var_list, [f], opt)
    # Store the jacobian of the function with respect to each of the variables in an dictionary indexed by the corresponding Pyomo constraint. The stored value is an ordered dictionary so that the ordering of the variables will be preserved. This is important for future function calls.
    jacs[new_constr] = OrderedDict([(k, {
        'jac': eqn.jacobian(k, 'f'),
        'var': flat_vars[k][1]
    }) for k in cas_var_name_list])
示例#2
0
def apply_OA(nl_set, oa_block, jacs):
    """Applies outer approximation with augmented Lagrangian/equality relaxation scheme

    Args:
        equip (Block): Pyomo block for process unit description
        nl_set (set): Set of nonlinear constraints
        oa_block (Block): Pyomo block to hold generated OA cuts
        jacs (dict): dictionary in which to store jacobian information

    Returns:
        None

    Raises:
        ValueError: if a variable naming conflict exists
    """
    # Set up outer approximation iterations set
    oa_iter = oa_block.oa_iter = Set()
    for con_data in nl_set:
        if not con_data.active:
            continue  # if constraint is inactive, skip it
        # process this constraint
        var_dict = OrderedDict()
        # Build CasADi expression to mirror existing constraint
        f = process_constraint(con_data, var_dict)
        eqn = Function(
            # constr[indx].local_name,
            # TODO: need to figure out how to name function properly
            con_data.parent_component().local_name,
            [cas_var for pyo_var, cas_var in itervalues(var_dict)],
            [f],
            {
                'input_scheme': list(var_dict.keys()),
                'output_scheme': ['f']
            })
        # Store the jacobian functions for future evaluation
        jacs[con_data] = {
            cname: eqn.jacobian(cname, 'f')
            for cname in iterkeys(var_dict)
        }
        if '_vars_' in jacs[con_data]:
            raise ValueError(
                'Name conflict: cannot have a variable named "_vars_"')
        else:
            jacs[con_data]['_vars_'] = {
                pyo_var.local_name: pyo_var
                for pyo_var, cas_var in itervalues(var_dict)
            }

        # Generate empty constraints for holding OA constraints
        if not hasattr(oa_block, con_data.parent_component().local_name):
            if not con_data.parent_component().is_indexed():
                new_constr = Constraint(oa_iter)
            else:
                new_constr = Constraint(oa_iter,
                                        con_data.parent_component()._index)
            setattr(oa_block,
                    con_data.parent_component().local_name, new_constr)
            # Generate slack variables
            new_slack_var = Var(oa_iter,
                                domain=NonNegativeReals,
                                initialize=0,
                                bounds=(0, 1000))
            setattr(oa_block,
                    '_slack_' + con_data.parent_component().local_name,
                    new_slack_var)