Esempio n. 1
0
def cost_constraint_rule(backend_model, cost, loc_tech):
    """
    Combine investment and time varying costs into one cost per technology

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{cost}(cost, loc::tech) = \\boldsymbol{cost_{investment}}(cost, loc::tech)
            + \\sum_{timestep \\in timesteps} \\boldsymbol{cost_{var}}(cost, loc::tech, timestep)

    """
    # FIXME: remove check for operate from constraint files, avoid investment costs more intelligently?
    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_investment_cost') and backend_model.mode != 'operate':
        cost_investment = backend_model.cost_investment[cost, loc_tech]
    else:
        cost_investment = 0

    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_om_cost'):
        cost_var = sum(backend_model.cost_var[cost, loc_tech, timestep]
                       for timestep in backend_model.timesteps)
    else:
        cost_var = 0

    return (
        backend_model.cost[cost, loc_tech] == cost_investment + cost_var
    )
Esempio n. 2
0
def cost_constraint_rule(backend_model, cost, loc_tech):
    """
    Combine investment and time varying costs into one cost per technology

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{cost}(cost, loc::tech) = \\boldsymbol{cost_{investment}}(cost, loc::tech)
            + \\sum_{timestep \\in timesteps} \\boldsymbol{cost_{var}}(cost, loc::tech, timestep)

    """
    # FIXME: remove check for operate from constraint files, avoid investment costs more intelligently?
    if loc_tech_is_in(
            backend_model, loc_tech,
            'loc_techs_investment_cost') and backend_model.mode != 'operate':
        cost_investment = backend_model.cost_investment[cost, loc_tech]
    else:
        cost_investment = 0

    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_om_cost'):
        cost_var = sum(backend_model.cost_var[cost, loc_tech, timestep]
                       for timestep in backend_model.timesteps)
    else:
        cost_var = 0

    return (backend_model.cost[cost, loc_tech] == cost_investment + cost_var)
Esempio n. 3
0
def unit_capacity_systemwide_constraint_rule(backend_model, tech):
    """
    Set constraints to limit the number of purchased units of a single technology
    type across all locations in the model.

    The first valid case is applied:

    .. container:: scrolling-wrapper

        .. math::

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

    """

    if tech in backend_model.techs_transmission_names:
        all_loc_techs = [
            i for i in backend_model.loc_techs_transmission
            if i.split('::')[1].split(':')[0] == tech
        ]
        multiplier = 2  # there are always two technologies associated with one link
    else:
        all_loc_techs = [
            i for i in backend_model.loc_techs
            if i.split('::')[1] == tech
        ]
        multiplier = 1

    max_systemwide = get_param(backend_model, 'units_max_systemwide', tech)
    equals_systemwide = get_param(backend_model, 'units_equals_systemwide', tech)

    if np.isinf(po.value(max_systemwide)) and not equals_systemwide:
        return po.Constraint.NoConstraint
    elif equals_systemwide and np.isinf(po.value(equals_systemwide)):
        raise ValueError(
            'Cannot use inf for energy_cap_equals_systemwide for tech `{}`'.format(tech)
        )

    sum_expr_units = sum(
        backend_model.units[loc_tech] for loc_tech in all_loc_techs
        if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_milp')
    )
    sum_expr_purchase = sum(
        backend_model.purchased[loc_tech] for loc_tech in all_loc_techs
        if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_purchase')
    )

    if equals_systemwide:
        return sum_expr_units + sum_expr_purchase == equals_systemwide * multiplier
    else:
        return sum_expr_units + sum_expr_purchase <= max_systemwide * multiplier
Esempio n. 4
0
def unit_capacity_systemwide_milp_constraint_rule(backend_model, tech):
    """
    Set constraints to limit the number of purchased units of a single technology
    type across all locations in the model.

    The first valid case is applied:

    .. container:: scrolling-wrapper

        .. math::

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

    """

    if tech in getattr(backend_model, 'techs_transmission_names', []):
        all_loc_techs = [
            i for i in backend_model.loc_techs_transmission
            if i.split('::')[1].split(':')[0] == tech
        ]
        multiplier = 2  # there are always two technologies associated with one link
    else:
        all_loc_techs = [
            i for i in backend_model.loc_techs
            if i.split('::')[1] == tech
        ]
        multiplier = 1

    max_systemwide = get_param(backend_model, 'units_max_systemwide', tech)
    equals_systemwide = get_param(backend_model, 'units_equals_systemwide', tech)

    if np.isinf(po.value(max_systemwide)) and not equals_systemwide:
        return po.Constraint.NoConstraint
    elif equals_systemwide and np.isinf(po.value(equals_systemwide)):
        raise ValueError(
            'Cannot use inf for energy_cap_equals_systemwide for tech `{}`'.format(tech)
        )

    sum_expr_units = sum(
        backend_model.units[loc_tech] for loc_tech in all_loc_techs
        if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_milp')
    )
    sum_expr_purchase = sum(
        backend_model.purchased[loc_tech] for loc_tech in all_loc_techs
        if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_purchase')
    )

    if equals_systemwide:
        return sum_expr_units + sum_expr_purchase == equals_systemwide * multiplier
    else:
        return sum_expr_units + sum_expr_purchase <= max_systemwide * multiplier
Esempio n. 5
0
def cost_var_constraint_rule(backend_model, cost, loc_tech, timestep):
    """
    Calculate costs from time-varying decision variables

    .. container:: scrolling-wrapper

        .. math::

    """
    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 po.value(cost_om_prod):
        cost_prod = cost_om_prod * weight * backend_model.carrier_prod[
            loc_tech_carrier, timestep]
    else:
        cost_prod = 0

    if loc_tech_is_in(backend_model, loc_tech,
                      'loc_techs_supply_plus') and cost_om_con:
        resource_eff = get_param(backend_model, 'resource_eff',
                                 (loc_tech, timestep))
        if po.value(
                resource_eff
        ) > 0:  # In case resource_eff is zero, to avoid an infinite value
            # Dividing by r_eff here so we get the actual r used, not the r
            # moved into storage...
            cost_con = cost_om_con * weight * (
                backend_model.resource_con[loc_tech, timestep] / resource_eff)
        else:
            cost_con = 0
    elif loc_tech_is_in(backend_model, loc_tech,
                        'loc_techs_supply') and cost_om_con:
        energy_eff = get_param(backend_model, 'energy_eff',
                               (loc_tech, timestep))
        if po.value(
                energy_eff
        ) > 0:  # in case energy_eff is zero, to avoid an infinite value
            cost_con = cost_om_con * weight * (
                backend_model.carrier_prod[loc_tech_carrier, timestep] /
                energy_eff)
        else:
            cost_con = 0
    else:
        cost_con = 0

    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])
