Пример #1
0
    def _get_bounds(backend_model, *idx):
        def _get_bound(bound):
            if bounds.get(bound) is not None:
                return get_param(backend_model, bounds.get(bound), idx)
            else:
                return None

        scale = _get_bound("scale")
        _equals = _get_bound("equals")
        _min = _get_bound("min")
        _max = _get_bound("max")

        if not invalid(_equals):
            if not invalid(scale):
                _equals *= scale
            bound_tuple = (_equals, _equals)
        else:
            if invalid(_min):
                _min = None
            if invalid(_max):
                _max = None
            bound_tuple = (_min, _max)

        if not invalid(scale):
            bound_tuple = tuple(i * scale for i in bound_tuple)

        return bound_tuple
Пример #2
0
    def test_invalid(self):
        pyomo_model = po.ConcreteModel()
        pyomo_model.new_set = po.Set(initialize=["a", "b"])
        pyomo_model.new_param = po.Param(
            pyomo_model.new_set,
            initialize={"a": 1},
            mutable=True,
            within=po.NonNegativeReals,
        )

        assert invalid(pyomo_model.new_param["a"]) is False
        assert invalid(pyomo_model.new_param["b"]) is True
Пример #3
0
def resource_area_constraint_rule(backend_model, constraint_group, what):
    """
    Enforce upper and lower bounds of resource_area for groups of
    technologies and locations.

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{resource_{area}}(loc::tech) \\leq group\\_resource\\_area\\_max\\\\

            \\boldsymbol{resource_{area}}(loc::tech) \\geq group\\_resource\\_area\\_min

    """
    threshold = get_param(backend_model, "group_resource_area_{}".format(what),
                          (constraint_group))

    if invalid(threshold):
        return return_noconstraint("resource_area", constraint_group)
    else:
        lhs_loc_techs = getattr(
            backend_model,
            "group_constraint_loc_techs_{}".format(constraint_group))

        lhs = sum(backend_model.resource_area[loc_tech]
                  for loc_tech in lhs_loc_techs)
        rhs = threshold

        return equalizer(lhs, rhs, what)
Пример #4
0
def cost_cap_constraint_rule(backend_model, group_name, cost, what):
    """
    Limit cost for a specific cost class to a certain value,
    i.e. Ɛ-constrained costs,
    for groups of technologies and locations.

    .. container:: scrolling-wrapper

        .. math::

            \\sum{loc::tech \\in loc\\_techs_{group\\_name}, timestep \\in timesteps}
            \\boldsymbol{cost}(cost, loc::tech, timestep)
            \\begin{cases}
                \\leq cost\\_max(cost)
                \\geq cost\\_min(cost)
                = cost\\_equals(cost)
            \\end{cases}

    """
    cost_cap = get_param(backend_model, "group_cost_{}".format(what),
                         (cost, group_name))

    if invalid(cost_cap):
        return return_noconstraint("cost_cap", group_name)
    else:
        loc_techs = [
            i for i in getattr(
                backend_model, "group_constraint_loc_techs_{}".format(
                    group_name)) if i in backend_model.loc_techs_cost
        ]

        sum_cost = sum(backend_model.cost[cost, loc_tech]
                       for loc_tech in loc_techs)

        return equalizer(sum_cost, cost_cap, what)
Пример #5
0
def energy_cap_share_constraint_rule(backend_model, constraint_group, what):
    """
    Enforces shares of energy_cap for groups of technologies and locations. The
    share is relative to ``supply`` and ``supply_plus`` technologies only.

    .. container:: scrolling-wrapper

        .. math::

            \\sum_{loc::tech \\in given\\_group} energy_{cap}(loc::tech) \\leq
            share \\times \\sum_{loc::tech \\in loc\\_tech\\_supply\\_all \\in given\\_locations} energy_{cap}(loc::tech)
    """
    share = get_param(backend_model, "group_energy_cap_share_{}".format(what),
                      (constraint_group))

    if invalid(share):
        return return_noconstraint("energy_cap_share", constraint_group)
    else:
        lhs_loc_techs = getattr(
            backend_model,
            "group_constraint_loc_techs_{}".format(constraint_group))
        lhs_locs = [loc_tech.split("::")[0] for loc_tech in lhs_loc_techs]
        rhs_loc_techs = [
            i for i in backend_model.loc_techs_supply_conversion_all
            if i.split("::")[0] in lhs_locs
        ]

        lhs = sum(backend_model.energy_cap[loc_tech]
                  for loc_tech in lhs_loc_techs)
        rhs = share * sum(backend_model.energy_cap[loc_tech]
                          for loc_tech in rhs_loc_techs)

        return equalizer(lhs, rhs, what)
