> ## Documentation Index
> Fetch the complete documentation index at: https://docs.engrate.io/llms.txt
> Use this file to discover all available pages before exploring further.

# In Optimisation

> How calculation pipelines map to optimisation models

A calculation pipeline is a directed dataflow of functions, each producing
one output dataset from one or more inputs. The same definitions support
two execution modes.

**Calculation mode.** When all input datasets carry concrete values, each
function computes its output deterministically.

**Optimisation mode.** When one or more input datasets are declared as
decision variables, each function emits output variables and constraints
that link them to the inputs. Across the pipeline, these variables and
constraints aggregate into a model that an optimisation solver can evaluate
against an objective function.

## Example: peak power fee

Consider the [peak power fee pipeline](/api-reference/cost-of-energy/calculation-pipeline)
from the calculation reference. To optimise consumption against this tariff,
declare quarter-hourly energy offtake $E_t$ as the decision variable. Each
function emits its optimisation form:

**Aggregate to hourly energy** ($T_k$: the four quarter-hours in hour $k$):

$E^h_k = \sum_{t \in T_k} E_t \qquad \forall k$

**Divide by 1 hour** to get hourly power:

$P^h_k = E^h_k / 1\,\text{h}$

**Select the 3 highest hours per month** ($T_m$: the hours in month $m$).
Introduce a binary $\delta_{m,k} \in \lbrace 0,1 \rbrace$ and threshold $\theta_m$:

$\sum_{k \in T_m} \delta_{m,k} = 3, \quad \delta_{m,k} = 1 \;\Rightarrow\; P^h_k \geq \theta_m, \quad \delta_{m,k} = 0 \;\Rightarrow\; P^h_k \leq \theta_m$

**Aggregate(mean) over selected hours.** Average the 3 selected values:

$P^\text{peak}_m = \frac{1}{3} \sum_{\substack{k \in T_m \\ \delta_{m,k}=1}} P^h_k$

**Multiply by 50 SEK/kW** to get the monthly cost:

$C_m = 50 \cdot P^\text{peak}_m$

The optimiser minimises $\sum_m C_m$ against $E_t$, subject to whatever
consumption constraints the customer's assets impose.

### Alternative linear implementation

The select-3-highest formulation above introduces a binary $\delta_{m,k}$ per
hour per month, which turns the problem into a Mixed Integer Linear Programming model (MILP).
The solver must reason discretely about *which* 3 hours are selected.

If only the **cost** matters, which is typically the case, the same exact
monthly top-$3$ peak cost can be obtained from a purely linear formulation,
with no binaries.

For this we introduce positive *excess variables* $z_{m,k}$ above our monthly threshold:

$z_{m,k} \geq P^h_k - \theta_m \qquad \forall k \in T_m$

The monthly peak power is then simply calculated as:

$P^\text{peak}_m = \theta_m + \frac{1}{3} \sum_{k \in T_m} z_{m,k}$

(monthly peak = threshold + average excess of the 3 highest hourly power values above the threshold)

## Functions

