Vensim Translation

PySD parses a vensim ‘.mdl’ file and translates the result into python, creating a new file in the same directory as the original. For example, the Vensim file Teacup.mdl becomes Teacup.py .

This allows model execution independent of the Vensim environment, which can be handy for deploying models as backends to other products, or for performing massively parallel distributed computation.

These translated model files are read by PySD, which provides methods for modifying or running the model and conveniently accessing simulation results.

Translated Functions

Ongoing development of the translator will support the full subset of Vensim functionality that has an equivalent in XMILE. The current release supports the following functionality:

Vensim Python Translation
ABS abs
INTEGER int
MIN min
MAX max
= ==
< <
> >
>= >=
<= <=
^ **
SQRT np.sqrt
EXP np.exp
LN np.log
PI np.pi
SIN np.sin
COS np.cos
TAN np.tan
ARCSIN np.arcsin
ARCCOS np.arccos
ARCTAN np.arctan
MODULO np.mod
ELMCOUNT len
IF THEN ELSE functions.if_then_else
PULSE TRAIN functions.pulse_train
RAMP functions.ramp
INVERT MATRIX functions.invert_matrix
VMIN functions.vmin
VMAX functions.vmax
SUM functions.sum
PROD functions.prod
LOGNORMAL np.random.lognormal
STEP functions.step
PULSE functions.pulse
EXPRND np.random.exponential
POISSON np.random.poisson
RANDOM NORMAL functions.bounded_normal
RANDOM UNIFORM np.random.rand
DELAY1 functions.Delay
DELAY3 functions.Delay
DELAY N functions.DelayN
DELAY FIXED functions.DelayFixed
FORECAST functions.Forecast
SAMPLE IF TRUE functions.SampleIfTrue
SMOOTH3 functions.Smooth
SMOOTH N functions.Smooth
SMOOTH functions.Smooth
INITIAL functions.Initial
XIDZ functions.XIDZ
ZIDZ functions.XIDZ
GET XLS DATA external.ExtData
GET DIRECT DATA external.ExtData
GET XLS LOOKUPS external.ExtLookup
GET DIRECT LOOKUPS external.ExtLookup
GET XLS CONSTANTS external.ExtConstant
GET DIRECT CONSTANTS external.ExtConstant
GET XLS SUBSCRIPT external.ExtSubscript
GET DIRECT SUBSCRIPT external.ExtSubscript
np corresponds to the numpy package

Additionally, identifiers are currently limited to alphanumeric characters and the dollar sign $.

Future releases will include support for:

  • subscripts
  • arrays
  • arbitrary identifiers

There are some constructs (such as tagging variables as ‘suplementary’) which are not currently parsed, and may throw an error. Future releases will handle this with more grace.

Used Functions for Translation

These functions translate vensim .mdl file to pieces needed by the builder module to write a python version of the model. Everything that requires knowledge of vensim syntax should be here.

pysd.py_backend.vensim.vensim2py._classify_elements_by_module(sketch, namespace, subview_sep)[source]

Takes the Vensim sketch as a string, parses it (line by line) and returns a dictionary containing the views/subviews as keys and the model elements that belong to each view/subview inside a list as values.

Parameters:
  • sketch (string) – Representation of the Vensim Sketch as a string.
  • namespace (dict) – Translation from original model element names (keys) to python safe function identifiers (values).
  • subview_sep (list) – Characters used to split view names into view + subview (e.g. if a view is named ENERGY.Demand and suview_sep is set to “.”, then the Demand subview would be placed inside the ENERGY directory)
Returns:

views_dict – Dictionary containing view names as keys and a list of the corresponding variables as values. If the subview_sep is defined, then the dictionary will have a nested dict containing the subviews.

Return type:

dict

pysd.py_backend.vensim.vensim2py._include_common_grammar(source_grammar)[source]
pysd.py_backend.vensim.vensim2py._split_sketch(text)[source]

Splits the model file between the main section and the sketch

Parameters:text (string) – Full model as a string.
Returns:
  • text (string) – Model file without sketch.
  • sketch (string) – Model sketch.
pysd.py_backend.vensim.vensim2py.get_equation_components(equation_str, root_path=None)[source]

Breaks down a string representing only the equation part of a model element. Recognizes the various types of model elements that may exist, and identifies them.

Parameters:
  • equation_str (basestring) – the first section in each model element - the full equation.
  • root_path (basestring) – the root path of the vensim file (necessary to resolve external data file paths)