Пример #6
0
def carrier_con_constraint_rule(backend_model, constraint_group, carrier,
                                what):
    """
    Enforces carrier_con for groups of technologies and locations,
    as a sum over the entire model period. limits are always negative, so min/max
    is relative to zero (i.e. min = -1 means carrier_con must be -1 or less)

    .. container:: scrolling-wrapper

        .. math::

            \\sum_{loc::tech::carrier \\in given\\_group, timestep \\in timesteps} carrier_{con}(loc::tech::carrier, timestep) \\geq carrier_con_max

    """
    limit = get_param(backend_model, "group_carrier_con_{}".format(what),
                      (carrier, constraint_group))

    if invalid(limit):
        return return_noconstraint("carrier_con", constraint_group)
    else:
        lhs_loc_techs = get_carrier_lhs_loc_techs(backend_model,
                                                  constraint_group)

        lhs = sum(backend_model.carrier_con[loc_tech + "::" + carrier,
                                            timestep]
                  for loc_tech in lhs_loc_techs
                  for timestep in backend_model.timesteps if loc_tech + "::" +
                  carrier in backend_model.loc_tech_carriers_con)

        return equalizer(limit, lhs, what)
Пример #7
0
def carrier_prod_constraint_rule(backend_model, constraint_group, carrier,
                                 what):
    """
    Enforces carrier_prod for groups of technologies and locations,
    as a sum over the entire model period.

    .. container:: scrolling-wrapper

        .. math::

            \\sum_{loc::tech::carrier \\in given\\_group, timestep \\in timesteps} carrier_{prod}(loc::tech::carrier, timestep) \\leq carrier_prod_max

    """
    limit = get_param(backend_model, "group_carrier_prod_{}".format(what),
                      (carrier, constraint_group))

    if invalid(limit):
        return return_noconstraint("carrier_prod", constraint_group)
    else:
        lhs_loc_techs = get_carrier_lhs_loc_techs(backend_model,
                                                  constraint_group)

        lhs = sum(backend_model.carrier_prod[loc_tech + "::" + carrier,
                                             timestep]
                  for loc_tech in lhs_loc_techs
                  for timestep in backend_model.timesteps if loc_tech + "::" +
                  carrier in backend_model.loc_tech_carriers_prod)
        return equalizer(lhs, limit, what)
Пример #8
0
def energy_capacity_systemwide_constraint_rule(backend_model, tech):
    """
    Set constraints to limit the capacity of a single technology type across all locations in the model.

    The first valid case is applied:

    .. container:: scrolling-wrapper

        .. math::

            \\sum_{loc}\\boldsymbol{energy_{cap}}(loc::tech)
            \\begin{cases}
                = energy_{cap, equals, systemwide}(loc::tech),&
                    \\text{if } energy_{cap, equals, systemwide}(loc::tech)\\\\
                \\leq energy_{cap, max, systemwide}(loc::tech),&
                    \\text{if } energy_{cap, max, systemwide}(loc::tech)\\\\
                \\text{unconstrained},& \\text{otherwise}
            \\end{cases}
            \\forall tech \\in techs

    """

    max_systemwide = get_param(backend_model, "energy_cap_max_systemwide",
                               tech)
    equals_systemwide = get_param(backend_model,
                                  "energy_cap_equals_systemwide", tech)
    energy_cap = po.quicksum(
        backend_model.energy_cap[node, tech] for node in backend_model.nodes
        if [node, tech] in backend_model.energy_cap._index)
    if not invalid(equals_systemwide):
        return energy_cap == equals_systemwide
    else:
        return energy_cap <= max_systemwide
