Development guide

The code lives on GitHub at calliope-project/calliope.

Development takes place in the master branch. Stable versions are tagged off of master with semantic versioning.

Tests are included and can be run with py.test from the project’s root directory.

Installing a development version

With pip:

$ pip install -e git+

Or, for a more easily modifiable local installation, first clone the repository to a location of your choosing, and then install via pip:

$ git clone
$ pip install -e ./calliope

Creating modular extensions

Constraint generator functions

By making use of the ability to load custom constraint generator functions (see Loading optional constraints), a Calliope model can be extended by additional constraints easily without modifying the core code.

Constraint generator functions are called during construction of the model with the Model object passed as the only parameter.

The Model object provides, amongst other things:

  • The Pyomo model instance, under the property m
  • The model data under the data property
  • An easy way to access model configuration with the get_option() method

A constraint generator function can add constraints, parameters, and variables directly to the Pyomo model instance (Model.m). Refer to the Pyomo documentation for information on how to construct these model components.

The default cost-minimizing objective function provides a good example:

import pyomo.core as po

def objective_cost_minimization(model):
    Minimizes total system monetary cost. Used as a default if
    a model does not specify another objective.

    m = model.m

    def obj_rule(m):
        return sum(model.get_option(y + '.weight') *
                   sum(m.cost[y, x, 'monetary'] for x in m.x)
                   for y in m.y)

    m.obj = po.Objective(sense=po.minimize, rule=obj_rule)
    m.obj.domain = po.NonNegativeReals

See the source code of the ramping_rate() function for a more elaborate example.

Time mask functions

Like custom constraint generator functions, custom time mask functions can be loaded dynamically during model initialization. By default, Calliope first checks whether a given time mask function string refers to a function inside an importable module, and if not, attempts to load the function from the calliope.time_masks module:

   resolution: 24
      # Loaded from built-in masks in calliope.time_masks
      - function: mask_extreme_week
        options: {tech: demand_power, what: min}

      # Loaded from a dynamically loaded custom module
      - function: my_custom_module.my_custom_mask
        options: {...}

See calliope.time_masks for examples of time mask functions.

Previous: The built-in example model | Next: API Documentation