Returns:

  • Returns a dictionary containing the following
  • real_name (basestring) – The name of the element as given in the original vensim file
  • subs (list of strings) – list of subscripts or subscript elements
  • expr (basestring)
  • kind (basestring) – What type of equation have we found? - component - normal model expression or constant - lookup - a lookup table - subdef - a subscript definition - data - a data variable
  • keyword (basestring or None)

Examples

>>> get_equation_components(r'constant = 25')
{'expr': '25', 'kind': 'component', 'subs': [], 'real_name': 'constant'}

Notes

in this function we don’t create python identifiers, we use real names. This is so that when everything comes back together, we can manage any potential namespace conflicts properly

pysd.py_backend.vensim.vensim2py.get_file_sections(file_str)[source]

This is where we separate out the macros from the rest of the model file. Working based upon documentation at: https://www.vensim.com/documentation/index.html?macros.htm

Macros will probably wind up in their own python modules eventually.

Parameters:file_str
Returns:entries – Each dictionary represents a different section of the model file, either a macro, or the main body of the model file. The dictionaries contain various elements: - returns: list of strings
represents what is returned from a macro (for macros) or empty for main model
  • params: list of strings
    represents what is passed into a macro (for macros) or empty for main model
  • name: string
    the name of the macro, or ‘main’ for main body of model
  • string: string
    string representing the model section
Return type:list of dictionaries

Examples

>>> get_file_sections(r'a~b~c| d~e~f| g~h~i|')
[{'returns': [], 'params': [], 'name': 'main', 'string': 'a~b~c| d~e~f| g~h~i|'}]
pysd.py_backend.vensim.vensim2py.get_model_elements(model_str)[source]

Takes in a string representing model text and splits it into elements

All newline characters were alreeady removed in a previous step.

Parameters:model_str (string) –
Returns:entries – Each dictionary contains the components of a different model element, separated into the equation, units, and docstring.
Return type:array of dictionaries

Examples

# Basic Parsing: >>> get_model_elements(r’a~b~c| d~e~f| g~h~i|’) [{‘doc’: ‘c’, ‘unit’: ‘b’, ‘eqn’: ‘a’}, {‘doc’: ‘f’, ‘unit’: ‘e’, ‘eqn’: ‘d’}, {‘doc’: ‘i’, ‘unit’: ‘h’, ‘eqn’: ‘g’}]

# Special characters are escaped within double-quotes: >>> get_model_elements(r’a~b~c| d~e”~”~f| g~h~i|’) [{‘doc’: ‘c’, ‘unit’: ‘b’, ‘eqn’: ‘a’}, {‘doc’: ‘f’, ‘unit’: ‘e”~”’, ‘eqn’: ‘d’}, {‘doc’: ‘i’, ‘unit’: ‘h’, ‘eqn’: ‘g’}] >>> get_model_elements(r’a~b~c| d~e~”|”f| g~h~i|’) [{‘doc’: ‘c’, ‘unit’: ‘b’, ‘eqn’: ‘a’}, {‘doc’: ‘”|”f’, ‘unit’: ‘e’, ‘eqn’: ‘d’}, {‘doc’: ‘i’, ‘unit’: ‘h’, ‘eqn’: ‘g’}]

# Double-quotes within escape groups are themselves escaped with # backslashes: >>> get_model_elements(r’a~b~c| d~e””~”~f| g~h~i|’) [{‘doc’: ‘c’, ‘unit’: ‘b’, ‘eqn’: ‘a’}, {‘doc’: ‘f’, ‘unit’: ‘e”"~”’, ‘eqn’: ‘d’}, {‘doc’: ‘i’, ‘unit’: ‘h’, ‘eqn’: ‘g’}] >>> get_model_elements(r’a~b~c| d~e~””|”f| g~h~i|’) [{‘doc’: ‘c’, ‘unit’: ‘b’, ‘eqn’: ‘a’}, {‘doc’: ‘”"|”f’, ‘unit’: ‘e’, ‘eqn’: ‘d’}, {‘doc’: ‘i’, ‘unit’: ‘h’, ‘eqn’: ‘g’}] >>> get_model_elements(r’a~b~c| d~e”xnx”~f| g~h~|’) [{‘doc’: ‘c’, ‘unit’: ‘b’, ‘eqn’: ‘a’}, {‘doc’: ‘f’, ‘unit’: ‘e”x\nx”’, ‘eqn’: ‘d’}, {‘doc’: ‘’, ‘unit’: ‘h’, ‘eqn’: ‘g’}]

