Built from Other Functions
system dynamics, xmile, stella, sd dsl, bptk, bptk-py, python, self, clocktime, endval, runcount
Built from Other Functions
A few XMILE builtins have no function of their own in the SD DSL, because what they do is already there in another form: a function used in a particular way, or a few lines of Python around the model. Each builtin is shown with its XMILE equation first, then the SD DSL code. The code uses this model:
from BPTK_Py import Model
from BPTK_Py import sd_functions as sd
model = Model(starttime=0.0, stoptime=10.0, dt=0.5,
name="built_from_others")
demand = model.converter("demand")
demand.equation = 100.0 + 10.0 * sd.time()SELF
SELF stands for the variable in whose equation it appears. It is used to read the variable’s own past. In the SD DSL the variable is available by name, so it is passed directly:
XMILE
growth = PREVIOUS(SELF, 1) * 2
SD DSL
growth = model.converter("growth")
growth.equation = sd.previous(model, growth, 1.0) * 2.0CLOCKTIME
CLOCKTIME is the wall-clock time in seconds, not the model’s time. In the SD DSL a user-defined function reads it. Such a function runs on the Rust engine too: the engine calls back into Python for it.
XMILE
started_at = CLOCKTIME
SD DSL
import time
clocktime = model.function("clocktime",
lambda model, t: float(int(time.time())))
started_at = model.converter("started_at")
started_at.equation = clocktime()ENDVAL
ENDVAL is a variable’s value at the end of the previous run, and its initial value in the first one. In the SD DSL, Python starts each run, so Python also carries the value from one run to the next: read the value at the stop time and set it as a constant for the next run. Setting an equation clears the values the model has computed, so the next read runs the model again.
XMILE
last_end = ENDVAL(demand, 0)
SD DSL
last_end = model.constant("last_end")
last_end.equation = 0.0 # the first run
end_of_run = demand(model.stoptime)
last_end.equation = end_of_run # the next runThe same works across scenarios: the end value of one scenario becomes a constant of the next one.
RUNCOUNT and SENSIRUNCOUNT
RUNCOUNT is the number of the current run in a session, SENSIRUNCOUNT the number of the current run in a sensitivity analysis. In the SD DSL, Python counts the runs and sets the count as a constant before each run:
XMILE
run_number = RUNCOUNT
SD DSL
run_number = model.constant("run_number")
ends = []
for count in range(1, 4):
run_number.equation = float(count)
ends.append(demand(model.stoptime) * run_number(model.stoptime))A sensitivity analysis is the same loop, run over the values of a parameter instead of a count.