GraphQL API

This section explains how to build Predicer input data using Hertta’s GraphQL API. Each subsection describes an object, its parameters, and provides examples.

TimeLineSettings

The timeline defines the resolution and length of your optimisation problem. It is the same for all data objects, so the number of values in every time series must match the number of timesteps.

Set the timeline with the updateTimeLine mutation.

Example (six-hour timeline, hourly steps):

updateTimeLine(
  timeLineInput: {
    duration: { hours: 6, minutes: 0, seconds: 0 }
    step: { hours: 1, minutes: 0, seconds: 0 }
  }
) {
  errors { field message }
}

For a six-hour horizon starting at 2025-04-20T00:00:00Z the resulting timestamps are:

2025-04-20T00:00:00Z
2025-04-20T01:00:00Z
2025-04-20T02:00:00Z
2025-04-20T03:00:00Z
2025-04-20T04:00:00Z
2025-04-20T05:00:00Z

InputDataSetup

Global settings that affect optimisation behaviour. Create or edit via createInputDataSetup / updateInputDataSetup.

Example (enable market bids and reserves):

createInputDataSetup(
  setupUpdate: {
    useMarketBids: true
    useReserves: true
    useReserveRealisation: false
    useNodeDummyVariables: true
    useRampDummyVariables: true
    commonTimesteps: 0
    commonScenarioName: null
    nodeDummyVariableCost: 1000000.0
    rampDummyVariableCost: 1000000.0
  }
) {
  errors { field message }
}

Scenarios

Scenarios represent different possible futures. Use createScenario to add them.

Example:

createScenario(name: "scenarioA", weight: 1.0) { message }
createScenario(name: "scenarioB", weight: 2.0) { message }

Nodes

Nodes are fundamental building blocks in Predicer. Use createNode. Nodes may represent commodities, markets, reserves or storages.

Example 1: constant cost, no inflow:

createNode(
  node: {
    name: "Node1"
    isCommodity: false
    isMarket: false
    isRes: false
    cost: [
      { scenario: null, constant: 24.0, series: null }
    ]
    inflow: []
  }
) {
  errors { field message }
}

Example 2: cost series for a scenario:

createNode(
  node: {
    name: "Node2"
    isCommodity: false
    isMarket: false
    isRes: false
    cost: [
      {
        scenario: "Scenario1"
        constant: null
        series: [10.0, 12.0, 14.0, 13.0, 11.0, 15.0]
      }
    ]
    inflow: []
  }
) {
  errors { field message }
}

Example 3: inflow from an electricity price forecast:

createNode(
  node: {
    name: "Node3"
    isCommodity: false
    isMarket: false
    isRes: false
    cost: []
    inflow: [
      {
        scenario: "Scenario1"
        constant: null
        series: null
        forecast: "ELERING"
        fType: "electricity"
      }
    ]
  }
) {
  errors { field message }
}

Node state (storage)

If a node has storage, set its state separately using setNodeState:

setNodeState(
  nodeName: "Node1"
  state: {
    inMax: 100.0
    outMax: 80.0
    stateLossProportional: 0.01
    stateMin: 20.0
    stateMax: 200.0
    initialState: 50.0
    isScenarioIndependent: true
    isTemp: false
    tEConversion: 1.0
    residualValue: 0.0
  }
) {
  errors { field message }
}

Processes

Processes convert or transfer commodities between nodes. Use createProcess with a NewProcess input.

Example:

createProcess(
  process: {
    name: "Process1"
    conversion: UNIT
    isCfFix: false
    isOnline: true
    isRes: false
    eff: 0.95
    loadMin: 0.3
    loadMax: 1.0
    startCost: 100.0
    minOnline: 2.0
    maxOnline: 6.0
    minOffline: 1.0
    maxOffline: 24.0
    initialState: false
    isScenarioIndependent: true
    cf: []
    effTs: []
  }
) {
  errors { field message }
}

Groups

Groups allow you to bundle nodes or processes.

Example:

# Create a process group
createProcessGroup(name: "g1") { message }

# Add a process to the group
addProcessToGroup(processName: "Process1", groupName: "g1") { message }