Пример #9
0
def net_import_share_constraint_rule(backend_model, constraint_group, carrier,
                                     what):
    """
    Enforces demand shares of net imports from transmission technologies for groups of locations,
    on average over the entire model period. Transmission within the group are ignored. The share
    is relative to ``demand`` technologies only.

    .. container:: scrolling-wrapper

        .. math::

            \\sum_{loc::tech::carrier \\in loc\\_tech\\_carriers\\_transmission \\in given\\_locations, timestep \\in timesteps} carrier_{prod}(loc::tech::carrier, timestep)
            + \\sum_{loc::tech::carrier \\in loc\\_tech\\_carriers\\_transmission \\in given\\_locations, timestep \\in timesteps} carrier_{con}(loc::tech::carrier, timestep) \\leq
            share \\times \\sum_{loc::tech:carrier \\in loc\\_tech\\_demand \\in given\\_locations, timestep\\in timesteps}
            carrier_{con}(loc::tech::carrier, timestep)

    """
    share = get_param(
        backend_model,
        "group_net_import_share_{}".format(what),
        (carrier, constraint_group),
    )

    if invalid(share):
        return return_noconstraint("net_import_share", constraint_group)
    else:
        trans_loc_tech = getattr(
            backend_model,
            "group_constraint_loc_techs_{}".format(constraint_group))
        locs = set(loc_tech.split("::")[0] for loc_tech in trans_loc_tech)
        trans_loc_tech = filter(
            lambda loc_tec: loc_tec.split(":")[-1] not in locs, trans_loc_tech)
        demand_loc_tech = [
            i for i in backend_model.loc_tech_carriers_demand
            if i.split("::")[0] in locs
        ]

        lhs = sum(
            (backend_model.carrier_prod[loc_tech + "::" + carrier, timestep] +
             backend_model.carrier_con[loc_tech + "::" + carrier, timestep])
            for loc_tech in trans_loc_tech
            for timestep in backend_model.timesteps)
        rhs = -share * sum(backend_model.carrier_con[loc_tech, timestep]
                           for loc_tech in demand_loc_tech
                           for timestep in backend_model.timesteps)
        return equalizer(lhs, rhs, what)
Пример #10
0
def carrier_prod_share_per_timestep_constraint_rule(backend_model,
                                                    constraint_group, carrier,
                                                    timestep, what):
    """
    Enforces shares of carrier_prod for groups of technologies and locations,
    in each timestep. The share is relative to ``supply`` and ``supply_plus``
    technologies only.

    .. container:: scrolling-wrapper

        .. math::

            \\sum_{loc::tech::carrier \\in given\\_group} carrier_{prod}(loc::tech::carrier, timestep) \\leq
            share \\times \\sum_{loc::tech:carrier \\in loc\\_tech\\_carriers\\_supply\\_all \\in given\\_locations}
            carrier_{prod}(loc::tech::carrier, timestep) for timestep \\in timesteps

    """
    share = get_param(
        backend_model,
        "group_carrier_prod_share_per_timestep_{}".format(what),
        (carrier, constraint_group),
    )

    if invalid(share):
        return return_noconstraint("carrier_prod_share_per_timestep",
                                   constraint_group)
    else:
        lhs_loc_techs = get_carrier_lhs_loc_techs(backend_model,
                                                  constraint_group)
        rhs_loc_techs = get_carrier_prod_share_rhs_loc_techs(
            backend_model, lhs_loc_techs)

        lhs = sum(backend_model.carrier_prod[loc_tech + "::" + carrier,
                                             timestep]
                  for loc_tech in lhs_loc_techs)
        rhs = share * sum(backend_model.carrier_prod[loc_tech + "::" + carrier,
                                                     timestep]
                          for loc_tech in rhs_loc_techs)

        return equalizer(lhs, rhs, what)
Пример #11
0
def demand_share_per_timestep_constraint_rule(backend_model, group_name,
                                              carrier, timestep, what):
    """
    Enforces shares of demand of a carrier to be met by the given groups
    of technologies at the given locations, in each timestep.
    The share is relative to ``demand`` technologies only.

    .. container:: scrolling-wrapper

        .. math::

            \\sum_{loc::tech::carrier \\in given\\_group} carrier_{prod}(loc::tech::carrier, timestep) \\leq
            share \\times \\sum_{loc::tech:carrier \\in loc\\_techs\\_demand \\in given\\_locations}
            carrier_{con}(loc::tech::carrier, timestep) for timestep \\in timesteps

    """
    share = get_param(
        backend_model,
        "group_demand_share_per_timestep_{}".format(what),
        (carrier, group_name),
    )

    if invalid(share):
        return return_noconstraint("demand_share_per_timestep", group_name)
    else:
        (
            lhs_loc_tech_carriers,
            rhs_loc_tech_carriers,
        ) = get_demand_share_lhs_and_rhs_loc_tech_carriers(
            backend_model, group_name, carrier)

        lhs = sum(backend_model.carrier_prod[loc_tech_carrier, timestep]
                  for loc_tech_carrier in lhs_loc_tech_carriers)

        rhs = (share * -1 *
               sum(backend_model.carrier_con[loc_tech_carrier, timestep]
                   for loc_tech_carrier in rhs_loc_tech_carriers))

        return equalizer(lhs, rhs, what)
