Saving Simulation Results to a Database

There are a number of reasons why you might want to save simulation results to a database:

  • Observing changes in model output over the course of model development

  • Cacheing runs of a model to speed later analysis or display, especially in large models

  • Creating a traceable record of your work

It’s relatively easy to set up a sequel database and commit runs output to it. This demo uses sqlite, which creates a database in a local file.

import sqlite3
import numpy as np
import pysd

Ingredients

Model

We’ll use the simple teacup model for this experiment, and we’ll ask for the value at integer times from [0..29].

model = pysd.read_vensim('../../models/Teacup/Teacup.mdl')
tseries = range(30)

A database

In this example, we’ll create a database which will be saved in the working directory as example.db. We populate its columns with two columns for storing the parameter values that we’ll change from run to run, and then a column for each timestamp value we intend to save:

conn = sqlite3.connect('example.db')
c = conn.cursor()

# Create table
c.execute('''CREATE TABLE simulations
             (room_temperature real, init_teacup_temperature real,
              %s ) '''%', '.join(['t%i real'%i for i in tseries]));

####Parameters We want to save the output of our model when driven with a variety of parameters. For demonstration, we’ll set these randomly:

room_temps = np.random.normal(75, 5, 100)
init_tea_temps = np.random.normal(175, 15, 100)

The Recipe

We’re now ready to simulate our model with the various parameters. After execution, we construct a SQL insert querry containing each of the returned values, and commit it to the database.

for room_temp, init_tea_temp in zip(room_temps, init_tea_temps):
    output = model.run(params={'room_temperature':room_temp},
                       initial_condition=(0,{'teacup_temperature':init_tea_temp}),
                       return_timestamps=tseries)

    c.execute("INSERT INTO simulations VALUES (%i,%i,%s)"%
              (room_temp, init_tea_temp, ', '.join(output['Teacup Temperature'].apply(str))))

    conn.commit()

We can see that the result was added properly by fetching a record:

c.execute('SELECT * FROM simulations')
c.fetchone()
(73.0,
 161.0,
 161.3004136013599,
 152.93211154727877,
 145.3649295009812,
 138.52217409471785,
 132.33449402260226,
 126.73917716532998,
 121.67951500303411,
 117.10422787460355,
 112.96694525846864,
 109.22573580750158,
 105.84268237493694,
 102.78349772420083,
 100.01717702786915,
 97.51568363383308,
 95.25366491391246,
 93.2081953150439,
 91.35854400886798,
 89.68596478484497,
 88.17350605746613,
 86.80583906198456,
 85.56910249742967,
 84.45076204236265,
 83.43948331956555,
 82.52501702216168,
 81.69809503692045,
 80.95033651195727,
 80.27416291682422,
 79.66272123412638,
 79.10981450421062,
 78.60983901899793)

Finally, we must remember to close our connection to the database:

conn.close()
#remove the database file when we are finished with it.
!rm example.db