# Create a node group
createNodeGroup(name: "ng1") { message }

# Add a node to the group
addNodeToGroup(nodeName: "Node1", groupName: "ng1") { message }

Markets

Markets balance your model by allowing buying or selling of commodities. Markets can be of energy or reserve type.

Example (energy market):

createMarket(
  market: {
    name: "el_market"
    mType: ENERGY
    node: "GridNode"
    processGroup: ""
    direction: null
    reserveType: null
    isBid: true
    isLimited: false
    minBid: 0.0
    maxBid: 0.0
    fee: 0.0
    price: [
      { scenario: null, constant: 50.0, series: null, forecast: null, fType: null }
    ]
    upPrice: []
    downPrice: []
    realisation: []
    reserveActivationPrice: []
  }
) {
  errors { field message }
}

Connect market prices to an external forecast:

connectMarketPricesToForecast(
  marketName: "el_market",
  forecastName: "ENTSOE",
  forecastType: "electricity"
) { message }

Risk

Predicer models uncertainty by adding a Conditional Value at Risk (CVaR) term to the objective function.

Example:

createRisk(risk: { parameter: "alfa", value: 0.95 }) { errors { field message } }
createRisk(risk: { parameter: "beta", value: 0.1 }) { errors { field message } }

Inflow Blocks

Inflow blocks (or blocks) model flexibility actions like shifting inflow between timesteps. Each block has a binary variable and a series of constants defining how much inflow is shifted at each timestep. Blocks are linked to a specific node and scenario, and only one block may be active for a given node, time and scenario.

Example of a block b1 acting on node n1 in scenario s1 (tabular form):

t   b1,n1   b1,s1
1   20.4.2022 1:00   6
2   20.4.2022 2:00  -3
3   20.4.2022 3:00  -2
4   20.4.2022 4:00  -1

(The Hertta API does not currently support creating blocks via GraphQL.)

Node Diffusion

Node diffusion models energy flow between two storage nodes based on their state (e.g. heat transfer). The flow is E = k * (T1 - T2), where k is the diffusion coefficient.

Example:

createNodeDiffusion(
  newDiffusion: {
    fromNode: "Room1"
    toNode: "Room2"
    coefficient: [ { scenario: null, constant: 0.05, series: null } ]
  }
) { errors { field message } }

Node Delay

A node delay represents a one-way delay between two nodes (e.g. water travel time between reservoirs). Commodity and market nodes cannot participate in a delay.

Example:

createNodeDelay(
  delay: {
    fromNode: "R1"
    toNode: "R2"
    delay: 3.0
    minDelayFlow: -1e9
    maxDelayFlow: 1e9
  }
) { errors { field message } }

Node Histories

When modelling delays, the downstream node needs a history defining inflow for the first d timesteps.

Example:

createNodeHistory(nodeName: "R2") { errors { field message } }

addStepToNodeHistory(
  nodeName: "R2",
  step: {
    scenario: "Scenario1"
    durations: [ { hours: 1, minutes: 0, seconds: 0 }, { hours: 1, minutes: 0, seconds: 0 } ]
    values: [3.0, 4.0]
  }
) { errors { field message } }

General Constraints

General constraints restrict relationships between process flows, storage states or online variables. They can be rigid (strict equality/inequality) or setpoint constraints with a penalty for deviations.

Example (less-than constraint + flow factors):

createGenConstraint(
  constraint: {
    name: "gt_c1"
    gcType: LESS_THAN
    isSetpoint: false
    penalty: 0.0
    constant: [ { scenario: null, constant: 0.0, series: null } ]
  }
) { errors { field message } }

# Electricity flow factor
createFlowConFactor(
  factor: [ { scenario: null, constant: 3.0, series: null } ],
  constraintName: "gt_c1",
  processName: "gas_turb",
  sourceOrSinkNodeName: "elc"
)

# Heat flow factor
createFlowConFactor(
  factor: [ { scenario: null, constant: -1.0, series: null } ],
  constraintName: "gt_c1",
  processName: "gas_turb",
  sourceOrSinkNodeName: "heat"
)