Skip to content

Flow-based market coupling#

This file states PyPSA's flow-based domains. It is one of the 25 whole specs of one topic each (fragments) that merge composes into the spec of examples/pypsa.yaml. It adds a term to the sum Bus_injection. FlowBased_net_position is what a zone bus exports to its domain. GlobalConstraint_flow_based_ub holds each critical network element to its remaining available margin, and FlowBased_balance sums each domain's net positions to zero through a sum by a relation.

given:
  parameters:
    GlobalConstraint_type: { dims: [global_constraint], dtype: str }
    GlobalConstraint_sense: { dims: [scenario, global_constraint], dtype: str }
    GlobalConstraint_counts_snapshot: { dims: [scenario, global_constraint, snapshot], dtype: bool }
  variables:
    Link_p: { dims: [scenario, snapshot, link] }
  expressions:
    Bus_injection: { dims: [bus, scenario, snapshot] }

dimensions:
  scenario:
    description: the futures dispatch is chosen in, each with a weight
  snapshot:
    description: dispatch periods
    dtype: datetime
    ordered: true
  bus:
    description: network nodes
  link:
    description: controllable connections, each from one bus to the buses it delivers to
  global_constraint:
    description: PyPSA's `GlobalConstraint` rows, one label per declared limit
  flow_based_domain:
    description: >-
      the flow-based domains, each with its own zero-sum balance of net
      positions (PyPSA's `domain` of a `flow_based` row), data prep. A run whose
      flow-based rows name no domain has one. A run without them has none

relations:
  Bus_flow_based_domain:
    description: >-
      the flow-based domain a zone bus belongs to: the domain whose rows set
      the bus's `ptdf_<bus>` column, data prep. A bus that is no zone has no
      row
    key: bus
    values: flow_based_domain
    missing: absent

parameters:
  GlobalConstraint_ram:
    description: >-
      the remaining available margin of a `flow_based` row, one critical
      network element (CNEC). It may differ by snapshot. No value drops the
      row in that snapshot (`flow_based.py:201`)
    dims: [scenario, snapshot, global_constraint]
    missing: absent
  GlobalConstraint_zonal_ptdf:
    description: >-
      the zonal power transfer distribution factor of a `flow_based` row for a
      zone bus: PyPSA's `ptdf_<bus>` column read long, data prep. It may differ
      by snapshot. No value is zero, as PyPSA fills it
    dims: [scenario, snapshot, global_constraint, bus]
    missing: 0
  GlobalConstraint_link_ptdf:
    description: >-
      the factor of a `flow_based` row for a corridor link, whose flow enters
      the row: PyPSA's `ptdf_<link>` column read long, data prep. No value is
      zero
    dims: [scenario, snapshot, global_constraint, link]
    missing: 0
  Bus_flow_based_zone:
    description: >-
      whether a bus is a zone of a flow-based domain: some `flow_based` row
      has its `ptdf_<bus>` column, data prep
    dims: [bus]
    dtype: bool

variables:
  FlowBased_net_position:
    description: >-
      `FlowBased-net_position` — what a zone bus exports to the flow-based
      domain, unbounded. It leaves the zone's nodal balance
      (`flow_based.py:207-234`). A bus that is no zone has none
    dims: [scenario, snapshot, bus]
    where: Bus_flow_based_zone
    missing: 0

expressions:
  FlowBased_injection:
    description: what a zone bus exports to its flow-based domain leaves the bus (`constraints.py:1578`)
    dims: [scenario, snapshot, bus]
    expression: -FlowBased_net_position
    adds_to: Bus_injection

constraints:
  GlobalConstraint_flow_based_ub:
    description: >-
      `flow_based` — a CNEC's flow, the zones' net positions and the corridor
      links' flows each times its factor, is at most its remaining available
      margin. PyPSA names the rows `GlobalConstraint-ram`, one per CNEC and
      snapshot, and reads no `constant` (`flow_based.py:262-293`)
    dims: [scenario, snapshot, global_constraint]
    where: >-
      GlobalConstraint_type == 'flow_based' AND GlobalConstraint_sense == '<='
      AND GlobalConstraint_ram AND GlobalConstraint_counts_snapshot
    expression: >-
      sum(GlobalConstraint_zonal_ptdf * FlowBased_net_position, over=bus)
      + sum(GlobalConstraint_link_ptdf * Link_p, over=link)
      <= GlobalConstraint_ram
  FlowBased_balance:
    description: "`FlowBased-balance` — the net positions of each domain's zones sum to zero"
    dims: [scenario, snapshot, flow_based_domain]
    expression: sum(FlowBased_net_position, over=bus, by=Bus_flow_based_domain[flow_based_domain]) == 0

assumptions:
  GlobalConstraint_flow_based_sense_is_le:
    holds: "GlobalConstraint_sense == '<='"
    where: "GlobalConstraint_type == 'flow_based'"
    description: >-
      PyPSA refuses a `flow_based` row whose sense is not `<=`
      (`flow_based.py:109-114`)

Given#

Symbol Meaning
\(\mathrm{in}\) GlobalConstraint_counts_snapshot over \(\mathcal{G} \times \Xi \times \mathcal{T}\), data another file declares
\(\mathrm{sense}\) GlobalConstraint_sense over \(\mathcal{G} \times \Xi\), data another file declares
\(\mathrm{type}\) GlobalConstraint_type over \(\mathcal{G}\), data another file declares
\(f\) Link_p over \(\mathcal{L} \times \Xi \times \mathcal{T}\)
\(\mathit{Bus\_injection}\) Bus_injection over \(\mathcal{N} \times \Xi \times \mathcal{T}\), an expression this file adds FlowBased_injection to

Sets#

Symbol Meaning
\(\mathcal{N}\) index \(n\) — bus with \(\mathrm{Bus\_flow\_based\_domain}: \mathcal{N} \to \mathcal{L}\), Bus_flow_based_domain is absent where the data has no row — network nodes
\(\mathcal{L}\) index \(l\) — flow_based_domain with \(\mathrm{Bus\_flow\_based\_domain}: \mathcal{N} \to \mathcal{L}\), Bus_flow_based_domain is absent where the data has no row — the flow-based domains, each with its own zero-sum balance of net positions (PyPSA's domain of a flow_based row), data prep. A run whose flow-based rows name no domain has one. A run without them has none
\(\mathcal{G}\) index \(g\) — global_constraint — PyPSA's GlobalConstraint rows, one label per declared limit
\(\mathcal{L}\) index \(l\) — link — controllable connections, each from one bus to the buses it delivers to
\(\Xi\) index \(\xi\) — scenario — the futures dispatch is chosen in, each with a weight
\(\mathcal{T}\) index \(t\) — snapshot — dispatch periods

Parameters#

Symbol Meaning
\(\mathrm{zone}\) Bus_flow_based_zone over \(\mathcal{N}\) — whether a bus is a zone of a flow-based domain: some flow_based row has its ptdf_<bus> column, data prep
\(\mathrm{PTDF}^{f}\) GlobalConstraint_link_ptdf over \(\mathcal{G} \times \mathcal{L} \times \Xi \times \mathcal{T}\), \(0\) where the data has no row — the factor of a flow_based row for a corridor link, whose flow enters the row: PyPSA's ptdf_<link> column read long, data prep. No value is zero
\(\mathrm{RAM}\) GlobalConstraint_ram over \(\mathcal{G} \times \Xi \times \mathcal{T}\), absent where the data has no row — the remaining available margin of a flow_based row, one critical network element (CNEC). It may differ by snapshot. No value drops the row in that snapshot (flow_based.py:201)
\(\mathrm{PTDF}^{z}\) GlobalConstraint_zonal_ptdf over \(\mathcal{N} \times \mathcal{G} \times \Xi \times \mathcal{T}\), \(0\) where the data has no row — the zonal power transfer distribution factor of a flow_based row for a zone bus: PyPSA's ptdf_<bus> column read long, data prep. It may differ by snapshot. No value is zero, as PyPSA fills it

Variables#

Symbol Meaning
\(\mathit{NP}\) FlowBased_net_position over \(\mathcal{N} \times \Xi \times \mathcal{T}\), \(0\) where the mask leaves it out — FlowBased-net_position — what a zone bus exports to the flow-based domain, unbounded. It leaves the zone's nodal balance (flow_based.py:207-234). A bus that is no zone has none

Definitions#

Symbol Meaning
\(\mathit{NP}^{\mathrm{inj}}\) FlowBased_injection over \(\mathcal{N} \times \Xi \times \mathcal{T}\) — what a zone bus exports to its flow-based domain leaves the bus (constraints.py:1578)

Variable domains#

FlowBased_net_position

\[ \mathit{NP}_{n,\xi,t} \in \mathbb{R} \qquad \forall\, n \in \mathcal{N},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{zone}_{n} \]

Definitions#

FlowBased_injection

\[ \mathit{NP}^{\mathrm{inj}}_{n,\xi,t} = -\mathit{NP}_{n,\xi,t} \qquad \forall\, n \in \mathcal{N},\ \xi \in \Xi,\ t \in \mathcal{T} \]

Subject to#

FlowBased_balance

\[ \sum_{n \in \mathcal{N} \,:\, \mathrm{Bus\_flow\_based\_domain}(n) = l} \mathit{NP}_{n,\xi,t} = 0 \qquad \forall\, l \in \mathcal{L},\ \xi \in \Xi,\ t \in \mathcal{T} \]

GlobalConstraint_flow_based_ub

\[ \sum_{n \in \mathcal{N}} \mathrm{PTDF}^{z}_{n,g,\xi,t} \cdot \mathit{NP}_{n,\xi,t} + \sum_{l \in \mathcal{L}} \mathrm{PTDF}^{f}_{g,l,\xi,t} \cdot f_{l,\xi,t} \le \mathrm{RAM}_{g,\xi,t} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{type}_{g} = \text{'}\mathrm{flow\_based}\text{'} \wedge \mathrm{sense}_{g,\xi} = \text{'}\mathrm{<=}\text{'} \wedge \mathrm{RAM}_{g,\xi,t} \text{ is defined} \wedge \mathrm{in}_{g,\xi,t} \]

Assumptions#

GlobalConstraint_flow_based_sense_is_le

\[ \mathrm{sense}_{g,\xi} = \text{'}\mathrm{<=}\text{'} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi \,:\, \mathrm{type}_{g} = \text{'}\mathrm{flow\_based}\text{'} \]

See also