# title: Demand share per timestep as decision variable
#
# description: |
#   Allows the model to decide on how a unitless of demand for a carrier is met by the given group of technologies, which will each have the same share in each timestep.
#   Variables and constraints defined here could be extended to iterate over nodes  and over carriers if desired.
#   If summing over nodes, remove the summation over nodes in the constraints and add it into the list in `demand_share_per_timestep_decision_sum` (if using).
#   If summing over carriers, the slicing of `sink_use_equals` will need to change per carrier by using a where statement in `slices: ...`.
#
#   The share is relative to the flow `sink` from a specified `demand` technology (or group thereof) only.
#
#   Specifying `relaxation` inside the constraint to a non-zero value allows the constraint some flexibility around a given value, making a model easier to solve.
#
#   New indexed parameters:
#
#   - ``relaxation`` (defined here directly)
#   - ``demand_share_limit`` (defined here directly)
#
#   Helper functions used:
#
#   - sum (expression)
#   - select_from_lookup_arrays (expression)
#
# ---

parameters:
  demand_share_relaxation:
    description: >-
      If given, the degree to which total demand share can deviate from demand_share_limit.
      That is, (lhs == rhs -> lhs >= 0.99 * rhs & lhs <= 1.01 * rhs).
      0 == no relaxation, 0.01 == 1% relaxation.
      This is usually only required to mitigate against numerical trouble when solving.
    default: 0
    unit: unitless

  demand_share_limit:
    description: The total share of demand that the technologies must meet in total.
    default: 1
    unit: unitless

lookups:
  decide_demand_share:
    description: >-
      Link between a generating technology and the consuming (i.e., demand)
      technology whose inflow they will be generating a share of.
    dtype: string
  demand_share_carrier:
    description: The carrier that is being tracked between generating and consuming technologies when setting the demand share.
    dtype: string

variables:
  demand_share_per_timestep_decision:
    description: >
      Relative share of demand that a given technology must meet per node.
    unit: unitless
    default: 0
    foreach: [nodes, techs]
    where: decide_demand_share
    bounds:
      min: 0
      max: .inf

checks:
  demand_share_is_fraction:
    where: demand_share_limit > 1 OR demand_share_limit < 0
    message: The demand share limit must be a fraction of demand, i.e. between 0 and 1.
    errors: raise

# The two main constraints enforce that the shares are the same in each timestep,
# with an optional relaxation.

constraints:
  demand_share_per_timestep_decision_main_min:
    description: >-
      Limit the lower bound of a technology's outflow to the share of demand
      that the model has decided it will meet.
    foreach: [nodes, techs, timesteps]
    where: demand_share_per_timestep_decision
    equations:
      - expression: >
          flow_out[carriers=$carrier] >=
          (1 - demand_share_relaxation)
          * select_from_lookup_arrays(sink_use_equals, techs=decide_demand_share)
          * demand_share_per_timestep_decision
    slices: &slice
      carrier:
        - expression: demand_share_carrier  # assigned as an indexed parameter

  demand_share_per_timestep_decision_main_max:
    description: >-
      Limit the upper bound of a technology's outflow to
      the share of demand that the model has decided it will meet.
    foreach: [nodes, techs, timesteps]
    where: demand_share_per_timestep_decision
    equations:
      - expression: >
          flow_out[carriers=$carrier] <=
          (1 + demand_share_relaxation)
          * select_from_lookup_arrays(sink_use_equals, techs=decide_demand_share)
          * demand_share_per_timestep_decision
    slices: *slice

# The optional additional sum constraint ensures that all decision shares add up
# to a specified share of carrier demand (here, 50% = 0.5 of electricity demand).

  demand_share_per_timestep_decision_sum:
    description: >-
      Limit the total share of demand that can be met by technologies controlled
      by the `demand_share_per_timestep_decision` variable.
    foreach: [nodes, timesteps]
    where: demand_share_per_timestep_decision AND demand_share_limit
    equations:
      - expression: >
          sum(demand_share_per_timestep_decision, over=[techs])
          == demand_share_limit
