Inter-cluster storage#
A patch of Calliope in fragments. Calliope's storage_inter_cluster.yaml: with clustered days, a store carries a fill between the days of the whole time series and swings within its clustered day. A patch is not a spec, so it prints as the declarations it writes, in the spec it lands on.
variants/storage_inter_cluster.yaml
dimensions:
clusters:
description: Calliope's `clusters` — the representative days a clustered time series is made of
dtype: int
datesteps:
description: Calliope's `datesteps` — the days of the whole time series, in order
dtype: datetime
relations:
timestep_cluster:
description: "`timestep_cluster` — the cluster a time step belongs to"
key: timesteps
values: clusters
lookup_datestep_cluster:
description: "`lookup_datestep_cluster` — the cluster a day stands for"
key: datesteps
values: clusters
lookup_datestep_last_cluster_timestep:
description: "`lookup_datestep_last_cluster_timestep` — the last time step of the cluster a day stands for"
key: datesteps
values: timesteps
parameters:
storage_loss:
description: >-
`storage_loss` — the share of what a store holds that it loses in an
hour. Between days it is raised to 24, so it does not vary over time
steps here
dims: [nodes, techs]
variables:
storage:
description: >-
`storage` — what a store holds within a clustered day, relative to what
it carries between days. It may go below zero, as long as the sum does
not
bounds: { lower: null }
storage_inter_cluster:
description: "`storage_inter_cluster` — what a store carries from one day of the whole time series to the next"
dims: [nodes, techs, datesteps]
where: include_storage OR base_tech == 'storage'
bounds: { lower: 0 }
absence: zero
storage_intra_cluster_max:
description: "`storage_intra_cluster_max` — the most a store holds within a clustered day"
dims: [nodes, techs, clusters]
where: include_storage OR base_tech == 'storage'
storage_intra_cluster_min:
description: "`storage_intra_cluster_min` — the least a store holds within a clustered day"
dims: [nodes, techs, clusters]
where: include_storage OR base_tech == 'storage'
expressions:
storage_previous_step:
description: >-
`$storage_previous_step` under inter-cluster storage — what a store
carries into a time step: its initial fill at the first step of a store
that is not cyclic, nothing at the first step of a clustered day, and
what is left of the step before everywhere else
cases:
initial:
when: position(timesteps) == 0 AND NOT cyclic_storage
expression: storage_initial * storage_cap
cluster_start:
when: lookup_cluster_last_timestep AND NOT (position(timesteps) == 0 AND NOT cyclic_storage)
expression: "0"
storage_inter_previous_step:
description: >-
`$storage_previous_step` of `balance_storage_inter` — what a store
carries into a day: its initial fill on the first day of a store that
is not cyclic, and what is left of the day before everywhere else.
Calliope reads the initial fill as a share, not times the capacity, as
here
dims: [nodes, techs, datesteps]
cases:
initial:
when: position(datesteps) == 0 AND NOT cyclic_storage
expression: storage_initial
otherwise: (1 - storage_loss) ** 24 * shift(storage_inter_cluster, along=datesteps, offset=1, edge='wrap')
storage_intra:
description: >-
`$storage_intra` of `balance_storage_inter` — what the clustered day of
the day before left at its last step, and nothing on the first day of
a store that is not cyclic
dims: [nodes, techs, datesteps]
cases:
initial:
when: position(datesteps) == 0 AND NOT cyclic_storage
expression: "0"
otherwise: >-
shift(at(storage, by=lookup_datestep_last_cluster_timestep, over=timesteps, into=datesteps),
along=datesteps, offset=1, edge='wrap')
constraints:
storage_max: null
set_storage_initial:
description: >-
`set_storage_initial` under inter-cluster storage — a cyclic store with
an initial fill carries it between days at the end, after a day's loss
dims: [nodes, techs, datesteps]
where: position(datesteps) == -1 AND storage_inter_cluster AND storage_initial AND cyclic_storage
expression: storage_inter_cluster * (1 - storage_loss) ** 24 == storage_initial * storage_cap
storage_intra_max:
description: "`storage_intra_max` — a store holds at most its most within its clustered day"
dims: [nodes, techs, timesteps]
where: include_storage OR base_tech == 'storage'
expression: storage <= at(storage_intra_cluster_max, by=timestep_cluster, over=clusters, into=timesteps)
storage_intra_min:
description: "`storage_intra_min` — a store holds at least its least within its clustered day"
dims: [nodes, techs, timesteps]
where: include_storage OR base_tech == 'storage'
expression: storage >= at(storage_intra_cluster_min, by=timestep_cluster, over=clusters, into=timesteps)
storage_inter_max:
description: "`storage_inter_max` — what a store carries between days plus the most of its day is at most its capacity"
dims: [nodes, techs, datesteps]
where: include_storage OR base_tech == 'storage'
expression: >-
storage_inter_cluster + at(storage_intra_cluster_max, by=lookup_datestep_cluster, over=clusters, into=datesteps)
<= storage_cap
storage_inter_min:
description: "`storage_inter_min` — what a store carries between days, after a day's loss, plus the least of its day is not below zero"
dims: [nodes, techs, datesteps]
where: include_storage OR base_tech == 'storage'
expression: >-
storage_inter_cluster * (1 - storage_loss) ** 24
+ at(storage_intra_cluster_min, by=lookup_datestep_cluster, over=clusters, into=datesteps) >= 0
balance_storage_inter:
description: >-
`balance_storage_inter` — what a store carries into a day is what it
carried into the day before, after a day's loss, plus what that day's
cluster left
dims: [nodes, techs, datesteps]
where: include_storage OR base_tech == 'storage'
expression: storage_inter_cluster == storage_inter_previous_step + storage_intra
assumptions:
cyclic_storage_needs_inter_cluster: null
storage
\[
\mathit{storage}_{n,i,t} \in \mathbb{R} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ t \in \mathcal{T} \,:\, \mathrm{include\_storage}_{n,i} \vee \mathrm{base\_tech}_{i} = \text{'}\mathrm{storage}\text{'}
\]
storage_inter_cluster
\[
\mathit{storage}^{\mathrm{inter,cluster}}_{n,i,d} \ge 0 \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ d \in \mathcal{D} \,:\, \mathrm{include\_storage}_{n,i} \vee \mathrm{base\_tech}_{i} = \text{'}\mathrm{storage}\text{'}
\]
storage_intra_cluster_max
\[
\mathit{storage}^{\mathrm{intra,cluster,max}}_{n,i,l} \in \mathbb{R} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ l \in \mathcal{L} \,:\, \mathrm{include\_storage}_{n,i} \vee \mathrm{base\_tech}_{i} = \text{'}\mathrm{storage}\text{'}
\]
storage_intra_cluster_min
\[
\mathit{storage}^{\mathrm{intra,cluster,min}}_{n,i,l} \in \mathbb{R} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ l \in \mathcal{L} \,:\, \mathrm{include\_storage}_{n,i} \vee \mathrm{base\_tech}_{i} = \text{'}\mathrm{storage}\text{'}
\]
storage_previous_step
\[
\mathit{storage}^{\mathrm{previous,step}}_{n,i,t} = \begin{cases} \mathrm{storage}^{\mathrm{initial}}_{n,i} \cdot \mathit{storage}^{\mathrm{cap}}_{n,i} & \text{if } \mathrm{pos}(t) = 0 \wedge \neg \mathrm{cyclic\_storage}_{n,i} \\ 0 & \text{if } \mathrm{lookup\_cluster\_last\_timestep}(t) \text{ is defined} \wedge \neg \left( \mathrm{pos}(t) = 0 \wedge \neg \mathrm{cyclic\_storage}_{n,i} \right) \\ \left( 1 - \mathrm{storage}^{\mathrm{loss}}_{n,i} \right)^{\mathrm{timestep\_resolution}_{t \ominus 1}} \cdot \mathit{storage}_{n,i,t \ominus 1} & \text{otherwise} \end{cases} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ t \in \mathcal{T}
\]
storage_inter_previous_step
\[
\mathit{storage}^{\mathrm{inter,previous,step}}_{n,i,d} = \begin{cases} \mathrm{storage}^{\mathrm{initial}}_{n,i} & \text{if } \mathrm{pos}(d) = 0 \wedge \neg \mathrm{cyclic\_storage}_{n,i} \\ \left( 1 - \mathrm{storage}^{\mathrm{loss}}_{n,i} \right)^{24} \cdot \mathit{storage}^{\mathrm{inter,cluster}}_{n,i,d \ominus 1} & \text{otherwise} \end{cases} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ d \in \mathcal{D}
\]
storage_intra
\[
\mathit{storage}^{\mathrm{intra}}_{n,i,d} = \begin{cases} 0 & \text{if } \mathrm{pos}(d) = 0 \wedge \neg \mathrm{cyclic\_storage}_{n,i} \\ \mathit{storage}_{n,i,\mathrm{lookup\_datestep\_last\_cluster\_timestep}(d \ominus 1)} & \text{otherwise} \end{cases} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ d \in \mathcal{D}
\]
set_storage_initial
\[
\mathit{storage}^{\mathrm{inter,cluster}}_{n,i,d} \cdot \left( 1 - \mathrm{storage}^{\mathrm{loss}}_{n,i} \right)^{24} = \mathrm{storage}^{\mathrm{initial}}_{n,i} \cdot \mathit{storage}^{\mathrm{cap}}_{n,i} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ d \in \mathcal{D} \,:\, \mathrm{pos}(d) = \lvert \mathcal{D} \rvert - 1 \wedge \mathit{storage}^{\mathrm{inter,cluster}}_{n,i,d} \text{ exists} \wedge \mathrm{storage}^{\mathrm{initial}}_{n,i} \text{ is defined} \wedge \mathrm{cyclic\_storage}_{n,i}
\]
storage_intra_max
\[
\mathit{storage}_{n,i,t} \le \mathit{storage}^{\mathrm{intra,cluster,max}}_{n,i,\mathrm{timestep\_cluster}(t)} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ t \in \mathcal{T} \,:\, \mathrm{include\_storage}_{n,i} \vee \mathrm{base\_tech}_{i} = \text{'}\mathrm{storage}\text{'}
\]
storage_intra_min
\[
\mathit{storage}_{n,i,t} \ge \mathit{storage}^{\mathrm{intra,cluster,min}}_{n,i,\mathrm{timestep\_cluster}(t)} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ t \in \mathcal{T} \,:\, \mathrm{include\_storage}_{n,i} \vee \mathrm{base\_tech}_{i} = \text{'}\mathrm{storage}\text{'}
\]
storage_inter_max
\[
\mathit{storage}^{\mathrm{inter,cluster}}_{n,i,d} + \mathit{storage}^{\mathrm{intra,cluster,max}}_{n,i,\mathrm{lookup\_datestep\_cluster}(d)} \le \mathit{storage}^{\mathrm{cap}}_{n,i} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ d \in \mathcal{D} \,:\, \mathrm{include\_storage}_{n,i} \vee \mathrm{base\_tech}_{i} = \text{'}\mathrm{storage}\text{'}
\]
storage_inter_min
\[
\mathit{storage}^{\mathrm{inter,cluster}}_{n,i,d} \cdot \left( 1 - \mathrm{storage}^{\mathrm{loss}}_{n,i} \right)^{24} + \mathit{storage}^{\mathrm{intra,cluster,min}}_{n,i,\mathrm{lookup\_datestep\_cluster}(d)} \ge 0 \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ d \in \mathcal{D} \,:\, \mathrm{include\_storage}_{n,i} \vee \mathrm{base\_tech}_{i} = \text{'}\mathrm{storage}\text{'}
\]
balance_storage_inter
\[
\mathit{storage}^{\mathrm{inter,cluster}}_{n,i,d} = \mathit{storage}^{\mathrm{inter,previous,step}}_{n,i,d} + \mathit{storage}^{\mathrm{intra}}_{n,i,d} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ d \in \mathcal{D} \,:\, \mathrm{include\_storage}_{n,i} \vee \mathrm{base\_tech}_{i} = \text{'}\mathrm{storage}\text{'}
\]
Removed: storage_max, cyclic_storage_needs_inter_cluster.