Esempio n. 6
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 po.value(cost_om_prod):
        cost_prod = cost_om_prod * weight * backend_model.carrier_prod[loc_tech_carrier, timestep]
    else:
        cost_prod = 0

    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_supply_plus') and cost_om_con:
        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') and cost_om_con:
        energy_eff = get_param(backend_model, 'energy_eff', (loc_tech, timestep))
        if po.value(energy_eff) > 0:  # in case energy_eff is zero, to avoid an infinite value
            cost_con = cost_om_con * weight * (backend_model.carrier_prod[loc_tech_carrier, timestep] / energy_eff)
        else:
            cost_con = 0
    else:
        cost_con = 0

    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])
Esempio n. 7
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 po.value(cost_om_prod):
        cost_prod = cost_om_prod * weight * backend_model.carrier_prod[loc_tech_carrier, timestep]
    else:
        cost_prod = 0

    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_supply_plus') and cost_om_con:
        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') and cost_om_con:
        energy_eff = get_param(backend_model, 'energy_eff', (loc_tech, timestep))
        if po.value(energy_eff) > 0:  # in case energy_eff is zero, to avoid an infinite value
            cost_con = cost_om_con * weight * (backend_model.carrier_prod[loc_tech_carrier, timestep] / energy_eff)
        else:
            cost_con = 0
    else:
        cost_con = 0

    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])
Esempio n. 8
0
def update_costs_investment_purchase_constraint(backend_model, cost, loc_tech):
    """
    Add binary investment costs (cost * binary_purchased_unit)

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{cost_{investment}}(cost, loc::tech) += \\boldsymbol{purchased}(loc::tech)
            \\times cost_{purchase}(cost, loc::tech) * timestep_{weight} * depreciation
            \\quad \\forall cost \\in costs, \\forall loc::tech \\in loc::techs_{cost_{investment}, purchase}

    """
    model_data_dict = backend_model.__calliope_model_data__
    ts_weight = get_timestep_weight(backend_model)
    depreciation_rate = model_data_dict['data']['cost_depreciation_rate'][(
        cost, loc_tech)]

    cost_purchase = get_param(backend_model, 'cost_purchase', (cost, loc_tech))
    cost_of_purchase = (backend_model.purchased[loc_tech] * cost_purchase *
                        ts_weight * depreciation_rate)

    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_transmission'):
        cost_of_purchase = cost_of_purchase / 2

    backend_model.cost_investment_rhs[cost, loc_tech].expr += cost_of_purchase

    return None
Esempio n. 9
0
def balance_demand_constraint_rule(backend_model, loc_tech, timestep):
    """
    Limit consumption from demand techs to their required resource.

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{carrier_{con}}(loc::tech::carrier, timestep) \\times \\eta_{energy}(loc::tech, timestep) \\geq
            required\_resource(loc::tech, timestep) \\quad \\forall loc::tech \\in loc::techs_{demand},
            \\forall timestep \\in timesteps

    If :math:`force\_resource(loc::tech)` is set:

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{carrier_{con}}(loc::tech::carrier, timestep) \\times \\eta_{energy}(loc::tech, timestep) =
            required\_resource(loc::tech, timestep)
            \\quad \\forall loc::tech \\in loc::techs_{demand}, \\forall timestep \\in timesteps

    Where:

    .. container:: scrolling-wrapper

        .. math::

            required\_resource(loc::tech, timestep) = resource(loc::tech, timestep)
            \\times resource\_scale(loc::tech)

    if :math:`loc::tech` is in :math:`loc::techs_{area}`:

    .. container:: scrolling-wrapper

        .. math::

            required\_resource(loc::tech, timestep) = resource(loc::tech, timestep)
            \\times resource\_scale(loc::tech) \\times \\boldsymbol{resource_{area}}(loc::tech)

    """
    model_data_dict = backend_model.__calliope_model_data__['data']

    resource = get_param(backend_model, 'resource', (loc_tech, timestep))
    energy_eff = get_param(backend_model, 'energy_eff', (loc_tech, timestep))
    resource_scale = get_param(backend_model, 'resource_scale', loc_tech)
    force_resource = get_param(backend_model, 'force_resource', loc_tech)

    loc_tech_carrier = model_data_dict['lookup_loc_techs'][loc_tech]
    carrier_con = backend_model.carrier_con[loc_tech_carrier, timestep] * energy_eff

    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_area'):
        required_resource = resource * resource_scale * backend_model.resource_area[loc_tech]
    else:
        required_resource = resource * resource_scale

    if po.value(force_resource):
        return carrier_con == required_resource
    else:
        return carrier_con >= required_resource
Esempio n. 10
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))

    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)
Esempio n. 11
0
def update_costs_investment_purchase_constraint(backend_model, cost, loc_tech):
    """
    Add binary investment costs (cost * binary_purchased_unit)

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{cost_{investment}}(cost, loc::tech) += \\boldsymbol{purchased}(loc::tech)
            \\times cost_{purchase}(cost, loc::tech) * timestep_{weight} * depreciation
            \\quad \\forall cost \\in costs, \\forall loc::tech \\in loc::techs_{cost_{investment}, purchase}

    """
    model_data_dict = backend_model.__calliope_model_data__
    ts_weight = get_timestep_weight(backend_model)
    depreciation_rate = model_data_dict['data']['cost_depreciation_rate'][(cost, loc_tech)]

    cost_purchase = get_param(backend_model, 'cost_purchase', (cost, loc_tech))
    cost_of_purchase = (
        backend_model.purchased[loc_tech] * cost_purchase * ts_weight * depreciation_rate
    )

    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_transmission'):
        cost_of_purchase = cost_of_purchase / 2

    backend_model.cost_investment_rhs[cost, loc_tech].expr += cost_of_purchase

    return None
Esempio n. 12
0
def balance_transmission_constraint_rule(backend_model, loc_tech, timestep):
    """
    Balance carrier production and consumption of transmission technologies

    .. container:: scrolling-wrapper

        .. math::

            -1 * \\boldsymbol{carrier_{con}}(loc_{from}::tech:loc_{to}::carrier, timestep)
            \\times \\eta_{energy}(loc::tech, timestep)
            = \\boldsymbol{carrier_{prod}}(loc_{to}::tech:loc_{from}::carrier, timestep)
            \\quad \\forall loc::tech:loc \\in locs::techs:locs_{transmission},
            \\forall timestep \\in timesteps

    Where a link is the connection between :math:`loc_{from}::tech:loc_{to}`
    and :math:`loc_{to}::tech:loc_{from}` for locations `to` and `from`.

    """
    model_data_dict = backend_model.__calliope_model_data__['data']

    energy_eff = get_param(backend_model, 'energy_eff', (loc_tech, timestep))
    loc_tech_carrier = model_data_dict['lookup_loc_techs'][loc_tech]
    remote_loc_tech = model_data_dict['lookup_remotes'][loc_tech]
    remote_loc_tech_carrier = model_data_dict['lookup_loc_techs'][remote_loc_tech]

    if (loc_tech_is_in(backend_model, remote_loc_tech, 'loc_techs_transmission')
            and get_param(backend_model, 'energy_prod', (loc_tech)) == 1):
        return (
            backend_model.carrier_prod[loc_tech_carrier, timestep] ==
            -1 * backend_model.carrier_con[remote_loc_tech_carrier, timestep] *
            energy_eff
        )
    else:
        return po.Constraint.NoConstraint