Пример #12
0
def energy_cap_constraint_rule(backend_model, constraint_group, what):
    """
    Enforce upper and lower bounds for energy_cap of energy_cap
    for groups of technologies and locations.

    .. container:: scrolling-wrapper

        .. math::

            \\sum_{loc::tech \\in given\\_group} energy_{cap}(loc::tech) \\leq energy\\_cap\\_max\\\\

            \\sum_{loc::tech \\in given\\_group} energy_{cap}(loc::tech) \\geq energy\\_cap\\_min

    """
    threshold = get_param(backend_model, "group_energy_cap_{}".format(what),
                          (constraint_group))

    if invalid(threshold):
        return return_noconstraint("energy_cap", constraint_group)
    else:
        lhs_loc_techs = getattr(
            backend_model,
            "group_constraint_loc_techs_{}".format(constraint_group))

        # Transmission techs only contribute half their capacity in each direction
        lhs = []
        for loc_tech in lhs_loc_techs:
            if loc_tech_is_in(backend_model, loc_tech,
                              "loc_techs_transmission"):
                weight = 0.5
            else:
                weight = 1

            lhs.append(weight * backend_model.energy_cap[loc_tech])

        rhs = threshold

        return equalizer(sum(lhs), rhs, what)
Пример #13
0
def demand_share_per_timestep_decision_sum_constraint_rule(
        backend_model, group_name, carrier):
    """
    Allows the model to decide on how a fraction of demand for a carrier is met
    by the given groups, which will all have the same share in each timestep.
    The share is relative to the actual demand from ``demand`` technologies only.

    The sum constraint ensures that all decision shares add up to the share of
    carrier demand specified in the constraint.

    This constraint is only applied if the share of carrier demand has been
    set to a not-None value.

 .. container:: scrolling-wrapper

        .. math::
            share = \\sum_{loc::tech::carrier \\in given\\_group}
            demand\\_share\\_per\\_timestep\\_decision(loc::tech::carrier)


    """
    share_of_carrier_demand = get_param(
        backend_model, "group_demand_share_per_timestep_decision",
        (carrier, group_name))

    # If inf was given that means that we don't limit the total share
    if invalid(share_of_carrier_demand) or np.isinf(share_of_carrier_demand):
        return return_noconstraint("demand_share_per_timestep_decision_sum",
                                   group_name)
    else:
        lhs_loc_tech_carriers, _ = get_demand_share_lhs_and_rhs_loc_tech_carriers(
            backend_model, group_name, carrier)

        return share_of_carrier_demand == sum(
            backend_model.demand_share_per_timestep_decision[loc_tech_carrier]
            for loc_tech_carrier in lhs_loc_tech_carriers)
