Example #1
0
def _reset():
    build_indexed_BlockWVars.model = Block(concrete=True)
    build_indexed_BlockWVars.model.ndx = RangeSet(0, N - 1)
    build_indexed_BlockWVars.indexed_Block_rule = _indexed_Block_rule
Example #2
0
def _reset():
    build_indexed_Constraint.model = Block(concrete=True)
    build_indexed_Constraint.model.ndx = RangeSet(0, N - 1)
    build_indexed_Constraint.rule = _con_rule
Example #3
0
    def _create_using(self, model, **kwds):
        precision = kwds.pop('precision', 8)
        user_discretize = kwds.pop('discretize', set())
        verbose = kwds.pop('verbose', False)

        M = model.clone()

        # TODO: if discretize is not empty, we must translate those
        # components over to the components on the cloned instance
        _discretize = {}
        if user_discretize:
            for _var in user_discretize:
                _v = M.find_component(_var.name)
                if _v.component() is _v:
                    for _vv in _v.itervalues():
                        _discretize.setdefault(id(_vv), len(_discretize))
                else:
                    _discretize.setdefault(id(_v), len(_discretize))

        # Iterate over all Constraints and identify the bilinear and
        # quadratic terms
        bilinear_terms = []
        quadratic_terms = []
        for constraint in M.component_map(Constraint,
                                          active=True).itervalues():
            for cname, c in constraint._data.iteritems():
                if c.body.polynomial_degree() != 2:
                    continue
                self._collect_bilinear(c.body, bilinear_terms, quadratic_terms)

        # We want to find the (minimum?) number of variables to
        # discretize so that we cover all the bilinearities -- without
        # discretizing both sides of any single bilinear expression.
        # First step: figure out how many expressions each term appears
        # in
        _counts = {}
        for q in quadratic_terms:
            if not q[1].is_continuous():
                continue
            _id = id(q[1])
            if _id not in _counts:
                _counts[_id] = (q[1], set())
            _counts[_id][1].add(_id)
        for bi in bilinear_terms:
            for i in (0, 1):
                if not bi[i + 1].is_continuous():
                    continue
                _id = id(bi[i + 1])
                if _id not in _counts:
                    _counts[_id] = (bi[i + 1], set())
                _counts[_id][1].add(id(bi[2 - i]))

        _tmp_counts = dict(_counts)
        # First, remove the variables that the user wants to have discretized
        for _id in _discretize:
            for _i in _tmp_counts[_id][1]:
                if _i == _id:
                    continue
                _tmp_counts[_i][1].remove(_id)
            del _tmp_counts[_id]
        # All quadratic terms must be discretized (?)
        #for q in quadratic_terms:
        #    _id = id(q[1])
        #    if _id not in _tmp_counts:
        #        continue
        #    _discretize.setdefault(_id, len(_discretize))
        #    for _i in _tmp_counts[_id][1]:
        #        if _i == _id:
        #            continue
        #        _tmp_counts[_i][1].remove(_id)
        #    del _tmp_counts[_id]

        # Now pick a (minimal) subset of the terms in bilinear expressions
        while _tmp_counts:
            _ct, _id = max((len(_tmp_counts[i][1]), i) for i in _tmp_counts)
            if not _ct:
                break
            _discretize.setdefault(_id, len(_discretize))
            for _i in list(_tmp_counts[_id][1]):
                if _i == _id:
                    continue
                _tmp_counts[_i][1].remove(_id)
            del _tmp_counts[_id]

        #
        # Discretize things
        #

        # Define a block (namespace) for holding the disaggregated
        # variables and new constraints
        if False:  # Set to true when the LP writer is fixed
            M._radix_linearization = Block()
            _block = M._radix_linearization
        else:
            _block = M
        _block.DISCRETIZATION = RangeSet(precision)
        _block.DISCRETIZED_VARIABLES = RangeSet(0, len(_discretize) - 1)
        _block.z = Var(_block.DISCRETIZED_VARIABLES,
                       _block.DISCRETIZATION,
                       within=Binary)
        _block.dv = Var(_block.DISCRETIZED_VARIABLES,
                        bounds=(0, 2**-precision))

        # Actually discretize the terms we have marked for discretization
        for _id, _idx in iteritems(_discretize):
            if verbose:
                logger.info("Discretizing variable %s as %s" %
                            (_counts[_id][0].name, _idx))
            self._discretize_variable(_block, _counts[_id][0], _idx)

        _known_bilinear = {}
        # For each quadratic term, if it hasn't been discretized /
        # generated, do so, and remember the resulting W term for later
        # use...
        #for _expr, _x1 in quadratic_terms:
        #    self._discretize_term( _expr, _x1, _x1,
        #                           _block, _discretize, _known_bilinear )
        # For each bilinear term, if it hasn't been discretized /
        # generated, do so, and remember the resulting W term for later
        # use...
        for _expr, _x1, _x2 in bilinear_terms:
            self._discretize_term(_expr, _x1, _x2, _block, _discretize,
                                  _known_bilinear)

        # Return the discretized instance!
        return M