<AccordionGroup>
  <Accordion title="Add">
    $x_{\text{out},t} = \sum_n x_{\text{in},n,t} \qquad \forall t$

    (where $n$ ranges over operand labels.)
  </Accordion>

  <Accordion title="Subtract">
    $x_{\text{out},t} = x_{\text{in},L,t} - x_{\text{in},R,t} \qquad \forall t$
  </Accordion>

  <Accordion title="Multiply">
    When at least one operand is fixed (a constant or known data, $c_t$):

    $x_{\text{out},t} = c_t \cdot x_{\text{in},t} \qquad \forall t$

    If both operands are decision variables, the relation is bilinear — most
    solvers cannot handle this directly. In tariffs this is essentially never
    the case: multiplies are quantity × rate, where the rate is a known `Level`
    or constant data.
  </Accordion>

  <Accordion title="Divide">
    When the denominator is fixed (a constant or known data, $c_t$):

    $x_{\text{out},t} = \frac{x_{\text{in},t}}{c_t} \qquad \forall t$

    If the denominator is a decision variable, the relation is bilinear — most
    solvers cannot handle this directly. In tariffs the denominator is almost
    always a known quantity (e.g. dividing energy by 1 hour to convert to power,
    or dividing by a fixed time-window length).
  </Accordion>

  <Accordion title="Aggregate">
    The form depends on `aggregation_function`. ($T_k$ denotes the set of
    fine-resolution timesteps in group $k$.)

    **Sum:**

    $x_{\text{out},k} = \sum_{t \in T_k} x_{\text{in},t} \qquad \forall k$

    **Mean:**

    $x_{\text{out},k} = \frac{1}{|T_k|} \sum_{t \in T_k} x_{\text{in},t} \qquad \forall k$

    **Max:**

    $x_{\text{out},k} \geq x_{\text{in},t} \qquad \forall t \in T_k, \forall k$

    When minimising a cost where $x_{\text{out},k}$ has a positive coefficient,
    this inequality alone is sufficient — the solver pushes $x_{\text{out},k}$
    down to $\max_t x_{\text{in},t}$. The non-convex case — when cost pressure
    doesn't push $x_{\text{out},k}$ toward $\max_t x_{\text{in},t}$ —
    introduces binary variables and indicator or big-M constraints.

    **Min:**

    $x_{\text{out},k} \leq x_{\text{in},t} \qquad \forall t \in T_k, \forall k$

    Mirror of max: the inequality suffices when the cost coefficient is
    negative (e.g. a credit being maximised); the non-convex case is handled
    the same way.
  </Accordion>

  <Accordion title="Resample">
    Broadcasting only: each fine-resolution slot takes the value of the
    coarse-resolution input for its group.

    $x_{\text{out},t} = x_{\text{in},k(t)} \qquad \forall t$

    where $k(t)$ is the coarse-resolution group containing $t$.
  </Accordion>

  <Accordion title="Select">
    The form depends on the condition.

    **Time-based conditions** (`month`, `day_of_week`, `time_of_day`,
    `exclude_holidays`, and their logical combinations): matching timesteps
    are known a priori from timestamps — the output references the input for
    matching timesteps, non-matching timesteps are absent.

    **`highest` / `lowest`:** discrete selection per group. Introduces a
    binary variable $\delta_{k,t} \in \lbrace 0,1 \rbrace$ per element and a continuous
    threshold $\theta_k$ per group:

    $\sum_{t \in T_k} \delta_{k,t} = n \qquad \forall k$

    For `highest`:

    $\delta_{k,t} = 1 \;\Rightarrow\; x_{\text{in},t} \geq \theta_k, \qquad \delta_{k,t} = 0 \;\Rightarrow\; x_{\text{in},t} \leq \theta_k$

    `lowest` mirrors with the inequalities flipped. The implications are
    modelled with indicator or big-M constraints.
  </Accordion>

  <Accordion title="Mask">
    Conditional substitution: matching timesteps are known a priori from
    timestamps — the output references the replacement where the condition
    matches, the input otherwise.
  </Accordion>

  <Accordion title="Clip">
    The form depends on which bounds are configured.

    **`minimum` only** (where $L_t$ is the configured minimum):

    $x_{\text{out},t} \geq x_{\text{in},t} \qquad x_{\text{out},t} \geq L_t$

    When minimising a cost where $x_{\text{out},t}$ has a positive coefficient,
    these inequalities alone are sufficient — the solver pushes
    $x_{\text{out},t}$ down to $\max(x_{\text{in},t}, L_t)$. The non-convex
    case introduces a binary variable per timestep and indicator or big-M
    constraints.

    **`maximum` only** (where $U_t$ is the configured maximum):

    $x_{\text{out},t} \leq x_{\text{in},t} \qquad x_{\text{out},t} \leq U_t$

    Mirror of `minimum` only: the inequalities suffice when the cost
    coefficient is negative; the non-convex case is handled the same way.

    **Both `minimum` and `maximum`:** introduces two binary variables per
    timestep (one per bound) and indicator or big-M constraints.
  </Accordion>

  <Accordion title="Lookup">
    Stepwise tier selection with $R$ tiers, boundaries $b_1, \ldots, b_{R-1}$
    (ascending), and level values $\ell_1, \ldots, \ell_R$. Introduces a
    binary variable $\delta_{r,t} \in \lbrace 0,1 \rbrace$ per timestep per tier,
    i.e. only 1 tier active at a time:

    $\sum_{r=1}^{R} \delta_{r,t} = 1 \qquad \forall t$

    $\delta_{r,t} = 1 \;\Rightarrow\; b_{r-1} \leq x_{\text{in},t} < b_r$

    The output is the active tier's level:

    $x_{\text{out},t} = \sum_{r=1}^{R} \delta_{r,t} \cdot \ell_r$
  </Accordion>
</AccordionGroup>
