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 is shown here after its XMILE equation. The code runs on 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 is the variable whose equation it appears in. Its use is to read the variable’s own past, and in the SD DSL the variable itself is at hand for that:
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. A user-defined function reads it; it runs on the Rust engine too, which 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 a run is something Python starts, so the value is carried from one run to the next by Python: read it at the stop time, and set it as a constant for the next run. Setting an equation clears what the model has computed, so the next read runs 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.
RUNCOUNT and SENSIRUNCOUNT
RUNCOUNT counts the runs of a session, SENSIRUNCOUNT the runs of a sensitivity analysis. In the SD DSL Python does the counting, and the model sees it as a constant set 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 over a parameter instead of a count.
REWORK
Not supported, by the SD DSL or by the transpiler.