Example #4
0
def _reset():
    build_indexed_Var.model = Block(concrete=True)
    build_indexed_Var.model.ndx = RangeSet(0, N - 1)
    build_indexed_Var.bounds_rule = _bounds_rule
    build_indexed_Var.initialize_rule = _initialize_rule
    def create_model(self):
        """
        Create and return the mathematical model.
        """

        if options.DEBUG:
            logging.info("Creating model for day %d" % self.day_id)

        # Obtain the orders book
        book = self.orders
        complexOrders = self.complexOrders

        # Create the optimization model
        model = ConcreteModel()
        model.periods = Set(initialize=book.periods)
        maxPeriod = max(book.periods)
        model.bids = Set(initialize=range(len(book.bids)))
        model.L = Set(initialize=book.locations)
        model.sBids = Set(initialize=[
            i for i in range(len(book.bids)) if book.bids[i].type == 'SB'
        ])
        model.bBids = Set(initialize=[
            i for i in range(len(book.bids)) if book.bids[i].type == 'BB'
        ])
        model.cBids = RangeSet(len(complexOrders))  # Complex orders
        model.C = RangeSet(len(self.connections))
        model.directions = RangeSet(2)  # 1 == up, 2 = down TODO: clean

        # Variables
        model.xs = Var(model.sBids, domain=Reals,
                       bounds=(0.0, 1.0))  # Single period bids acceptance
        model.xb = Var(model.bBids, domain=Binary)  # Block bids acceptance
        model.xc = Var(model.cBids, domain=Binary)  # Complex orders acceptance
        model.pi = Var(model.L * model.periods,
                       domain=Reals,
                       bounds=self.priceCap)  # Market prices
        model.s = Var(model.bids, domain=NonNegativeReals)  # Bids
        model.sc = Var(model.cBids, domain=NonNegativeReals)  # complex orders
        model.complexVolume = Var(model.cBids, model.periods,
                                  domain=Reals)  # Bids
        model.pi_lg_up = Var(model.cBids * model.periods,
                             domain=NonNegativeReals)  # Market prices
        model.pi_lg_down = Var(model.cBids * model.periods,
                               domain=NonNegativeReals)  # Market prices
        model.pi_lg = Var(model.cBids * model.periods,
                          domain=Reals)  # Market prices

        def flowBounds(m, c, d, t):
            capacity = self.connections[c - 1].capacity_up[t] if d == 1 else \
                self.connections[c - 1].capacity_down[t]
            return (0, capacity)

        model.f = Var(model.C * model.directions * model.periods,
                      domain=NonNegativeReals,
                      bounds=flowBounds)
        model.u = Var(model.C * model.directions * model.periods,
                      domain=NonNegativeReals)

        # Objective
        def primalObj(m):
            # Single period bids cost
            expr = summation(
                {i: book.bids[i].price * book.bids[i].volume
                 for i in m.sBids}, m.xs)
            # Block bids cost
            expr += summation(
                {
                    i: book.bids[i].price * sum(book.bids[i].volumes.values())
                    for i in m.bBids
                }, m.xb)
            return -expr

        if options.PRIMAL and not options.DUAL:
            model.obj = Objective(rule=primalObj, sense=maximize)

        def primalDualObj(m):
            return primalObj(m) + sum(1e-5 * m.xc[i] for i in model.cBids)

        if options.PRIMAL and options.DUAL:
            model.obj = Objective(rule=primalDualObj, sense=maximize)

        # Complex order constraint
        if options.PRIMAL and options.DUAL:
            model.deactivate_suborders = ConstraintList()
            for o in model.cBids:
                sub_ids = complexOrders[o - 1].ids
                curves = complexOrders[o - 1].curves
                for id in sub_ids:
                    bid = book.bids[id]
                    if bid.period <= complexOrders[o - 1].SSperiods and bid.price == \
                            curves[bid.period].bids[0].price:
                        pass  # This bid, first step of the cruve in the scheduled stop periods, is not automatically deactivated when MIC constraint is not satisfied
                    else:
                        model.deactivate_suborders.add(
                            model.xs[id] <= model.xc[o])

        # Ramping constraints for complex orders
        def complex_volume_def_rule(m, o, p):
            sub_ids = complexOrders[o - 1].ids
            return m.complexVolume[o, p] == sum(m.xs[i] * book.bids[i].volume
                                                for i in sub_ids
                                                if book.bids[i].period == p)

        if options.PRIMAL:
            model.complex_volume_def = Constraint(model.cBids,
                                                  model.periods,
                                                  rule=complex_volume_def_rule)

        def complex_lg_down_rule(m, o, p):
            if p + 1 > maxPeriod or complexOrders[o - 1].ramp_down == None:
                return Constraint.Skip
            else:
                return m.complexVolume[o, p] - m.complexVolume[o, p + 1] <= complexOrders[
                                                                                o - 1].ramp_down * \
                                                                            m.xc[o]

        if options.PRIMAL and options.APPLY_LOAD_GRADIENT:
            model.complex_lg_down = Constraint(model.cBids,
                                               model.periods,
                                               rule=complex_lg_down_rule)

        def complex_lg_up_rule(m, o, p):
            if p + 1 > maxPeriod or complexOrders[o - 1].ramp_up == None:
                return Constraint.Skip
            else:
                return m.complexVolume[o, p + 1] - m.complexVolume[
                    o, p] <= complexOrders[o - 1].ramp_up

        if options.PRIMAL and options.APPLY_LOAD_GRADIENT:
            model.complex_lg_up = Constraint(
                model.cBids, model.periods,
                rule=complex_lg_up_rule)  # Balance constraint

        # Energy balance constraints
        balanceExpr = {l: {t: 0.0 for t in model.periods} for l in model.L}
        for i in model.sBids:  # Simple bids
            bid = book.bids[i]
            balanceExpr[bid.location][bid.period] += bid.volume * model.xs[i]
        for i in model.bBids:  # Block bids
            bid = book.bids[i]
            for t, v in bid.volumes.items():
                balanceExpr[bid.location][t] += v * model.xb[i]

        def balanceCstr(m, l, t):
            export = 0.0
            for c in model.C:
                if self.connections[c - 1].from_id == l:
                    export += m.f[c, 1, t] - m.f[c, 2, t]
                elif self.connections[c - 1].to_id == l:
                    export += m.f[c, 2, t] - m.f[c, 1, t]
            return balanceExpr[l][t] == export

        if options.PRIMAL:
            model.balance = Constraint(model.L * book.periods,
                                       rule=balanceCstr)

        # Surplus of single period bids
        def sBidSurplus(m, i):  # For the "usual" step orders
            bid = book.bids[i]
            if i in self.plain_single_orders:
                return m.s[i] >= (m.pi[bid.location, bid.period] -
                                  bid.price) * bid.volume
            else:
                return Constraint.Skip

        if options.DUAL:
            model.sBidSurplus = Constraint(model.sBids, rule=sBidSurplus)

        # Surplus definition for complex suborders accounting for impact of load gradient condition
        if options.DUAL:
            model.complex_sBidSurplus = ConstraintList()
            for o in model.cBids:
                sub_ids = complexOrders[o - 1].ids
                l = complexOrders[o - 1].location
                for i in sub_ids:
                    bid = book.bids[i]
                    model.complex_sBidSurplus.add(
                        model.s[i] >=
                        (model.pi[l, bid.period] + model.pi_lg[o, bid.period] -
                         bid.price) * bid.volume)

        def LG_price_def_rule(m, o, p):
            l = complexOrders[o - 1].location

            exp = 0
            if options.APPLY_LOAD_GRADIENT:
                D = complexOrders[o - 1].ramp_down
                U = complexOrders[o - 1].ramp_up
                if D is not None:
                    exp += (m.pi_lg_down[o, p - 1] if p > 1 else
                            0) - (m.pi_lg_down[o, p] if p < maxPeriod else 0)
                if U is not None:
                    exp -= (m.pi_lg_up[o, p - 1] if p > 1 else
                            0) - (m.pi_lg_up[o, p] if p < maxPeriod else 0)

            return m.pi_lg[o, p] == exp

        if options.DUAL:
            model.LG_price_def = Constraint(model.cBids,
                                            model.periods,
                                            rule=LG_price_def_rule)

        # Surplus of block bids
        def bBidSurplus(m, i):
            bid = book.bids[i]
            bidVolume = -sum(bid.volumes.values())
            bigM = (self.priceCap[1] -
                    self.priceCap[0]) * bidVolume  # FIXME tighten BIGM
            return m.s[i] + sum([
                m.pi[bid.location, t] * -v for t, v in bid.volumes.items()
            ]) >= bid.cost * bidVolume + bigM * (1 - m.xb[i])

        if options.DUAL:
            model.bBidSurplus = Constraint(model.bBids, rule=bBidSurplus)

        # Surplus of complex orders
        def cBidSurplus(m, o):
            complexOrder = complexOrders[o - 1]
            sub_ids = complexOrder.ids
            if book.bids[sub_ids[0]].volume > 0:  # supply
                bigM = sum((self.priceCap[1] - book.bids[i].price) *
                           book.bids[i].volume for i in sub_ids)
            else:
                bigM = sum((book.bids[i].price - self.priceCap[0]) *
                           book.bids[i].volume for i in sub_ids)
            return m.sc[o] + bigM * (1 - m.xc[o]) >= sum(m.s[i]
                                                         for i in sub_ids)

        if options.DUAL:
            model.cBidSurplus = Constraint(model.cBids, rule=cBidSurplus)

        # Surplus of complex orders
        def cBidSurplus_2(m, o):
            complexOrder = complexOrders[o - 1]
            expr = 0
            for i in complexOrder.ids:
                bid = book.bids[i]
                if (bid.period <= complexOrder.SSperiods) and (
                        bid.price
                        == complexOrder.curves[bid.period].bids[0].price):
                    expr += m.s[i]
            return m.sc[o] >= expr

        if options.DUAL:
            model.cBidSurplus_2 = Constraint(
                model.cBids, rule=cBidSurplus_2)  # MIC constraint

        def cMIC(m, o):
            complexOrder = complexOrders[o - 1]

            if complexOrder.FT == 0 and complexOrder.VT == 0:
                return Constraint.Skip

            expr = 0
            bigM = complexOrder.FT
            for i in complexOrder.ids:
                bid = book.bids[i]
                if (bid.period <= complexOrder.SSperiods) and (
                        bid.price
                        == complexOrder.curves[bid.period].bids[0].price):
                    bigM += (bid.volume * (self.priceCap[1] - bid.price)
                             )  # FIXME assumes order is supply
                expr += bid.volume * m.xs[i] * (bid.price - complexOrder.VT)

            return m.sc[o] + expr + bigM * (1 - m.xc[o]) >= complexOrder.FT

        if options.DUAL and options.PRIMAL:
            model.cMIC = Constraint(model.cBids, rule=cMIC)

        # Dual connections capacity
        def dualCapacity(m, c, t):
            exportPrices = 0.0
            for l in m.L:
                if l == self.connections[c - 1].from_id:
                    exportPrices += m.pi[l, t]
                elif l == self.connections[c - 1].to_id:
                    exportPrices -= m.pi[l, t]
            return m.u[c, 1, t] - m.u[c, 2, t] + exportPrices == 0.0

        if options.DUAL:
            model.dualCapacity = Constraint(model.C * model.periods,
                                            rule=dualCapacity)

        # Dual optimality
        def dualObj(m):
            dualObj = summation(m.s) + summation(m.sc)

            for o in m.cBids:
                sub_ids = complexOrders[o - 1].ids
                for id in sub_ids:
                    dualObj -= m.s[
                        id]  # Remove contribution of complex suborders which were accounted for in prevous summation over single bids

                if options.APPLY_LOAD_GRADIENT:
                    ramp_down = complexOrders[o - 1].ramp_down
                    ramp_up = complexOrders[o - 1].ramp_up
                    for p in m.periods:
                        if p == maxPeriod:
                            continue
                        if ramp_down is not None:
                            dualObj += ramp_down * m.pi_lg_down[
                                o, p]  # Add contribution of load gradient
                        if ramp_up is not None:
                            dualObj += ramp_up * m.pi_lg_up[
                                o, p]  # Add contribution of load gradient

            for c in model.C:
                for t in m.periods:
                    dualObj += self.connections[c - 1].capacity_up[t] * m.u[c,
                                                                            1,
                                                                            t]
                    dualObj += self.connections[c -
                                                1].capacity_down[t] * m.u[c, 2,
                                                                          t]

            return dualObj

        if not options.PRIMAL:
            model.obj = Objective(rule=dualObj, sense=minimize)

        def primalEqualsDual(m):
            return primalObj(m) >= dualObj(m)

        if options.DUAL and options.PRIMAL:
            model.primalEqualsDual = Constraint(rule=primalEqualsDual)

        self.model = model