A minimal example

The simplest example has two nodes: an Input() which adds flow to the network and an Output() which removes flow from the network.

from pywr.core import Model, Input, Output

# create a model (including an empty network)
model = Model()

# create two nodes: a supply, and a demand
supply = Input(model, name='supply')
demand = Output(model, name='demand')

# create a connection from the supply to the demand

While technically valid, this model isn’t very interesting because we haven’t set any constraints or costs on flows in the network.

Let’s add some flow constraints to the problem:

# set maximum flows
supply.max_flow = 10.0
demand.max_flow = 6.0

The default minimum flow for a BaseNode() is zero, so we don’t need to set it explicitly.

The model still doesn’t do anything as it’s missing costs for flow through the nodes. If the cost of supply is less than the benefit received from satisfying demand, flow in the network will occur (within the models constraints).

# set cost (+ve) or benefit (-ve)
supply.cost = 3.0
demand.cost = -100.0

Next we need to tell the model how long to run for. As an example, we’ll use a daily timestep for all of 2015.

import datetime
from pywr.core import Timestepper

model.timestepper = Timestepper(
    pandas.to_datetime('2015-01-01'),  # first day
    pandas.to_datetime('2015-12-31'),  # last day
    datetime.timedelta(1)  # interval

In order to capture the output from the model we need to use a recorder, such as the pywr.recorders.NumpyArrayNodeRecorder().

from pywr.recorders import NumpyArrayNodeRecorder

recorder = NumpyArrayNodeRecorder(model, supply)

Finally we are ready to run our model:

# lets get this party started!

We can check the result for the first timestep by accessing the recorder’s data property:

scenario = 0
timestep = 0
print(recorder.data[scenario][timestep])  # prints 6.0

The result of this example model is trivial: the supply exceeds the demand, so the maximum flow at the demand is the limiting factor.

The model defined about could equally have been defined in a JSON document:

    "metadata": {
        "title": "tutorial",
        "minimum_version": "0.1"
    "timestepper": {
        "start": "2015-01-01",
        "end": "2015-12-31",
        "timestep": 1
    "nodes": [
            "name": "supply",
            "type": "Input",
            "max_flow": 10,
            "cost": 3
            "name": "demand",
            "type": "Output",
            "max_flow": 6,
            "cost": -100
    "edges": [
        ["supply", "demand"]
    "recorders": {
        "recorder": {
            "type": "NumpyArrayNodeRecorder",
            "node": "supply"


The model can be loaded using the Model.load method:

# load the model
model = Model.load("tutorial.json")
# run, forest, run!

See further information in on JSON see the JSON model format section.