XMILE in the SD DSL

For every XMILE builtin, the SD DSL function that expresses it, the engines that run it and whether the XMILE transpiler compiles it.
Keywords

system dynamics, xmile, smile, stella, sd dsl, bptk, bptk-py, python, builtins, transpiler

XMILE in the SD DSL

A model written in XMILE - in Stella, say - reaches BPTK-Py in one of two ways. The transpiler turns the file into a Python class, as XMILE Step by Step shows. Or you write the model again in the SD DSL, by hand. This chapter puts the two side by side: for every XMILE builtin, what the SD DSL calls it, which engine runs it and whether the transpiler compiles it.

  • Builtins Side by Side: every builtin as an XMILE equation and the SD DSL call that computes the same
  • Built from Other Functions: SELF, CLOCKTIME, ENDVAL, RUNCOUNT and SENSIRUNCOUNT, which the SD DSL writes with what it has
  • Model Structure: stocks, flows, conveyors, graphical functions, arrays and run specs in XML and in Python

Every builtin at a glance

sd is BPTK_Py.sd_functions; element.arr_... is a method of an arrayed element. Every SD DSL function runs on the Python engine. A random function with a seed does not run on the Rust engine yet, whatever the table says about the function itself. Queues and ovens are not listed: neither the SD DSL nor the transpiler has them.

XMILE SD DSL Rust engine Transpiler
ABS sd.abs x x
AGECOUNT sd.agecount - -
AND sd.And x x
ARCCOS sd.arccos x x
ARCSIN sd.arcsin x x
ARCTAN sd.arctan x x
BETA sd.beta x x
BINOMIAL sd.binomial x x
COMBINATIONS sd.combinations x partly
COS sd.cos x x
CGROWTH sd.cgrowth x x
CLOCKTIME a user-defined function x x
COSWAVE sd.coswave x x
COUNTER sd.counter x partly
DELAY sd.delay x x
DELAY1 sd.delay1 x x
DELAY3 sd.delay3 x x
DELAYN sd.delayn x x
DERIVN sd.derivn x partly
DT sd.dt x x
ELSE sd.If x x
EXP sd.exp x x
EXPRND sd.exprnd x x
ENDVAL a second scenario x x
FACTORIAL sd.factorial x x
FORCST sd.forecast x partly
FV sd.fv x x
GAMMA sd.gamma x x
GAMMALN sd.gammaln x x
GEOMETRIC sd.geometric x x
HISTORY sd.history x x
IF sd.If x x
INF sd.Inf x x
INTERPOLATE sd.interpolate - x
INIT sd.init x x
INT sd.floor x x
INVNORM sd.invnorm x x
IRR sd.irr - partly
LOG10 sd.log10 x x
LOGISTIC sd.logistic x x
LOGNORMAL sd.lognormal x x
LOOKUP sd.lookup x x
LOOKUPAREA sd.lookuparea - x
LOOKUPINV sd.lookupinv - x
LN sd.ln x x
MAX sd.max x x
MEAN element.arr_mean x x
MIN sd.min x x
MOD % x x
MONTECARLO sd.montecarlo x x
NAN sd.nan x x
NEGBINOMIAL sd.negbinomial x x
NORMAL sd.normal x x
NORMALCDF sd.normalcdf x x
NOT sd.Not x x
NPV sd.npv x x
OR sd.Or x x
PARETO sd.pareto x x
PERCENT sd.percent x x
PERMUTATIONS sd.permutations x x
PI sd.pi x x
PMT sd.pmt x x
POISSON sd.poisson x x
PREVIOUS sd.previous x x
PULSE sd.pulse x partly
PV sd.pv x x
PROD element.arr_prod x x
RANDOM sd.random x x
RANK sd.rank - x
RAMP sd.ramp x x
REWORK - - -
ROUND sd.round x x
ROOTN sd.rootn x x
RUNCOUNT a constant set per run x -
SAFEDIV sd.safediv x x
SELF sd.previous on the element itself x x
SENSIRUNCOUNT a constant set per run x -
SIN sd.sin x x
SINWAVE sd.sinwave x x
SIZE element.arr_size x x
SMTH1 sd.smooth x partly
SMTH3 sd.smooth3 x x
SMTHN sd.smoothn x x
SQRT sd.sqrt x x
STARTTIME sd.starttime x x
STDDEV element.arr_stddev x x
STEP sd.step x x
STOPTIME sd.stoptime x x
SUM element.arr_sum x x
TAN sd.tan x x
THEN sd.If x x
TIME sd.time x x
TREND sd.trend x partly
TRIANGULAR sd.triangular x x
UNIFORM sd.uniform x x
WEIBULL sd.weibull x x

Where the transpiler differs

Eight builtins marked partly above compile, but not in every form. Each case is checked against the SD DSL with the rows of Builtins Side by Side:

  • SMTH1 and TREND fail to compile with a number as an argument, or without their optional third one; FORCST fails to compile in every form.
  • TREND with an initial trend reads it as the starting value of the internal average.
  • COUNTER counts from TIME rather than from STARTTIME, so it is right only in a model that starts at the counter’s start value; with a start of 0 it fails to compile.
  • DERIVN takes only a variable, not an expression; COMBINATIONS takes no number with a decimal point; IRR fails to compile.
  • PULSE without a first pulse time pulses at every DT rather than once at the start.

What the transpiler does not compile

  • Discrete elements: ovens, queues and conveyors. The SD DSL has conveyors - see Conveyor - though not leak zones, capacity, inflow limits, arrest or discrete conveyors.
  • Non-negative stocks only approximately: the stock is held at zero, but its outflows are not reduced, so they keep draining what the stock no longer has. The SD DSL has them as Stella does - see Stock.non_negative.
  • Subranges of arrays and their notations (N1:N2, @, the transpose operator '). The SD DSL has ranges and transposition - see Parts of an array - but not @: its arrays have labels, not named dimensions.
  • The optional arguments of the random functions: the transpiled functions take only the mandatory ones - usually mean, scale or standard deviation - as given in the Stella documentation. A seed is taken, but it resets Python’s generator at the start of the run, so the seed of one function moves the others too; in the SD DSL each function with a seed draws a sequence of its own - see Seeds. Neither draws Stella’s numbers: Stella’s random number generator is not open source.