Esempio n. 13
0
def update_costs_investment_units_milp_constraint(backend_model, cost, loc_tech):
    """
    Add MILP investment costs (cost * number of units purchased)

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{cost_{investment}}(cost, loc::tech) += \\boldsymbol{units}(loc::tech)
            \\times cost_{purchase}(cost, loc::tech) * timestep_{weight} * depreciation
            \\quad \\forall cost \\in costs, \\forall loc::tech \\in loc::techs_{cost_{investment}, milp}
    """
    model_data_dict = backend_model.__calliope_model_data
    ts_weight = get_timestep_weight(backend_model)
    depreciation_rate = model_data_dict["data"]["cost_depreciation_rate"][
        (cost, loc_tech)
    ]

    cost_purchase = get_param(backend_model, "cost_purchase", (cost, loc_tech))
    cost_of_purchase = (
        backend_model.units[loc_tech] * cost_purchase * ts_weight * depreciation_rate
    )

    if loc_tech_is_in(backend_model, loc_tech, "loc_techs_transmission"):
        cost_of_purchase = cost_of_purchase / 2

    backend_model.cost_investment_rhs[cost, loc_tech].expr += cost_of_purchase

    return None
Esempio n. 14
0
def export_max_constraint_rule(backend_model, carrier, node, tech, timestep):
    """
    Set maximum export. All exporting technologies.

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{carrier_{export}}(loc::tech::carrier, timestep)
            \\leq export_{cap}(loc::tech)
            \\quad \\forall loc::tech::carrier \\in locs::tech::carriers_{export},
            \\forall timestep \\in timesteps

    If the technology is defined by integer units, not a continuous capacity,
    this constraint becomes:

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{carrier_{export}}(loc::tech::carrier, timestep)
            \\leq export_{cap}(loc::tech) \\times
            \\boldsymbol{operating_{units}}(loc::tech, timestep)

    """

    if loc_tech_is_in(backend_model, (node, tech), "operating_units_index"):
        operating_units = backend_model.operating_units[node, tech, timestep]
    else:
        operating_units = 1

    export_max = get_param(backend_model, "export_max", (node, tech))
    return (backend_model.carrier_export[carrier, node, tech, timestep] <=
            export_max * operating_units)
Esempio n. 15
0
def cost_expression_rule(backend_model, cost, node, tech):
    """
    Combine investment and time varying costs into one cost per technology.

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{cost}(cost, loc::tech) = \\boldsymbol{cost_{investment}}(cost, loc::tech)
            + \\sum_{timestep \\in timesteps} \\boldsymbol{cost_{var}}(cost, loc::tech, timestep)

    """

    if loc_tech_is_in(backend_model, (cost, node, tech),
                      "cost_investment_index"):
        cost_investment = backend_model.cost_investment[cost, node, tech]
    else:
        cost_investment = 0

    if hasattr(backend_model, "cost_var"):
        cost_var = po.quicksum(
            backend_model.cost_var[cost, node, tech, timestep]
            for timestep in backend_model.timesteps
            if [cost, node, tech, timestep] in backend_model.cost_var._index)
    else:
        cost_var = 0

    return cost_investment + cost_var
Esempio n. 16
0
def balance_transmission_constraint_rule(backend_model, loc_tech, timestep):
    """
    Balance carrier production and consumption of transmission technologies

    .. container:: scrolling-wrapper

        .. math::

            -1 * \\boldsymbol{carrier_{con}}(loc_{from}::tech:loc_{to}::carrier, timestep)
            \\times \\eta_{energy}(loc::tech, timestep)
            = \\boldsymbol{carrier_{prod}}(loc_{to}::tech:loc_{from}::carrier, timestep)
            \\quad \\forall loc::tech:loc \\in locs::techs:locs_{transmission},
            \\forall timestep \\in timesteps

    Where a link is the connection between :math:`loc_{from}::tech:loc_{to}`
    and :math:`loc_{to}::tech:loc_{from}` for locations `to` and `from`.

    """
    model_data_dict = backend_model.__calliope_model_data["data"]

    energy_eff = get_param(backend_model, "energy_eff", (loc_tech, timestep))
    loc_tech_carrier = model_data_dict["lookup_loc_techs"][loc_tech]
    remote_loc_tech = model_data_dict["lookup_remotes"][loc_tech]
    remote_loc_tech_carrier = model_data_dict["lookup_loc_techs"][
        remote_loc_tech]

    if (loc_tech_is_in(backend_model, remote_loc_tech,
                       "loc_techs_transmission")
            and get_param(backend_model, "energy_prod", (loc_tech)) == 1):
        return (backend_model.carrier_prod[loc_tech_carrier, timestep] == -1 *
                backend_model.carrier_con[remote_loc_tech_carrier, timestep] *
                energy_eff)
    else:
        return po.Constraint.NoConstraint
Esempio n. 17
0
def resource_availability_supply_plus_constraint_rule(backend_model, loc_tech,
                                                      timestep):
    """
    Limit production from supply_plus techs to their available resource.

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{resource_{con}}(loc::tech, timestep)
            \\leq available\_resource(loc::tech, timestep)
            \\quad \\forall loc::tech \\in loc::techs_{supply^{+}}, \\forall timestep \\in timesteps

    If :math:`force\_resource(loc::tech)` is set:

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{resource_{con}}(loc::tech, timestep)
            = available\_resource(loc::tech, timestep)
            \\quad \\forall loc::tech \\in loc::techs_{supply^{+}}, \\forall timestep \\in timesteps

    Where:

    .. container:: scrolling-wrapper

        .. math::

            available\_resource(loc::tech, timestep) = resource(loc::tech, timestep)
            \\times resource_{scale}(loc::tech)

    if :math:`loc::tech` is in :math:`loc::techs_{area}`:

    .. container:: scrolling-wrapper

        .. math::

            available\_resource(loc::tech, timestep) = resource(loc::tech, timestep)
            \\times resource_{scale}(loc::tech)
            \\times resource_{area}(loc::tech)

    """
    resource = get_param(backend_model, 'resource', (loc_tech, timestep))
    resource_scale = get_param(backend_model, 'resource_scale', loc_tech)
    force_resource = get_param(backend_model, 'force_resource', loc_tech)

    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_area'):
        available_resource = resource * resource_scale * backend_model.resource_area[
            loc_tech]
    else:
        available_resource = resource * resource_scale

    if po.value(force_resource):
        return backend_model.resource_con[loc_tech,
                                          timestep] == available_resource
    else:
        return backend_model.resource_con[loc_tech,
                                          timestep] <= available_resource
