Skip to content

A curve that is not convex#

The same dispatch model, with a curve that bends both ways. Nothing about the objective now keeps the weights on one segment, so the method builds the restriction out of binaries. adjacency is the default, and this is what it costs.

Compare the math with the convex page. A second variable appears, cost_curve_seg, one binary per segment. Two rows come with it: cost_curve_pick picks exactly one segment, and cost_curve_adjacency holds each weight under the segments it borders. The link rows and the convexity row are unchanged.

description: >-
  The same least-cost dispatch as `piecewise.yaml`, with a cost curve that is
  not convex. The weights need binaries to hold them on one segment, which is
  what the default method builds.

dimensions:
  snapshot:
    description: dispatch periods
    dtype: int
  generator:
    description: dispatchable units
    dtype: str
  bp:
    description: breakpoints of the cost curve
    dtype: int

parameters:
  capacity:
    description: maximum dispatch
    dims: [generator]
  load:
    description: demand to be met
    dims: [snapshot]
  bp_x:
    description: breakpoint dispatch levels, one curve per generator
    dims: [generator, bp]
  bp_y:
    description: cost at each breakpoint, one curve per generator
    dims: [generator, bp]

variables:
  dispatch:
    description: dispatched power
    dims: [snapshot, generator]
    bounds:
      lower: 0
      upper: capacity
  op_cost:
    description: operating cost, piecewise-linear in dispatch
    dims: [snapshot, generator]
    bounds:
      lower: 0

piecewise:
  cost_curve:
    description: >-
      cost read off the generator's curve. The curve bends both ways, so
      nothing but the restriction keeps the weights on one segment: a binary
      per segment picks the one they may sit on
    along: bp
    dims: [snapshot, generator]
    links:
      dispatch: [dispatch, bp_x]
      op_cost: [op_cost, bp_y]
    method: adjacency

constraints:
  balance:
    dims: [snapshot]
    expression: sum(dispatch, over=generator) == load

objective:
  sense: minimize
  description: total operating cost, taken off the curves rather than from a marginal rate
  expression: sum(op_cost)

The same least-cost dispatch as piecewise.yaml, with a cost curve that is not convex. The weights need binaries to hold them on one segment, which is what the default method builds.

Sets#

Symbol Meaning
\(\mathcal{T}\) index \(t\) — snapshot — dispatch periods
\(\mathcal{G}\) index \(g\) — generator — dispatchable units
\(\mathcal{B}\) index \(b\) — bp — breakpoints of the cost curve

Parameters#

Symbol Meaning
\(\mathrm{capacity}\) capacity over \(\mathcal{G}\) — maximum dispatch
\(\mathrm{load}\) load over \(\mathcal{T}\) — demand to be met
\(\mathrm{x}\) bp_x over \(\mathcal{G} \times \mathcal{B}\) — breakpoint dispatch levels, one curve per generator
\(\mathrm{y}\) bp_y over \(\mathcal{G} \times \mathcal{B}\) — cost at each breakpoint, one curve per generator

Variables#

Symbol Meaning
\(\mathit{dispatch}\) dispatch over \(\mathcal{T} \times \mathcal{G}\) — dispatched power
\(\mathit{op\_cost}\) op_cost over \(\mathcal{T} \times \mathcal{G}\) — operating cost, piecewise-linear in dispatch

Upright is what the data supplies — a parameter such as \(\mathrm{capacity}\), a coordinate map, a label — and italic is what the solver chooses, such as \(\mathit{dispatch}\). An index is italic too, being what a quantifier chooses, and a set is script.

Objective#

\[ \min \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} \mathit{op\_cost}_{t,g} \]

Subject to#

balance

\[ \sum_{g \in \mathcal{G}} \mathit{dispatch}_{t,g} = \mathrm{load}_{t} \qquad \forall\, t \in \mathcal{T} \]

cost_curve

\[ \left( \mathit{dispatch}_{t,g},\ \mathit{op\_cost}_{t,g} \right) \in \mathrm{pwl}_{b \in \mathcal{B}}(\mathrm{x}_{g,b},\ \mathrm{y}_{g,b}) \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Variable domains#

dispatch

\[ 0 \le \mathit{dispatch}_{t,g} \le \mathrm{capacity}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

op_cost

\[ \mathit{op\_cost}_{t,g} \ge 0 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Assumptions#

cost_curve_complete

\[ \mathrm{x}_{g,b} \text{ is defined} \wedge \mathrm{y}_{g,b} \text{ is defined} \qquad \forall\, g \in \mathcal{G},\ b \in \mathcal{B} \]