# Todo: Handle model-level or section-level documentation >>> get_model_elements(r’*** .model doc ***~ Docstring!| d~e~f| g~h~i|’) [{‘doc’: ‘Docstring!’, ‘unit’: ‘’, ‘eqn’: ‘’}, {‘doc’: ‘f’, ‘unit’: ‘e’, ‘eqn’: ‘d’}, {‘doc’: ‘i’, ‘unit’: ‘h’, ‘eqn’: ‘g’}]

# Handle control sections, returning appropriate docstring pieces >>> get_model_elements(r’a~b~c| ****.Control***~ Simulation Control Parameters | g~h~i|’) [{‘doc’: ‘c’, ‘unit’: ‘b’, ‘eqn’: ‘a’}, {‘doc’: ‘i’, ‘unit’: ‘h’, ‘eqn’: ‘g’}]

# Handle the model display elements (ignore them) >>> get_model_elements(r’a~b~c| d~e~f| -–///junk|junk~junk’) [{‘doc’: ‘c’, ‘unit’: ‘b’, ‘eqn’: ‘a’}, {‘doc’: ‘f’, ‘unit’: ‘e’, ‘eqn’: ‘d’}]

Notes

  • Tildes and pipes are not allowed in element docstrings, but we should

still handle them there

pysd.py_backend.vensim.vensim2py.parse_general_expression(element, namespace={}, subscript_dict={}, macro_list=None, elements_subs_dict={}, subs_compatibility={})[source]

Parses a normal expression # its annoying that we have to construct and compile the grammar every # time…

Parameters:
  • element (dictionary) –
  • namespace (dictionary) –
  • subscript_dict (dictionary) –
  • macro_list (list of dictionaries) – [{‘name’: ‘M’, ‘py_name’:’m’, ‘filename’:’path/to/file’, ‘args’: [‘arg1’, ‘arg2’]}]
  • elements_subs_dict (dictionary) – The dictionary with element python names as keys and their merged subscripts as values.
  • subs_compatibility (dictionary) – The dictionary storing the mapped subscripts
Returns:

  • translation
  • new_elements (list of dictionaries) – If the expression contains builder functions, those builders will create new elements to add to our running list (that will eventually be output to a file) such as stock initialization and derivative funcs, etc.

Examples

>>> parse_general_expression({'expr': 'INTEG (FlowA, -10)',
...                           'py_name':'test_stock',
...                           'subs':None},
...                          {'FlowA': 'flowa'}),
({'kind': 'component', 'py_expr': "_state['test_stock']"},
 [{'kind': 'implicit',
   'subs': None,
   'doc': 'Provides initial conditions for test_stock function',
   'py_name': 'init_test_stock',
   'real_name': None,
   'unit': 'See docs for test_stock',
   'py_expr': '-10'},
  {'py_name': 'dtest_stock_dt',
   'kind': 'implicit',
   'py_expr': 'flowa',
   'real_name': None}])
pysd.py_backend.vensim.vensim2py.parse_lookup_expression(element, subscript_dict)[source]

This syntax parses lookups that are defined with their own element

pysd.py_backend.vensim.vensim2py.parse_sketch_line(sketch_line, namespace)[source]

This syntax parses a single line of the Vensim sketch at a time.

Not all possibilities can be tested, so this gammar may be considered experimental for now

pysd.py_backend.vensim.vensim2py.parse_units(units_str)[source]

Extract and parse the units Extract the bounds over which the expression is assumed to apply.

Parameters:units_str

Examples

>>> parse_units('Widgets/Month [-10,10,1]')
('Widgets/Month', (-10,10,1))
>>> parse_units('Month [0,?]')
('Month', [-10, None])
>>> parse_units('Widgets [0,100]')
('Widgets', (0, 100))
>>> parse_units('Widgets')
('Widgets', (None, None))
>>> parse_units('[0, 100]')
('', (0, 100))
pysd.py_backend.vensim.vensim2py.translate_section(section, macro_list, sketch, root_path, subview_sep='')[source]
pysd.py_backend.vensim.vensim2py.translate_vensim(mdl_file, split_views, **kwargs)[source]

Translate a vensim file.

Parameters:
  • mdl_file (str) – File path of a vensim model file to translate to python.
  • split_views (bool) – If True, the sketch is parsed to detect model elements in each model view, and then translate each view in a separate python file. Setting this argument to True is recommended for large models that are split in many different views.
  • **kwargs ((optional)) – Additional parameters passed to the translate_vensim function
Returns:

outfile_name – Name of the output file.

Return type:

str

Examples

>>> translate_vensim('../tests/test-models/tests/subscript_3d_arrays/test_subscript_3d_arrays.mdl')