variables:

  # MILP specific

  purchased_units:
    title: Number of purchased units
    description: >-
      Integer number of a technology that has been purchased,
      for any technology set to require integer capacity purchasing.
      This is used to allow installation of fixed capacity units of technologies (
      if `flow_cap_max` == `flow_cap_min`) and/or to set a fixed cost for a technology,
      irrespective of its installed capacity.
      On top of a fixed technology cost,
      a continuous cost for the quantity of installed capacity can still be applied.

      Since technology capacity is no longer a continuous decision variable,
      it is possible for these technologies to have a lower bound set on outflow/consumption
      which will only be enforced in those timesteps that the technology is operating.
      Otherwise, the same lower bound forces the technology to produce/consume
      that minimum amount of carrier in *every* timestep.
    default: 0
    unit: integer
    foreach: [nodes, techs]
    where: "cap_method==integer"
    domain: integer
    bounds:
      min: purchased_units_min
      max: purchased_units_max

  operating_units:
    title: Number of operating units
    description: >-
      Integer number of a technology that is operating in each timestep,
      for any technology set to require integer capacity purchasing.
    default: 0
    unit: integer
    foreach: [nodes, techs, timesteps]
    where: "integer_dispatch==True AND cap_method==integer"
    domain: integer
    bounds:
      min: 0
      max: .inf

  async_flow_switch:
    title: Asynchronous carrier flow switch
    description: >-
      Binary switch to force asynchronous outflow/consumption of technologies with
      both `flow_in` and `flow_out` defined.
      This ensures that a technology with carrier flow efficiencies < 100% cannot
      produce and consume a flow simultaneously to remove unwanted carrier from the system.
    default: 0
    unit: integer
    foreach: [nodes, techs, timesteps]
    where: "force_async_flow==True"
    domain: integer
    bounds:
      min: 0
      max: 1

  available_flow_cap:
    title: Available carrier flow capacity
    description: >-
      Flow capacity that will be set to zero if the technology is not operating in a given
      timestep and will be set to the value of the decision variable `flow_cap` otherwise.
      This is useful when you want to set a minimum flow capacity for any technology investment, but also want to allow the model to decide the capacity.
      It is expected to only be used when `purchased_units_max == 1`,
      i.e., the `purchased_units` decision variable is binary.
      If `purchased_units_max > 1`, you may get strange results and should instead use the less flexible `flow_cap_per_unit`.
    default: 0
    unit: power
    foreach: [nodes, techs, carriers, timesteps]
    where: integer_dispatch==True AND flow_cap_max AND NOT flow_cap_per_unit
    bounds:
      min: 0
      max: .inf

  # Modified

  flow_cap:
    description: >-
      A technology's flow capacity, also known as its nominal or nameplate capacity.
      Minimum is set to 0 and handled in a distinct constraint to handle the integer purchase variable.
    bounds:
      min: 0

  area_use:
    description: >-
      The area in space utilised directly (e.g., solar PV panels)
      or indirectly (e.g., biofuel crops) by a technology.
      Minimum is set to 0 and handled in a distinct constraint to handle the integer purchase variable.
    bounds:
      min: 0

  source_cap:
    description: >-
      The upper limit on a flow that can be consumed from outside the system boundaries
      by a `supply` technology in each timestep.
      Minimum is set to 0 and handled in a distinct constraint to handle the integer purchase variable.
    bounds:
      min: 0

  storage_cap:
    description: >-
      The upper limit on a carrier that can be stored by a technology in any timestep.
      Minimum is set to 0 and handled in a distinct constraint to handle the integer purchase variable.
    bounds:
      min: 0


