This package contains classes that call Python functions. The classes can be used to send data to Python functions, and to obtain data from Python functions. This allows for example to use Python to communicate with web services, with hardware, or to do other computations inside a Python module.
The code has been tested with Python 3.6 on Linux 64 bit and Windows 64 bit.
To use classes from this package, a Python 3.6 runtime
environment must be installed. Also, the system environment
variable PYTHONPATH
may need to be set in order for
Python to find the modules that contain the functions. These
modules are stored in the directory
Buildings/Resources/Python-Sources
. In addition, an
environment variable (LD_LIBRARY_PATH
on Linux and
PATH
on Windows) may need to be set in order for a
simulation environment to find the dynamically linked
libraries.
The table below gives hints if there are problems running models that use Python code.
System | Settings |
---|---|
Linux 64 bit |
If the examples do not translate or simulate, enter on a console the commands export PYTHONPATH=${PYTHONPATH}:"Path_To_Buildings_Library"/Resources/Python-Sources export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:"Path_To_Buildings_Library"/Resources/Library/linux64 and restart the Modelica environment. If this does not help, make sure whereis libpython3.6m.so On Ubuntu 18.04, this library can be installed with sudo apt-get install libpython3.6-dev |
Dymola |
Because the Python libraries link to compiled C code, Dymola needs to be configured to generate code for 64-bit. This can be done by entering on the Dymola command line the assignment Advanced.CompileWith64=2 |
Two different types of Python functions are supported: Functions that do not need to pass Python objects between one invocation to another, and functions that need to pass a Python object from one invocation to another. For the first case, a Python function may be
def returnTwiceTheInput(xR): return 2.*xR
For the second case, a Python function may be
def incrementAndReturnACounter(i, obj): if obj == None: # Initialize the Python object obj = {'counter': i} else: # Use the python object obj['counter'] = obj['counter'] + i return [i, obj]
For the first case, set in the function Buildings.Utilities.IO.Python36.Functions.exchange
the input argument passPythonObject = false
, and for
the second case, set passPythonObject = true
. The
second case allows for example to build up a Python data structure
(or to instantiate a Python object), and do computations on this
object at each function invocation. For example, a Model Predictive
Control algorithm or a machine learning algorithm, implemented in
Python, could be fed with data at each time step. It could then
store this data and use the current and its historical data to feed
its algorithm. Based on this algorithm, it could output a control
signal for use in another Modelica model.
The parameters nDblWri
(or nIntWri
or
nStrWri
) and nDblRea
(or
nIntRea
) declare how many double (integer or string)
values should be written to, or read from, the Python function.
These values can be zero, in which case the Python function
receives no arguments for this data type, or it must return a list
with zero elements. However, because Modelica does not allow arrays
with zero elements, the arrays dblWri
and
dblRea
, respectively, must contain exactly one element
if nDblWri=0
or nDblRea=0
. In this
situation, dblWri
is a dummy argument that will not be
written to Python, and dblRea
contains a number that
must not be used in any model.
The arguments of the python functions are, in this order,
floats, integers and strings (and the Python object if
passPythonObject = true
). If there is only one element
of each data type, then a single value is passed. If there are
multiple elements of each data type, then they are stored in a
list. If there is no value of a data type (such as if
nDblWri=0
), then the argument is not present. Thus, if
a data type is not present, then the function will not
receive an empty list of this data type. If there are no arguments
at all, then the function takes no arguments (except if
passPythonObject = true
, in which case the only
argument is the Python object).
The table below shows the list of arguments for various combinations where no, one or two double values, integers and strings are passed as an argument to a Python function.
passPythonObject = false
:nDblWri | nIntWri | nStrWri | Arguments |
---|---|---|---|
1 | 0 | 0 | 1. |
0 | 1 | 1 | 1, "a" |
2 | 0 | 2 | [1.0, 2.0], ["a", "b"] |
1 | 1 | 1 | 1.0, 2, "a" |
1 | 2 | 0 | 1.0 , [1, 2] |
2 | 1 | 0 | [1.0, 2.0], 1 |
2 | 2 | 2 | [1.0, 2.0], [1, 2], ["a", "b"] |
passPythonObject = true
:nDblWri | nIntWri | nStrWri | Arguments |
---|---|---|---|
1 | 0 | 0 | 1., pytObj |
0 | 1 | 1 | 1, "a", pytObj |
2 | 0 | 2 | [1.0, 2.0], ["a", "b"], pytObj |
1 | 1 | 1 | 1.0, 2, "a", pytObj |
1 | 2 | 0 | 1.0 , [1, 2], pytObj |
2 | 1 | 0 | [1.0, 2.0], 1, pytObj |
2 | 2 | 2 | [1.0, 2.0], [1, 2], ["a", "b"], pytObj |
pytObj
is the Python object.The Python function must return their values in the following order:
nDblRea=1
) or
a list of doubles (if nDblRea > 1
).nIntRea=1
) or a list of integers (if nIntRea
> 1
).nDblRea = nIntRea = 0
, then the return values
of the function, if any, are ignored.The table below shows valid return types for various combinations where no, one or two double values and integer values are returned.
passPythonObject = false
:nDblRea | nIntRea | Return value |
---|---|---|
1 | 0 | 1. |
0 | 1 | 1 |
2 | 0 | [1.0, 2.0] |
1 | 1 | 1.0, 2 |
1 | 2 | 1.0 , [1, 2] |
2 | 1 | [1.0, 2.0], 1 |
2 | 2 | [1.0, 2.0], [1, 2] |
passPythonObject = true
:nDblRea | nIntRea | Return value |
---|---|---|
1 | 0 | 1., pytObj |
0 | 1 | 1, pytObj |
2 | 0 | [1.0, 2.0], pytObj |
1 | 1 | 1.0, 2, pytObj |
1 | 2 | 1.0 , [1, 2], pytObj |
2 | 1 | [1.0, 2.0], 1, pytObj |
2 | 2 | [1.0, 2.0], [1, 2], pytObj |
pytObj
is the Python object.The functions that exchange data with Python are implemented as
pure Modelica functions. Pure functions always return the
same value if called repeatedly. If these functions are used to
call hardware sensors or web services, they need to be called from
a when
-equation.
See the Modelica language specification for an explanation of pure and impure functions.
Various examples are provided, and for each of these, the Python
functions are stored in the directory
Buildings/Resources/Python-Sources
.
The examples Buildings.Utilities.IO.Python36.Functions.Examples.Exchange and Buildings.Utilities.IO.Python36.Functions.Examples.ExchangeWithPassPythonObject contains various calls to different Python functions without and with memory.
The example Buildings.Utilities.IO.Python36.Examples.KalmanFilter shows how to implement in a Modelica block a call to a Python function. This Python function stores its memory on disk between invocations (which, in general, is not recommended).
The example Buildings.Utilities.IO.Python36.Examples.SimpleRoom shows a similiar example. However, rather than using a file to store the room temperature and energy between invocations, the function returns an object with this information, and receives this object again in the next invocation.
String values cannot be returned from a Python function. The
reason is that Dymola 2013 FD01 generates a compile time error if a
Modelica function returns (Real[nR], Integer[nI],
String)
. This will be fixed in Dymola 2014. (Support request
#14983.)