Пример #14
0
def cost_var_constraint_rule(backend_model, cost, loc_tech, timestep):
    """
    Calculate costs from time-varying decision variables

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{cost_{var}}(cost, loc::tech, timestep) = cost_{prod}(cost, loc::tech, timestep) + cost_{con}(cost, loc::tech, timestep)

            cost_{prod}(cost, loc::tech, timestep) = cost_{om\\_prod}(cost, loc::tech, timestep) \\times weight(timestep) \\times \\boldsymbol{carrier_{prod}}(loc::tech::carrier, timestep)

            prod\\_con\\_eff =
            \\begin{cases}
                = \\boldsymbol{resource_{con}}(loc::tech, timestep),&
                    \\text{if } loc::tech \\in loc\\_techs\\_supply\\_plus \\\\
                = \\frac{\\boldsymbol{carrier_{prod}}(loc::tech::carrier, timestep)}{energy_eff(loc::tech, timestep)},&
                    \\text{if } loc::tech \\in loc\\_techs\\_supply \\\\
            \\end{cases}

            cost_{con}(cost, loc::tech, timestep) = cost_{om\\_con}(cost, loc::tech, timestep) \\times weight(timestep) \\times prod\\_con\\_eff

    """
    model_data_dict = backend_model.__calliope_model_data

    cost_om_prod = get_param(backend_model, "cost_om_prod",
                             (cost, loc_tech, timestep))
    cost_om_con = get_param(backend_model, "cost_om_con",
                            (cost, loc_tech, timestep))
    weight = backend_model.timestep_weights[timestep]

    loc_tech_carrier = model_data_dict["data"]["lookup_loc_techs"][loc_tech]

    if loc_tech_is_in(backend_model, loc_tech_carrier,
                      "loc_tech_carriers_prod") and not invalid(cost_om_prod):
        cost_prod = (cost_om_prod * weight *
                     backend_model.carrier_prod[loc_tech_carrier, timestep])
    else:
        cost_prod = 0

    cost_con = 0
    if not invalid(cost_om_con):
        if loc_tech_is_in(backend_model, loc_tech, "loc_techs_supply_plus"):
            cost_con = (cost_om_con * weight *
                        backend_model.resource_con[loc_tech, timestep])
        elif loc_tech_is_in(backend_model, loc_tech, "loc_techs_supply"):
            energy_eff = get_param(backend_model, "energy_eff",
                                   (loc_tech, timestep))
            # in case energy_eff is zero, to avoid an infinite value
            if po.value(energy_eff) > 0:
                cost_con = (
                    cost_om_con * weight *
                    (backend_model.carrier_prod[loc_tech_carrier, timestep] /
                     energy_eff))
        elif loc_tech_is_in(backend_model, loc_tech, "loc_techs_demand"):
            cost_con = (cost_om_con * weight * (-1) *
                        backend_model.carrier_con[loc_tech_carrier, timestep])

    backend_model.cost_var_rhs[cost, loc_tech,
                               timestep].expr = cost_prod + cost_con
    return (backend_model.cost_var[cost, loc_tech, timestep] ==
            backend_model.cost_var_rhs[cost, loc_tech, timestep])
Пример #15
0
def demand_share_per_timestep_decision_main_constraint_rule(
        backend_model, group_name, carrier, tech, timestep):
    """
    Allows the model to decide on how a fraction demand for a carrier is met
    by the given groups, which will all have the same share in each timestep.
    The share is relative to the actual demand from ``demand`` technologies only.

    The main constraint enforces that the shares are the same in each timestep.

    .. container:: scrolling-wrapper

        .. math::
            \\sum_{loc::tech::carrier \\in given\\_group} carrier_{prod}(loc::tech::carrier, timestep)

            =

            \\sum_{loc::tech::carrier \\in given\\_group}
            required\\_resource(loc::tech::carrier, timestep)

            \\times \\sum_{loc::tech::carrier \\in given\\_group}
            demand\\_share\\_per\\_timestep\\_decision(loc::tech::carrier)

            \\forall timestep \\in timesteps

            \\forall tech \\in techs

    """
    share_of_carrier_demand = get_param(
        backend_model, "group_demand_share_per_timestep_decision",
        (carrier, group_name))

    if invalid(share_of_carrier_demand):
        return return_noconstraint("demand_share_per_timestep_decision_main",
                                   group_name)
    else:
        # lhs are the supply technologies, rhs are the demand technologies
        (
            lhs_loc_tech_carriers,
            rhs_loc_tech_carriers,
        ) = get_demand_share_lhs_and_rhs_loc_tech_carriers(
            backend_model, group_name, carrier)
        # Filter the supply loc_tech_carriers by the current tech
        lhs_loc_tech_carriers = [
            i for i in lhs_loc_tech_carriers if "::{}::".format(tech) in i
        ]

        # Only techs that are in the given group are considered
        if len(lhs_loc_tech_carriers) == 0:
            return return_noconstraint(
                "demand_share_per_timestep_decision_main", group_name)

        lhs = sum(backend_model.carrier_prod[loc_tech_carrier, timestep]
                  for loc_tech_carrier in lhs_loc_tech_carriers)

        rhs = (-1 * sum(backend_model.required_resource[
            rhs_loc_tech_carrier.rsplit("::", 1)[0], timestep]
                        for rhs_loc_tech_carrier in rhs_loc_tech_carriers) *
               sum(backend_model.
                   demand_share_per_timestep_decision[lhs_loc_tech_carrier]
                   for lhs_loc_tech_carrier in lhs_loc_tech_carriers))

        return equalizer(lhs, rhs, "equals")