constraints:

  # MILP specific

  unit_commitment_milp:
    description: >-
      Set the upper bound of the number of integer units of technology that can exist,
      for any technology using integer units to define its capacity.
    foreach: [nodes, techs, timesteps]
    where: "operating_units AND purchased_units"
    equations:
      - expression: operating_units <= purchased_units

  flow_out_max_milp:
    description: >-
      Set the upper bound of a technology's ability to produce carriers,
      for any technology using integer units to define its capacity.
    foreach: [nodes, techs, carriers, timesteps]
    where: "flow_out AND operating_units AND flow_cap_per_unit"
    equations:
      - expression: flow_out <= operating_units * timestep_resolution * flow_cap_per_unit * flow_out_parasitic_eff

  flow_in_max_milp:
    description: >-
      Set the upper bound of a technology's ability to consume carriers,
      for any technology using integer units to define its capacity.
    foreach: [nodes, techs, carriers, timesteps]
    where: "flow_in AND operating_units AND flow_cap_per_unit"
    equations:
      - expression: flow_in <= operating_units * timestep_resolution * flow_cap_per_unit

  flow_out_min_milp:
    description: >-
      Set the lower bound of a technology's ability to produce carriers,
      for any technology using integer units to define its capacity.
    foreach: [nodes, techs, carriers, timesteps]
    where: "flow_out AND operating_units AND flow_out_min_relative"
    equations:
      - where: flow_cap_per_unit
        expression: flow_out >= operating_units * timestep_resolution * flow_cap_per_unit * flow_out_min_relative
      - where: available_flow_cap
        expression: flow_out >= available_flow_cap * timestep_resolution * flow_out_min_relative

  storage_capacity_units_milp:
    description: >-
      Fix the storage capacity of any technology using integer units to define its capacity.
    foreach: [nodes, techs]
    where: "storage AND purchased_units AND storage_cap_per_unit"
    equations:
      - expression: storage_cap == purchased_units * storage_cap_per_unit

  flow_capacity_units_milp:
    description: >-
      Fix the flow capacity of any technology using integer units to define its capacity.
    foreach: [nodes, techs, carriers]
    where: "purchased_units AND flow_cap_per_unit"
    equations:
      - expression: flow_cap == purchased_units * flow_cap_per_unit

  flow_capacity_max_purchase_milp:
    description: >-
      Set the upper bound on a technology's flow capacity,
      for any technology with integer capacity purchasing.
    foreach: [nodes, techs, carriers]
    where: "purchased_units"
    equations:
      - where: flow_cap_max
        expression: flow_cap <= flow_cap_max * purchased_units
      - where: NOT flow_cap_max
        expression: flow_cap <= bigM * purchased_units

  storage_capacity_max_purchase_milp:
    description: >-
      Set the upper bound on a technology's storage capacity,
      for any technology with integer capacity purchasing.
    foreach: [nodes, techs]
    where: "purchased_units AND storage_cap_max"
    equations:
      - expression: storage_cap <= storage_cap_max * purchased_units

  unit_capacity_max_systemwide_milp:
    description: >-
      Set the upper bound on the total number of units of a technology that can be purchased
      across all nodes where the technology can exist,
      for any technology using integer units to define its capacity.
    foreach: [techs]
    where: "purchased_units AND purchased_units_max_systemwide"
    equations:
      - expression: sum(purchased_units, over=nodes) <= purchased_units_max_systemwide

  unit_capacity_min_systemwide_milp:
    description: >-
      Set the lower bound on the total number of units of a technology that can be purchased
      across all nodes where the technology can exist,
      for any technology using integer units to define its capacity.
    foreach: [techs]
    where: "purchased_units AND purchased_units_max_systemwide"
    equations:
      - expression: sum(purchased_units, over=nodes) >= purchased_units_min_systemwide

  async_flow_in_milp:
    description: >-
      Set a technology's ability to have inflow in the same timestep that it has outflow,
      for any technology using the asynchronous flow binary switch.
    foreach: [nodes, techs, timesteps]
    where: "async_flow_switch"
    equations:
      - expression: sum(flow_in, over=carriers) <= (1 - async_flow_switch) * bigM

  async_flow_out_milp:
    description: >-
      Set a technology's ability to have outflow in the same timestep that it has inflow,
      for any technology using the asynchronous flow binary switch.
    foreach: [nodes, techs, timesteps]
    where: "async_flow_switch"
    equations:
      - expression: sum(flow_out, over=carriers) <= async_flow_switch * bigM

  available_flow_cap_continuous:
    description: >-
      Limit flow capacity to the value of the `flow_cap` decision variable when the
      technology is operating in a given timestep.
    foreach: [nodes, techs, carriers, timesteps]
    where: available_flow_cap
    equations:
      - expression: available_flow_cap <= flow_cap

  available_flow_cap_binary:
    description: Limit flow capacity to zero if the technology is not operating in a given timestep.
    foreach: [nodes, techs, carriers, timesteps]
    where: available_flow_cap
    equations:
      - expression: available_flow_cap <= flow_cap_max * operating_units

  available_flow_cap_max_binary_continuous_switch:
    description: >-
      Force flow capacity to equal the value of the `flow_cap` decision variable if the
      technology is operating in a given timestep, zero otherwise.
    foreach: [nodes, techs, carriers, timesteps]
    where: available_flow_cap
    equations:
      - expression: available_flow_cap >= flow_cap + ((operating_units - purchased_units) * flow_cap_max)

  flow_capacity_minimum:
    description: >-
      Set the lower bound on a technology's flow capacity,
      for any technology with a non-zero lower bound,
      with or without integer capacity purchasing.
    foreach: [nodes, techs, carriers]
    where: "flow_cap_min"
    equations:
      - where: NOT purchased_units
        expression: flow_cap >= flow_cap_min
      - where: purchased_units
        expression: flow_cap >= flow_cap_min * purchased_units

  storage_capacity_minimum:
    description: >-
      Set the lower bound on a technology's storage capacity
      for any technology with a non-zero lower bound,
      with or without integer capacity purchasing.
    foreach: [nodes, techs]
    where: "storage_cap_min"
    equations:
      - where: NOT purchased_units
        expression: storage_cap >= storage_cap_min
      - where: purchased_units
        expression: storage_cap >= storage_cap_min * purchased_units

  area_use_minimum:
    description: >-
      Set the lower bound on a technology's area use
      for any technology with a non-zero lower bound,
      with or without integer capacity purchasing.
    foreach: [nodes, techs]
    where: "area_use_min"
    equations:
      - where: NOT purchased_units
        expression: area_use >= area_use_min
      - where: purchased_units
        expression: area_use >= area_use_min * purchased_units

  source_capacity_minimum:
    description: >-
      Set the lower bound on a technology's source capacity
      for any supply technology with a non-zero lower bound,
      with or without integer capacity purchasing.
    foreach: [nodes, techs]
    where: "base_tech==supply AND source_cap_min"
    equations:
      - where: NOT purchased_units
        expression: source_cap >= source_cap_min
      - where: purchased_units
        expression: source_cap >= source_cap_min * purchased_units

  # Modified

  flow_out_max:
    description: >-
      Set the upper bound of a continuous technology's outflow.
    where: "carrier_out AND NOT operating_units"

  flow_out_min:
    description: >-
      Set the lower bound of a continuous technology's outflow.
    where: "flow_out_min_relative AND NOT operating_units"

  flow_in_max:
    description: >-
      Set the upper bound of a continuous technology's inflow.
    where: "carrier_in AND NOT operating_units"

global_expressions:

  # MILP specific

  cost_investment_purchase:
    title: Binary purchase investment costs
    description: >-
      The investment costs associated with the binary purchase of a technology.
    default: 0
    unit: cost
    foreach: [nodes, techs, costs]
    where: cost_purchase AND purchased_units
    order: -1
    equations:
      - where: base_tech==transmission
        expression: (cost_purchase + cost_purchase_per_distance * distance) * purchased_units * 0.5
      - where: "NOT base_tech==transmission"
        expression: cost_purchase * purchased_units

  # Modified

  cost_investment:
    title: Total investment costs
    description: >-
      The installation costs of a technology, including those linked to the nameplate capacity,
      land use, storage size, and binary/integer unit purchase.
    where: >-
      cost_investment_flow_cap OR cost_investment_storage_cap OR cost_investment_source_cap OR
      cost_investment_area_use OR cost_investment_purchase
    equations:
      - expression: >-
          sum(cost_investment_flow_cap, over=carriers) +
          cost_investment_storage_cap +
          cost_investment_source_cap +
          cost_investment_area_use +
          cost_investment_purchase
