Creating User-defined Functions in SD Models

Creating user-defined functions in the SD DSL that is part of the BPTK-Py business simulation framework.

Creating User-defined Functions in SD Models

One of the benefits of creating System Dynamics models in Python is that we can use the full power of Python to create our own functions, which we can then use in our models.

This how to illustrates how to do this.

First of all, lets set up our model:

from BPTK_Py import Model
from BPTK_Py import sd_functions as sd
model = Model(starttime=1,stoptime=10,dt=0.25,name='TestModel')

Now let’s define a function we would like to use in our model. A user defined function can have as many arguments as you like, but it must accept at least a model and time parameter (you don’t need to use the parameters if you don’t want to).

How you define your function is up to you - you can use any of the methods available in Python, such as class methods, using def, or lambda functions.

The example below uses a lambda function which simply multiplies the current time t with 5.

my_model_function = model.function("my_model_function", lambda model, t: 5*t)

As you can see, much like with stocks and converters, we associate our function with the model by calling the models functionmethod.

Next we set up a converter:

converter = model.converter("converter")

The converters equation calls the model function.

converter.equation = my_model_function()

We can test the function as follows:

converter(5)
25.0

Let’s plot the function over time:

converter.plot()

We can also create a stock that has the converter as an inflow:

stock = model.stock("stock")
stock.equation = converter
stock.plot()

We can do all the usual arithmethic:

stock.equation=converter/(sd.time())
stock.plot()

The function we created above was just dependent on time and not on other model variables. Let’s create a function that takes more arguments, e.g. one that multiplies a model variable with time.

You can add as many arguments as you like, but they must come after the model and t arguments.

another_model_function = model.function("another_model_function", lambda model, t, input_function, multiplier : t*input_function*multiplier)

Define a new converter which will be the input for the function:

input_function = model.converter("input_function")
input_function.equation= 5.0
multiplier = model.converter("multiplier")
multiplier.equation=1.0

Now add a converter which will apply the another_model_function:

another_converter = model.converter("another_converter")
another_converter.equation=another_model_function(input_function, multiplier)
another_converter.plot()

from BPTK_Py.bptk import bptk
import numpy as np
bptk=bptk()

Of course functions defined in this way can also be used within scenarios. The quickest way to set up a scenario manager for a given model is as follows:

bptk.register_model(model)

This automatically creates a scenario manager whose name is the name of the model with the prefix “sm” and a base scenario. The models name is normalized to start with a capital letter (so TestModel is converted to smTestmodel).

It also creates a “base” scenario which runs the model as-is, without any changed settings.

You can list all scenarios and scenario managers using the list_scenarios method.

bptk.list_scenarios(scenario_managers=["smTestmodel"])

*** smTestmodel ***
     base

Let’s add some new scenarios:

bptk.register_scenarios(
    scenarios =
        {
            "multiplier5": {
                "constants": {
                    "multiplier": 5.0
                }
            },
            "multiplier10": {
                "constants": {
                    "multiplier": 10.0
                }
            },
            "multiplier15": {
                "constants": {
                    "multiplier": 15.0
                }
            }
        }
    ,
    scenario_manager="smTestmodel")
bptk.list_scenarios(scenario_managers=["smTestmodel"])

*** smTestmodel ***
     base
     multiplier5
     multiplier10
     multiplier15
bptk.plot_scenarios(scenario_managers=["smTestmodel"],scenarios=["base","multiplier5","multiplier10","multiplier15"],equations=["another_converter"])