Xmile Translation¶
PySD allows parsing a Xmile file and translates the result to an AbstractModel
object that can be used to build the model.
Warning
Currently no Xmile users are working on the development of PySD. This is causing a gap between the Xmile and Vensim developments. Stella users are encouraged to take part in the development of PySD by including new test models and adding support for new functions and features.
The translation workflow¶
The following translation workflow allows splitting the Xmile file while parsing each part of it to build an AbstractModel
type object. The workflow may be summarized as follows:
Xmile file: Parses the file with etree library and creates a section for the model.
Xmile section: Full set of variables and definitions that can be integrated. Allows splitting the model elements.
Xmile element: A variable definition. It includes units and comments. Allows parsing the expressions it contains and saving them inside AbstractComponents, that are part of an AbstractElement.
Once the model is parsed and split following the previous steps. The AbstractModel
can be returned.
Xmile file¶
The XmileFile class allows reading the original Xmile model file and parsing it into Section elements. The final result can be exported to an AbstractModel class in order to build a model in another programming language.
- class pysd.translators.xmile.xmile_file.XmileFile(xmile_path: str | Path)[source]¶
The XmileFile class allows parsing an Xmile file. When the object is created, the model file is automatically opened and parsed with lxml.etree.
- Parameters:
xmile_path (str or pathlib.Path) – Path to the Xmile model.
- property verbose¶
Print model information to standard output.
- parse(parse_all: bool = True) None [source]¶
Create a XmileSection object from the model content and parse it. As macros are currently not supported, all models will have a single section. This function should split the macros in independent sections in the future.
- Parameters:
parse_all (bool (optional)) – If True, the created XmileSection objects will be automatically parsed. Otherwise, these objects will only be added to self.sections but not parsed. Default is True.
- get_abstract_model() AbstractModel [source]¶
Get Abstract Model used for building. This, method should be called after parsing the model (self.parse). This automatically calls the get_abstract_section method from the model sections.
- Returns:
AbstractModel – Abstract Model object that can be used for building the model in another language.
- Return type:
Xmile section¶
The Section class allows parsing a model section into Elements. The final result can be exported to an AbstractSection class in order to build a model in other language. A section could be either the main model (without the macros), or a macro definition (not supported yet for Xmile).
- class pysd.translators.xmile.xmile_section.Section(name: str, path: Path, section_type: str, params: List[str], returns: List[str], content_root: _Element, namespace: str, split: bool, views_dict: dict | None)[source]¶
Section object allows parsing the elements of that section.
- Parameters:
name (str) – Section name. ‘__main__’ for the main section or the macro name.
path (pathlib.Path) – Section path. It should be the model name for main section and the clean macro name for a macro.
section_type (str ('main' or 'macro')) – The section type.
params (list) – List of params that takes the section. In the case of main section it will be an empty list.
returns (list) – List of variables that returns the section. In the case of main section it will be an empty list.
content_root (etree._Element) – Section parsed tree content.
namespace (str) – The namespace of the section given after parsing its content with etree.
split (bool) – If split is True the created section will split the variables depending on the views_dict.
views_dict (dict) – The dictionary of the views. Giving the variables classified at any level in order to split them by files.
- property verbose¶
Print section information to standard output.
- parse(parse_all: bool = True) None [source]¶
Parse section object. The subscripts of the section will be added to self subscripts. The variables defined as Flows, Auxiliary, Gf, and Stock will be converted in XmileElements. The control variables, if the section is __main__, will be converted to a ControlElement.
- Parameters:
parse_all (bool (optional)) – If True then the created VensimElement objects will be automatically parsed. Otherwise, this objects will only be added to self.elements but not parser. Default is True.
- get_abstract_section() AbstractSection [source]¶
Get Abstract Section used for building. This, method should be called after parsing the section (self.parse). This method is automatically called by Model’s get_abstract_model and automatically generates the AbstractSubscript ranges and merge the components in elements calling also the get_abstract_components method from each model component.
- Returns:
AbstractSection – Abstract Section object that can be used for building the model in another language.
- Return type:
Xmile element¶
The Element class child classes alow parsing the expressions of a given model element. There are four tipes of elements:
Auxiliars (Aux class): Auxiliary elements, defined with <aux>.
Flows (Flow class): Flow elements, defined with <flow>.
Gfs (Gf class): Lookup elements, defined with <gf>.
Stocks (Stock class): Data component, defined with <stock>
Moreover, a fith type element is defined ControlElement, which allows parsing the values of the model control variables (time step, initialtime, final time).
The final result from a parsed element can be exported to an AbstractElement object in order to build a model in other language.
- class pysd.translators.xmile.xmile_element.Element(node: _Element, ns: dict, subscripts)[source]¶
Element class. This class provides the shared methods for its children: Aux, Flow, Gf, Stock, and ControlElement.
- Parameters:
- property verbose¶
Print element information to standard output.
- class pysd.translators.xmile.xmile_element.Aux(node, ns, subscripts)[source]¶
Auxiliary variable defined by <aux> in Xmile.
- Parameters:
- get_abstract_element() AbstractElement [source]¶
Get Abstract Element used for building. This method is automatically called by Sections’s get_abstract_section.
- Returns:
AbstractElement – Abstract Element object that can be used for building the model in another language. It contains a list of AbstractComponents with the Abstract Syntax Tree of each of the expressions.
- Return type:
- class pysd.translators.xmile.xmile_element.Flow(node, ns, subscripts)[source]¶
Flow defined by <flow> in Xmile.
- class pysd.translators.xmile.xmile_element.Gf(node, ns, subscripts)[source]¶
Gf variable (lookup) defined by <gf> in Xmile.
- Parameters:
- get_abstract_element() AbstractElement [source]¶
Get Abstract Element used for building. This method is automatically called by Sections’s get_abstract_section.
- Returns:
AbstractElement – Abstract Element object that can be used for building the model in another language. It contains a list of AbstractComponents with the Abstract Syntax Tree of each of the expressions.
- Return type:
- class pysd.translators.xmile.xmile_element.Stock(node, ns, subscripts)[source]¶
Stock variable defined by <stock> in Xmile.
- Parameters:
- get_abstract_element() AbstractElement [source]¶
Get Abstract Element used for building. This method is automatically called by Sections’s get_abstract_section.
- Returns:
AbstractElement – Abstract Element object that can be used for building the model in another language. It contains a list of AbstractComponents with the Abstract Syntax Tree of each of the expressions.
- Return type:
- class pysd.translators.xmile.xmile_element.SubscriptRange(name: str, definition: List[str], mapping: List[str] = [])[source]¶
Subscript range definition.
- property verbose¶
Print subscript range information to standard output.
- get_abstract_subscript_range() AbstractSubscriptRange [source]¶
Get Abstract Subscript Range used for building. This method is automatically called by Sections’s get_abstract_section.
- Returns:
AbstractSubscriptRange – Abstract Subscript Range object that can be used for building the model in another language.
- Return type:
Supported Functions and Features¶
Ongoing development of the translator will support the full set of Xmile functionality. The current release supports the following operators, functions and features:
Warning
Not all the supported functions and features are properly tested. Any new test model to cover the missing functions test will be welcome.
Operators¶
All the basic operators are supported, this includes the ones shown in the tables below.:
Xmile |
Xmile example |
Abstract Syntax |
---|---|---|
- |
-A |
LogicStructure([‘negative’], (A,)) |
+ |
+A |
A |
not |
not A |
LogicStructure([‘:NOT:’], (A,)) |
Xmile |
Xmile example |
Abstract Syntax |
---|---|---|
^ |
A ^ B |
ArithmeticStructure([‘^’], (A, B)) |
* |
A * B |
ArithmeticStructure([‘*’], (A, B)) |
/ |
A / B |
ArithmeticStructure([‘/’], (A, B)) |
mod |
A mod B |
CallStructure(‘modulo’, (A, B)) |
+ |
A + B |
ArithmeticStructure([‘+’], (A, B)) |
- |
A - B |
ArithmeticStructure([‘-‘], (A, B)) |
= |
A = B |
LogicStructure([‘=’], (A, B)) |
<> |
A <> B |
LogicStructure([‘<>’], (A, B)) |
< |
A < B |
LogicStructure([‘<’], (A, B)) |
> |
A > B |
LogicStructure([‘>’], (A, B)) |
>= |
A >= B |
LogicStructure([‘>=’], (A, B)) |
<= |
A <= B |
LogicStructure([‘<=’], (A, B)) |
and |
A and B |
LogicStructure([‘:AND:’], (A, B)) |
or |
A or B |
LogicStructure([‘:OR:’], (A, B)) |
Functions¶
Not all the Xmile functions are included yet. The list of supported functions is shown below:
Xmile |
Xmile example |
Abstract Syntax |
Xmile comments |
---|---|---|---|
abs |
abs(A) |
CallStructure(‘abs’, (A,)) |
|
min |
min(A, B) |
CallStructure(‘min’, (A, B)) |
|
max |
max(A, B) |
CallStructure(‘max’, (A, B)) |
|
min |
min(A) |
CallStructure(‘vmin_xmile’, (A,)) |
|
max |
max(A) |
CallStructure(‘vmax_xmile’, (A,)) |
|
sqrt |
sqrt(A) |
CallStructure(‘sqrt’, (A,)) |
|
exp |
exp(A) |
CallStructure(‘exp’, (A,)) |
|
ln |
ln(A) |
CallStructure(‘ln’, (A,)) |
|
pi |
pi() |
CallStructure(‘pi’, (,)) |
|
sin |
sin(A) |
CallStructure(‘sin’, (A,)) |
|
cos |
cos(A) |
CallStructure(‘cos’, (A,)) |
|
tan |
tan(A) |
CallStructure(‘tan’, (A,)) |
|
arcsin |
arcsin(A) |
CallStructure(‘arcsin’, (A,)) |
|
arccos |
arccos(A) |
CallStructure(‘arccos’, (A,)) |
|
arctan |
arctan(A) |
CallStructure(‘arctan’, (A,)) |
|
int |
int(A) |
CallStructure(‘int’, (A,)) |
|
if_then_else |
if_then_else(A, B, C) |
CallStructure(‘if_then_else’, (A, B)) |
|
IF condition THEN value_true ELSE value_false |
IF A THEN B ELSE C |
CallStructure(‘if_then_else’, (A, B)) |
|
safediv |
safediv(A, B, X) |
CallStructure(‘xidz’, (A, B, X)) |
|
safediv |
safediv(A, B) |
CallStructure(‘zidz’, (A, B)) |
|
pulse |
pulse(magnitude, start) |
CallStructure(‘Xpulse’, (start, magnitude)) |
Not tested for Xmile! |
pulse |
pulse(magnitude, start, interval) |
CallStructure(‘Xpulse_train’, (start, interval, magnitude)) |
Not tested for Xmile! |
ramp |
ramp(slope, start_time, end_time) |
CallStructure(‘ramp’, (slope, start_time, end_time)) |
Not tested for Xmile! |
ramp |
ramp(slope, start_time) |
CallStructure(‘ramp’, (slope, start_time)) |
Not tested for Xmile! |
step |
step(height, step_time) |
CallStructure(‘step’, (height, step_time)) |
Not tested for Xmile! |
init |
init(value) |
InitialStructure(value) |
Xmile |
Xmile example |
Abstract Syntax |
Xmile comments |
---|---|---|---|
delay1 |
delay1(input, delay_time, initial_value) |
DelayStructure(input, delay_time, initial_value, 1) |
Not tested for Xmile! |
delay1 |
delay1(input, delay_time) |
DelayStructure(input, delay_time, input, 1) |
Not tested for Xmile! |
delay3 |
delay3(input, delay_time, initial_value) |
DelayStructure(input, delay_time, initial_value, 3) |
Not tested for Xmile! |
delay3 |
delay3(input, delay_time) |
DelayStructure(input, delay_time, input, 3) |
Not tested for Xmile! |
delayn |
delayn(input, delay_time, n, initial_value) |
DelayNStructure(input, delay_time, initial_value, n) |
Not tested for Xmile! |
delayn |
delayn(input, delay_time, n) |
DelayNStructure(input, delay_time, input, n) |
Not tested for Xmile! |
delay |
delay(input, delay_time, initial_value) |
DelayFixed(input, delay_time, initial_value) |
|
delay |
delay(input, delay_time) |
DelayFixed(input, delay_time, input) |
|
smth1 |
smth1(input, smth_time, initial_value) |
SmoothStructure(input, smth_time, initial_value, 1) |
Not tested for Xmile! |
smth1 |
smth1(input, smth_time) |
SmoothStructure(input, smth_time, input, 1) |
Not tested for Xmile! |
smth3 |
smth3(input, smth_time, initial_value) |
SmoothStructure(input, smth_time, initial_value, 3) |
Not tested for Xmile! |
smth3 |
smth3(input, smth_time) |
SmoothStructure(input, smth_time, input, 3) |
Not tested for Xmile! |
smthn |
smthn(input, smth_time, n, initial_value) |
SmoothNStructure(input, smth_time, initial_value, n) |
Not tested for Xmile! |
smthn |
smthn(input, smth_time, n) |
SmoothNStructure(input, smth_time, input, n) |
Not tested for Xmile! |
forcst |
forcst(input, average_time, horizon, initial_trend) |
ForecastStructure(input, average_time, horizon, initial_trend) |
Not tested for Xmile! |
forcst |
forcst(input, average_time, horizon) |
ForecastStructure(input, average_time, horizon, 0) |
Not tested for Xmile! |
trend |
trend(input, average_time, initial_trend) |
TrendStructure(input, average_time, initial_trend) |
Not tested for Xmile! |
trend |
trend(input, average_time) |
TrendStructure(input, average_time, 0) |
Not tested for Xmile! |
Stocks¶
Stocks are supported with any number of inflows and outflows. Stocks are translated to the AST as IntegStructure(flows, initial_value, non_negative). Non-negative flag is parsed for both stocks and flows, this can be set element by element or using the behavior section. Flows with non-negative flags are read as flows with a maximum condition, while for stocks this information is saved in the pysd.translators.structures.abstract_expressions.IntegStructure
object.
Subscripts¶
Several subscript related features are supported. These include:
Basic subscript operations with different ranges.
Subscript ranges and subranges definitions.
Graphical functions¶
Xmile graphical functions (gf), also known as lookups, are supported. They can be hardcoded or inlined.
Warning
Interpolation methods ‘extrapolate’ and ‘discrete’ are implemented but not tested. Full integration models with these methods are required.
Supported in Vensim but not in Xmile¶
Macro¶
Currently Xmile macros are not supported. In Vensim, macros are classified as an independent section of the model. If they are properly parsed in the XmileFile
, adding support for Xmile should be easy.
Planed New Functions and Features¶
Nothing yet.