Esempio n. 18
0
def cost_constraint_rule(backend_model, cost, loc_tech):
    """
    Combine investment and time varying costs into one cost per technology
    """
    # FIXME: remove check for operate from constraint files, avoid investment costs more intelligently?
    if loc_tech_is_in(
            backend_model, loc_tech,
            'loc_techs_investment_cost') and backend_model.mode != 'operate':
        cost_investment = backend_model.cost_investment[cost, loc_tech]
    else:
        cost_investment = 0

    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_om_cost'):
        cost_var = sum(backend_model.cost_var[cost, loc_tech, timestep]
                       for timestep in backend_model.timesteps)
    else:
        cost_var = 0

    return (backend_model.cost[cost, loc_tech] == cost_investment + cost_var)
Esempio n. 19
0
 def _get_investment_cost(capacity_decision_variable, calliope_set):
     """
     Conditionally add investment costs, if the relevant set of technologies
     exists. Both inputs are strings.
     """
     if loc_tech_is_in(backend_model, loc_tech, calliope_set):
         _cost = (getattr(backend_model, capacity_decision_variable)[loc_tech] *
             get_param(backend_model, 'cost_' + capacity_decision_variable, (cost, loc_tech)))
         return _cost
     else:
         return 0
Esempio n. 20
0
 def _get_investment_cost(capacity_decision_variable, calliope_set):
     """
     Conditionally add investment costs, if the relevant set of technologies
     exists. Both inputs are strings.
     """
     if loc_tech_is_in(backend_model, loc_tech, calliope_set):
         _cost = (getattr(backend_model, capacity_decision_variable)[loc_tech] *
             get_param(backend_model, 'cost_' + capacity_decision_variable, (cost, loc_tech)))
         return _cost
     else:
         return 0
Esempio n. 21
0
 def _get_investment_cost(capacity_decision_variable):
     """
     Conditionally add investment costs, if the relevant set of technologies
     exists. Both inputs are strings.
     """
     if loc_tech_is_in(backend_model, (node, tech),
                       capacity_decision_variable + "_index"):
         _cost = getattr(
             backend_model,
             capacity_decision_variable)[node, tech] * get_param(
                 backend_model, "cost_" + capacity_decision_variable,
                 (cost, node, tech))
         return _cost
     else:
         return 0
Esempio n. 22
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

    """
    model_data_dict = backend_model.__calliope_model_data['data']
    threshold = model_data_dict['group_energy_cap_{}'.format(what)][(
        constraint_group)]

    if np.isnan(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 = None
        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

            if lhs is not None:
                lhs += weight * backend_model.energy_cap[loc_tech]
            else:
                lhs = weight * backend_model.energy_cap[loc_tech]

        rhs = threshold

        return equalizer(lhs, rhs, what)
Esempio n. 23
0
def storage_cap_constraint_rule(backend_model, constraint_group, what):
    """
    Enforce upper and lower bounds for storage_cap of storage_cap
    for groups of technologies and locations.

    .. container:: scrolling-wrapper

        .. math::

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

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

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

    if check_value(threshold):
        return return_noconstraint("storage_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.storage_cap[loc_tech])

        rhs = threshold

        return equalizer(sum(lhs), rhs, what)
Esempio n. 24
0
def balance_supply_plus_constraint_rule(backend_model, loc_tech, timestep):
    """
    Balance carrier production and resource consumption of supply_plus technologies
    alongside any use of resource storage.

    .. _system_balance_constraint:

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{storage}(loc::tech, timestep) =
            \\boldsymbol{storage}(loc::tech, timestep_{previous})
            \\times (1 - storage\\_loss(loc::tech, timestep))^{timestep\\_resolution(timestep)} +
            \\boldsymbol{resource_{con}}(loc::tech, timestep) \\times \\eta_{resource}(loc::tech, timestep) -
            \\frac{\\boldsymbol{carrier_{prod}}(loc::tech::carrier, timestep)}{\\eta_{energy}(loc::tech, timestep) \\times \\eta_{parasitic}(loc::tech, timestep)}
            \\quad \\forall loc::tech \\in loc::techs_{supply^{+}}, \\forall timestep \\in timesteps

    If *no* storage is defined for the technology, this reduces to:

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{resource_{con}}(loc::tech, timestep) \\times \\eta_{resource}(loc::tech, timestep) =
            \\frac{\\boldsymbol{carrier_{prod}}(loc::tech::carrier, timestep)}{\\eta_{energy}(loc::tech, timestep) \\times \\eta_{parasitic}(loc::tech, timestep)}
            \\quad \\forall loc::tech \\in loc::techs_{supply^{+}}, \\forall timestep \\in timesteps

    """

    model_data_dict = backend_model.__calliope_model_data__['data']
    model_attrs = backend_model.__calliope_model_data__['attrs']

    resource_eff = get_param(backend_model, 'resource_eff', (loc_tech, timestep))
    energy_eff = get_param(backend_model, 'energy_eff', (loc_tech, timestep))
    parasitic_eff = get_param(backend_model, 'parasitic_eff', (loc_tech, timestep))
    total_eff = energy_eff * parasitic_eff

    if po.value(total_eff) == 0:
        carrier_prod = 0
    else:
        loc_tech_carrier = model_data_dict['lookup_loc_techs'][loc_tech]
        carrier_prod = backend_model.carrier_prod[loc_tech_carrier, timestep] / total_eff

    # A) Case where no storage allowed
    if not loc_tech_is_in(backend_model, loc_tech, 'loc_techs_store'):
        return backend_model.resource_con[loc_tech, timestep] * resource_eff == carrier_prod

    # B) Case where storage is allowed
    else:
        resource = backend_model.resource_con[loc_tech, timestep] * resource_eff
        current_timestep = backend_model.timesteps.order_dict[timestep]
        if current_timestep == 0 and not model_attrs['run.cyclic_storage']:
            storage_previous_step = get_param(backend_model, 'storage_initial', loc_tech)
        elif (hasattr(backend_model, 'storage_inter_cluster') and
                model_data_dict['lookup_cluster_first_timestep'][timestep]):
            storage_previous_step = 0
        else:
            if (hasattr(backend_model, 'clusters') and
                    model_data_dict['lookup_cluster_first_timestep'][timestep]):
                previous_step = model_data_dict['lookup_cluster_last_timestep'][timestep]
            elif current_timestep == 0 and model_attrs['run.cyclic_storage']:
                previous_step = backend_model.timesteps[-1]
            else:
                previous_step = get_previous_timestep(backend_model.timesteps, timestep)
            storage_loss = get_param(backend_model, 'storage_loss', loc_tech)
            time_resolution = backend_model.timestep_resolution[previous_step]
            storage_previous_step = (
                ((1 - storage_loss) ** time_resolution) *
                backend_model.storage[loc_tech, previous_step]
            )

        return (
            backend_model.storage[loc_tech, timestep] ==
            storage_previous_step + resource - carrier_prod
        )
Esempio n. 25
0
def cost_investment_expression_rule(backend_model, cost, node, tech):
    """
    Calculate costs from capacity decision variables.

    Transmission technologies "exist" at two locations, so their cost
    is divided by 2.

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{cost_{investment}}(cost, loc::tech) =
            cost_{fractional\\_om}(cost, loc::tech) +
            cost_{fixed\\_om}(cost, loc::tech) + cost_{cap}(cost, loc::tech)

            cost_{cap}(cost, loc::tech) =
            depreciation\\_rate * ts\\_weight *
            (cost_{energy\\_cap}(cost, loc::tech) \\times \\boldsymbol{energy_{cap}}(loc::tech)
            + cost_{storage\\_cap}(cost, loc::tech) \\times \\boldsymbol{storage_{cap}}(loc::tech)
            + cost_{resource\\_cap}(cost, loc::tech) \\times \\boldsymbol{resource_{cap}}(loc::tech)
            + cost_{resource\\_area}(cost, loc::tech)) \\times \\boldsymbol{resource_{area}}(loc::tech)

            depreciation\\_rate =
            \\begin{cases}
                = 1 / plant\\_life,&
                    \\text{if } interest\\_rate = 0\\\\
                = \\frac{interest\\_rate \\times (1 + interest\\_rate)^{plant\\_life}}{(1 + interest\\_rate)^{plant\\_life} - 1},&
                    \\text{if } interest\\_rate \\gt 0\\\\
            \\end{cases}

            ts\\_weight = \\sum_{timestep \\in timesteps} (time\\_res(timestep) \\times weight(timestep)) \\times \\frac{1}{8760}

    """
    def _get_investment_cost(capacity_decision_variable):
        """
        Conditionally add investment costs, if the relevant set of technologies
        exists. Both inputs are strings.
        """
        if loc_tech_is_in(backend_model, (node, tech),
                          capacity_decision_variable + "_index"):
            _cost = getattr(
                backend_model,
                capacity_decision_variable)[node, tech] * get_param(
                    backend_model, "cost_" + capacity_decision_variable,
                    (cost, node, tech))
            return _cost
        else:
            return 0

    cost_energy_cap = backend_model.energy_cap[node, tech] * get_param(
        backend_model, "cost_energy_cap", (cost, node, tech))

    cost_storage_cap = _get_investment_cost("storage_cap")
    cost_resource_cap = _get_investment_cost("resource_cap")
    cost_resource_area = _get_investment_cost("resource_area")

    cost_om_annual_investment_fraction = get_param(
        backend_model, "cost_om_annual_investment_fraction",
        (cost, node, tech))
    cost_om_annual = get_param(backend_model, "cost_om_annual",
                               (cost, node, tech))

    if loc_tech_is_in(backend_model, (node, tech), "units_index"):
        cost_of_purchase = (get_param(backend_model, "cost_purchase",
                                      (cost, node, tech)) *
                            backend_model.units[node, tech])
    elif loc_tech_is_in(backend_model, (node, tech), "purchased_index"):
        cost_of_purchase = (get_param(backend_model, "cost_purchase",
                                      (cost, node, tech)) *
                            backend_model.purchased[node, tech])
    else:
        cost_of_purchase = 0

    ts_weight = get_timestep_weight(backend_model)
    depreciation_rate = get_param(backend_model, "cost_depreciation_rate",
                                  (cost, node, tech))

    cost_cap = (depreciation_rate * ts_weight *
                (cost_energy_cap + cost_storage_cap + cost_resource_cap +
                 cost_resource_area + cost_of_purchase))

    # Transmission technologies exist at two locations, thus their cost is divided by 2
    if backend_model.inheritance[tech].value.endswith("transmission"):
        cost_cap = cost_cap / 2

    cost_fractional_om = cost_om_annual_investment_fraction * cost_cap
    cost_fixed_om = cost_om_annual * backend_model.energy_cap[node,
                                                              tech] * ts_weight

    return cost_fractional_om + cost_fixed_om + cost_cap
Esempio n. 26
0
def balance_supply_plus_constraint_rule(backend_model, loc_tech, timestep):
    """
    Balance carrier production and resource consumption of supply_plus technologies
    alongside any use of resource storage.

    .. _system_balance_constraint:

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{storage}(loc::tech, timestep) =
            \\boldsymbol{storage}(loc::tech, timestep_{previous})
            \\times (1 - storage\\_loss(loc::tech, timestep))^{timestep\\_resolution(timestep)} +
            \\boldsymbol{resource_{con}}(loc::tech, timestep) \\times \\eta_{resource}(loc::tech, timestep) -
            \\frac{\\boldsymbol{carrier_{prod}}(loc::tech::carrier, timestep)}{\\eta_{energy}(loc::tech, timestep) \\times \\eta_{parasitic}(loc::tech, timestep)}
            \\quad \\forall loc::tech \\in loc::techs_{supply^{+}}, \\forall timestep \\in timesteps

    If *no* storage is defined for the technology, this reduces to:

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{resource_{con}}(loc::tech, timestep) \\times \\eta_{resource}(loc::tech, timestep) =
            \\frac{\\boldsymbol{carrier_{prod}}(loc::tech::carrier, timestep)}{\\eta_{energy}(loc::tech, timestep) \\times \\eta_{parasitic}(loc::tech, timestep)}
            \\quad \\forall loc::tech \\in loc::techs_{supply^{+}}, \\forall timestep \\in timesteps

    """

    model_data_dict = backend_model.__calliope_model_data["data"]
    run_config = backend_model.__calliope_run_config

    resource_eff = get_param(backend_model, "resource_eff",
                             (loc_tech, timestep))
    energy_eff = get_param(backend_model, "energy_eff", (loc_tech, timestep))
    parasitic_eff = get_param(backend_model, "parasitic_eff",
                              (loc_tech, timestep))
    total_eff = energy_eff * parasitic_eff

    if po.value(total_eff) == 0:
        carrier_prod = 0
    else:
        loc_tech_carrier = model_data_dict["lookup_loc_techs"][loc_tech]
        carrier_prod = (
            backend_model.carrier_prod[loc_tech_carrier, timestep] / total_eff)

    # A) Case where no storage allowed
    if not loc_tech_is_in(backend_model, loc_tech, "loc_techs_store"):
        return (backend_model.resource_con[loc_tech, timestep] *
                resource_eff == carrier_prod)

    # B) Case where storage is allowed
    else:
        resource = backend_model.resource_con[loc_tech,
                                              timestep] * resource_eff
        current_timestep = backend_model.timesteps.order_dict[timestep]
        if current_timestep == 0 and not run_config["cyclic_storage"]:
            storage_previous_step = (
                get_param(backend_model, "storage_initial", loc_tech) *
                backend_model.storage_cap[loc_tech])
        elif (hasattr(backend_model, "storage_inter_cluster")
              and model_data_dict["lookup_cluster_first_timestep"][timestep]):
            storage_previous_step = 0
        else:
            if (hasattr(backend_model, "clusters") and
                    model_data_dict["lookup_cluster_first_timestep"][timestep]
                ):
                previous_step = model_data_dict[
                    "lookup_cluster_last_timestep"][timestep]
            elif current_timestep == 0 and run_config["cyclic_storage"]:
                previous_step = backend_model.timesteps[-1]
            else:
                previous_step = get_previous_timestep(backend_model.timesteps,
                                                      timestep)
            storage_loss = get_param(backend_model, "storage_loss", loc_tech)
            time_resolution = backend_model.timestep_resolution[previous_step]
            storage_previous_step = (
                (1 - storage_loss)**
                time_resolution) * backend_model.storage[loc_tech,
                                                         previous_step]

        return (backend_model.storage[loc_tech,
                                      timestep] == storage_previous_step +
                resource - carrier_prod)
Esempio n. 27
0
def cost_investment_constraint_rule(backend_model, cost, loc_tech):
    """
    Calculate costs from capacity decision variables

    .. container:: scrolling-wrapper

        .. math::

    """
    model_data_dict = backend_model.__calliope_model_data__

    def _get_investment_cost(capacity_decision_variable, calliope_set):
        """
        Conditionally add investment costs, if the relevant set of technologies
        exists. Both inputs are strings.
        """
        if loc_tech_is_in(backend_model, loc_tech, calliope_set):
            _cost = (
                getattr(backend_model, capacity_decision_variable)[loc_tech] *
                get_param(backend_model, 'cost_' + capacity_decision_variable,
                          (cost, loc_tech)))
            return _cost
        else:
            return 0

    cost_energy_cap = (backend_model.energy_cap[loc_tech] *
                       get_param(backend_model, 'cost_energy_cap',
                                 (cost, loc_tech)))

    cost_storage_cap = _get_investment_cost('storage_cap', 'loc_techs_store')
    cost_resource_cap = _get_investment_cost('resource_cap',
                                             'loc_techs_supply_plus')
    cost_resource_area = _get_investment_cost('resource_area',
                                              'loc_techs_area')

    cost_om_annual_investment_fraction = get_param(
        backend_model, 'cost_om_annual_investment_fraction', (cost, loc_tech))
    cost_om_annual = get_param(backend_model, 'cost_om_annual',
                               (cost, loc_tech))

    ts_weight = get_timestep_weight(backend_model)
    depreciation_rate = model_data_dict['data']['cost_depreciation_rate'].get(
        (cost, loc_tech), 0)

    cost_con = (depreciation_rate * ts_weight *
                (cost_energy_cap + cost_storage_cap + cost_resource_cap +
                 cost_resource_area))

    # Transmission technologies exist at two locations, thus their cost is divided by 2
    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_transmission'):
        cost_con = cost_con / 2

    cost_fractional_om = cost_om_annual_investment_fraction * cost_con
    cost_fixed_om = cost_om_annual * backend_model.energy_cap[
        loc_tech] * ts_weight

    backend_model.cost_investment_rhs[cost,
                                      loc_tech].expr = (cost_fractional_om +
                                                        cost_fixed_om +
                                                        cost_con)

    return (backend_model.cost_investment[cost, loc_tech] ==
            backend_model.cost_investment_rhs[cost, loc_tech])
Esempio n. 28
0
def cost_var_expression_rule(backend_model, cost, node, 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

    """

    weight = backend_model.timestep_weights[timestep]

    all_costs = []

    def _sum(var_name, carriers=backend_model.carriers):
        return po.quicksum(
            getattr(backend_model, var_name)[carrier, node, tech, timestep]
            for carrier in carriers
            if [carrier, node, tech, timestep] in getattr(
                backend_model, f"{var_name}_index"))

    cost_om_prod = get_param(backend_model, "cost_om_prod",
                             (cost, node, tech, timestep))
    if backend_model.inheritance[tech].value.endswith("conversion_plus"):
        carriers = [backend_model.primary_carrier_out[:, tech].index()[0][0]]
        all_costs.append(cost_om_prod *
                         _sum("carrier_prod", carriers=carriers))
    else:
        all_costs.append(cost_om_prod * _sum("carrier_prod"))

    cost_om_con = get_param(backend_model, "cost_om_con",
                            (cost, node, tech, timestep))
    if cost_om_con:
        if loc_tech_is_in(backend_model, (node, tech), "resource_con_index"):
            all_costs.append(cost_om_con *
                             backend_model.resource_con[node, tech, timestep])
        elif backend_model.inheritance[tech].value.endswith("supply"):
            energy_eff = get_param(backend_model, "energy_eff",
                                   (node, tech, timestep))
            # in case energy_eff is zero, to avoid an infinite value
            if po.value(energy_eff) > 0:
                all_costs.append(cost_om_con *
                                 (_sum("carrier_prod") / energy_eff))
        elif backend_model.inheritance[tech].value.endswith("conversion_plus"):
            carriers = [
                backend_model.primary_carrier_in[:, tech].index()[0][0]
            ]
            all_costs.append(cost_om_con * (-1) *
                             _sum("carrier_con", carriers=carriers))
        else:
            all_costs.append(cost_om_con * (-1) * _sum("carrier_con"))
    if hasattr(backend_model, "export_carrier"):
        export_carrier = backend_model.export_carrier[:, node, tech].index()
    else:
        export_carrier = []
    if len(export_carrier) > 0:
        all_costs.append(
            get_param(backend_model, "cost_export",
                      (cost, node, tech, timestep)) *
            backend_model.carrier_export[export_carrier[0][0], node, tech,
                                         timestep])

    return po.quicksum(all_costs) * weight
Esempio n. 29
0
def cost_investment_constraint_rule(backend_model, cost, loc_tech):
    """
    Calculate costs from capacity decision variables.

    Transmission technologies "exist" at two locations, so their cost
    is divided by 2.

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{cost_{investment}}(cost, loc::tech) =
            cost_{fractional\\_om}(cost, loc::tech) +
            cost_{fixed\\_om}(cost, loc::tech) + cost_{con}(cost, loc::tech)

            cost_{con}(cost, loc::tech) =
            depreciation\\_rate * ts\\_weight *
            (cost_{energy\\_cap}(cost, loc::tech) \\times \\boldsymbol{energy_{cap}}(loc::tech)
            + cost_{storage\\_cap}(cost, loc::tech) \\times \\boldsymbol{storage_{cap}}(loc::tech)
            + cost_{resource\\_cap}(cost, loc::tech) \\times \\boldsymbol{resource_{cap}}(loc::tech)
            + cost_{resource\\_area}(cost, loc::tech)) \\times \\boldsymbol{resource_{area}}(loc::tech)

            depreciation\\_rate =
            \\begin{cases}
                = 1 / plant\\_life,&
                    \\text{if } interest\\_rate = 0\\\\
                = \\frac{interest\\_rate \\times (1 + interest\\_rate)^{plant\\_life}}{(1 + interest\\_rate)^{plant\\_life} - 1},&
                    \\text{if } interest\\_rate \\gt 0\\\\
            \\end{cases}

            ts\\_weight = \\sum_{timestep \\in timesteps} (time\\_res(timestep) \\times weight(timestep)) \\times \\frac{1}{8760}

    """
    model_data_dict = backend_model.__calliope_model_data__

    def _get_investment_cost(capacity_decision_variable, calliope_set):
        """
        Conditionally add investment costs, if the relevant set of technologies
        exists. Both inputs are strings.
        """
        if loc_tech_is_in(backend_model, loc_tech, calliope_set):
            _cost = (
                getattr(backend_model, capacity_decision_variable)[loc_tech] *
                get_param(backend_model, 'cost_' + capacity_decision_variable,
                          (cost, loc_tech)))
            return _cost
        else:
            return 0

    cost_energy_cap = (backend_model.energy_cap[loc_tech] *
                       get_param(backend_model, 'cost_energy_cap',
                                 (cost, loc_tech)))

    cost_storage_cap = _get_investment_cost('storage_cap', 'loc_techs_store')
    cost_resource_cap = _get_investment_cost('resource_cap',
                                             'loc_techs_supply_plus')
    cost_resource_area = _get_investment_cost('resource_area',
                                              'loc_techs_area')

    cost_om_annual_investment_fraction = get_param(
        backend_model, 'cost_om_annual_investment_fraction', (cost, loc_tech))
    cost_om_annual = get_param(backend_model, 'cost_om_annual',
                               (cost, loc_tech))

    ts_weight = get_timestep_weight(backend_model)
    depreciation_rate = model_data_dict['data']['cost_depreciation_rate'].get(
        (cost, loc_tech), 0)

    cost_con = (depreciation_rate * ts_weight *
                (cost_energy_cap + cost_storage_cap + cost_resource_cap +
                 cost_resource_area))

    # Transmission technologies exist at two locations, thus their cost is divided by 2
    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_transmission'):
        cost_con = cost_con / 2

    cost_fractional_om = cost_om_annual_investment_fraction * cost_con
    cost_fixed_om = cost_om_annual * backend_model.energy_cap[
        loc_tech] * ts_weight

    backend_model.cost_investment_rhs[cost,
                                      loc_tech].expr = (cost_fractional_om +
                                                        cost_fixed_om +
                                                        cost_con)

    return (backend_model.cost_investment[cost, loc_tech] ==
            backend_model.cost_investment_rhs[cost, loc_tech])
Esempio n. 30
0
def balance_supply_plus_constraint_rule(backend_model, loc_tech, timestep):
    """
    Balance carrier production and resource consumption of supply_plus technologies
    alongside any use of resource storage.

    .. _system_balance_constraint:

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{storage}(loc::tech, timestep) =
            \\boldsymbol{storage}(loc::tech, timestep_{previous})
            \\times (1 - storage\_loss(loc::tech, timestep))^{timestep\_resolution(timestep)} +
            \\boldsymbol{resource_{con}}(loc::tech, timestep) -
            \\frac{\\boldsymbol{carrier_{prod}}(loc::tech::carrier, timestep)}{\\eta_{energy}(loc::tech, timestep) \\times \\eta_{parasitic}(loc::tech, timestep)}
            \\quad \\forall loc::tech \\in loc::techs_{supply^{+}}, \\forall timestep \\in timesteps

    If *no* storage is defined for the technology, this reduces to:

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{resource_{con}}(loc::tech, timestep) =
            \\frac{\\boldsymbol{carrier_{prod}}(loc::tech::carrier, timestep)}{\\eta_{energy}(loc::tech, timestep) \\times \\eta_{parasitic}(loc::tech, timestep)}
            \\quad \\forall loc::tech \\in loc::techs_{supply^{+}}, \\forall timestep \\in timesteps

    """

    model_data_dict = backend_model.__calliope_model_data__['data']

    energy_eff = get_param(backend_model, 'energy_eff', (loc_tech, timestep))
    parasitic_eff = get_param(backend_model, 'parasitic_eff', (loc_tech, timestep))
    total_eff = energy_eff * parasitic_eff

    if po.value(total_eff) == 0:
        carrier_prod = 0
    else:
        loc_tech_carrier = model_data_dict['lookup_loc_techs'][loc_tech]
        carrier_prod = backend_model.carrier_prod[loc_tech_carrier, timestep] / total_eff

    # A) Case where no storage allowed
    if not loc_tech_is_in(backend_model, loc_tech, 'loc_techs_store'):
        return backend_model.resource_con[loc_tech, timestep] == carrier_prod

    # B) Case where storage is allowed
    else:
        resource = backend_model.resource_con[loc_tech, timestep]
        if backend_model.timesteps.order_dict[timestep] == 0:
            storage_previous_step = get_param(backend_model, 'storage_initial', loc_tech)
        else:
            storage_loss = get_param(backend_model, 'storage_loss', loc_tech)
            previous_step = get_previous_timestep(backend_model, timestep)
            time_resolution = backend_model.timestep_resolution[previous_step]
            storage_previous_step = (
                ((1 - storage_loss) ** time_resolution) *
                backend_model.storage[loc_tech, previous_step]
            )

        return (
            backend_model.storage[loc_tech, timestep] ==
            storage_previous_step + resource - carrier_prod
        )
Esempio n. 31
0
def cost_investment_constraint_rule(backend_model, cost, loc_tech):
    """
    Calculate costs from capacity decision variables.

    Transmission technologies "exist" at two locations, so their cost
    is divided by 2.

    .. container:: scrolling-wrapper

        .. math::

            \\boldsymbol{cost_{investment}}(cost, loc::tech) =
            cost_{fractional\\_om}(cost, loc::tech) +
            cost_{fixed\\_om}(cost, loc::tech) + cost_{con}(cost, loc::tech)

            cost_{con}(cost, loc::tech) =
            depreciation\\_rate * ts\\_weight *
            (cost_{energy\\_cap}(cost, loc::tech) \\times \\boldsymbol{energy_{cap}}(loc::tech)
            + cost_{storage\\_cap}(cost, loc::tech) \\times \\boldsymbol{storage_{cap}}(loc::tech)
            + cost_{resource\\_cap}(cost, loc::tech) \\times \\boldsymbol{resource_{cap}}(loc::tech)
            + cost_{resource\\_area}(cost, loc::tech)) \\times \\boldsymbol{resource_{area}}(loc::tech)

            depreciation\\_rate =
            \\begin{cases}
                = 1 / plant\\_life,&
                    \\text{if } interest\\_rate = 0\\\\
                = \\frac{interest\\_rate \\times (1 + interest\\_rate)^{plant\\_life}}{(1 + interest\\_rate)^{plant\\_life} - 1},&
                    \\text{if } interest\\_rate \\gt 0\\\\
            \\end{cases}

            ts\\_weight = \\sum_{timestep \\in timesteps} (time\\_res(timestep) \\times weight(timestep)) \\times \\frac{1}{8760}

    """
    model_data_dict = backend_model.__calliope_model_data__

    def _get_investment_cost(capacity_decision_variable, calliope_set):
        """
        Conditionally add investment costs, if the relevant set of technologies
        exists. Both inputs are strings.
        """
        if loc_tech_is_in(backend_model, loc_tech, calliope_set):
            _cost = (getattr(backend_model, capacity_decision_variable)[loc_tech] *
                get_param(backend_model, 'cost_' + capacity_decision_variable, (cost, loc_tech)))
            return _cost
        else:
            return 0

    cost_energy_cap = (backend_model.energy_cap[loc_tech]
        * get_param(backend_model, 'cost_energy_cap', (cost, loc_tech)))

    cost_storage_cap = _get_investment_cost('storage_cap', 'loc_techs_store')
    cost_resource_cap = _get_investment_cost('resource_cap', 'loc_techs_supply_plus')
    cost_resource_area = _get_investment_cost('resource_area', 'loc_techs_area')

    cost_om_annual_investment_fraction = get_param(backend_model, 'cost_om_annual_investment_fraction', (cost, loc_tech))
    cost_om_annual = get_param(backend_model, 'cost_om_annual', (cost, loc_tech))

    ts_weight = get_timestep_weight(backend_model)
    depreciation_rate = model_data_dict['data']['cost_depreciation_rate'].get((cost, loc_tech), 0)

    cost_con = (
        depreciation_rate * ts_weight *
        (cost_energy_cap + cost_storage_cap + cost_resource_cap +
         cost_resource_area)
    )

    # Transmission technologies exist at two locations, thus their cost is divided by 2
    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_transmission'):
            cost_con = cost_con / 2

    cost_fractional_om = cost_om_annual_investment_fraction * cost_con
    cost_fixed_om = cost_om_annual * backend_model.energy_cap[loc_tech] * ts_weight

    backend_model.cost_investment_rhs[cost, loc_tech].expr = (
        cost_fractional_om + cost_fixed_om + cost_con
    )

    return (
        backend_model.cost_investment[cost, loc_tech] ==
        backend_model.cost_investment_rhs[cost, loc_tech]
    )
Esempio n. 32
0
def balance_supply_constraint_rule(backend_model, loc_tech, timestep):
    """
    Limit production from supply techs to their available resource

    .. container:: scrolling-wrapper

        .. math::

            min\_use(loc::tech) \\times available\_resource(loc::tech, timestep) \\leq
            \\frac{\\boldsymbol{carrier_{prod}}(loc::tech::carrier, timestep)}{\\eta_{energy}(loc::tech, timestep)}
            \\geq available\_resource(loc::tech, timestep)
            \\quad \\forall loc::tech \\in loc::techs_{supply}, \\forall timestep \\in timesteps

    If :math:`force\_resource(loc::tech)` is set:

    .. container:: scrolling-wrapper

        .. math::

            \\frac{\\boldsymbol{carrier_{prod}}(loc::tech::carrier, timestep)}{\\eta_{energy}(loc::tech, timestep)}
            = available\_resource(loc::tech, timestep)
            \\quad \\forall loc::tech \\in loc::techs_{supply}, \\forall timestep \\in timesteps

    Where:

    .. container:: scrolling-wrapper

        .. math::

            available\_resource(loc::tech, timestep) = resource(loc::tech, timestep)
            \\times resource\_scale(loc::tech)

    if :math:`loc::tech` is in :math:`loc::techs_{area}`:

    .. container:: scrolling-wrapper

        .. math::

            available\_resource(loc::tech, timestep) = resource(loc::tech, timestep)
            \\times resource\_scale(loc::tech) \\times \\boldsymbol{resource_{area}}(loc::tech)

    """
    model_data_dict = backend_model.__calliope_model_data__['data']

    resource = get_param(backend_model, 'resource', (loc_tech, timestep))
    energy_eff = get_param(backend_model, 'energy_eff', (loc_tech, timestep))
    resource_scale = get_param(backend_model, 'resource_scale', loc_tech)
    force_resource = get_param(backend_model, 'force_resource', loc_tech)
    loc_tech_carrier = model_data_dict['lookup_loc_techs'][loc_tech]
    min_use = get_param(backend_model, 'resource_min_use', (loc_tech, timestep))

    if po.value(energy_eff) == 0:
        return backend_model.carrier_prod[loc_tech_carrier, timestep] == 0
    else:
        carrier_prod = backend_model.carrier_prod[loc_tech_carrier, timestep] / energy_eff

    if loc_tech_is_in(backend_model, loc_tech, 'loc_techs_area'):
        available_resource = resource * resource_scale * backend_model.resource_area[loc_tech]
    else:
        available_resource = resource * resource_scale

    if po.value(force_resource):
        return carrier_prod == available_resource
    elif min_use:
        return min_use * available_resource <= carrier_prod <= available_resource
    else:
        return carrier_prod <= available_resource