The aimmspy Python Module
The main features of the aimmspy Python module are:
a seamless integration with existing AIMMS models from within a Python script
assign and retrieve data using Python dictionaries, Pandas, Polars or Arrow date frames
execute AIMMS procedures and retrieve results programmatically
The aimmspy library has been build with pybind11 for high-performance C++ integration.
Installation instructions
To install the aimmspy module, you can use pip:
pip install aimmspy
However, in case you execute your Python script from within an AIMMS procedure (i.e. using pyaimms), you do not need
to install the aimmspy module. It is sufficient to add the aimmspy
reference in your project’s .toml
file.
This will make sure that aimmspy
is installed in the virtual Python environment that will be set up.
Connecting to an AIMMS project
To connect your Python script to an (already existing) AIMMS model, you will need to initialize an instance of the Project class by providing:
the path of the AIMMS executable
the path to the project file
the license url
You can download a pre-made example from the link below:
Instead of the path to the AIMMS executable you can also provide just the AIMMS version number and use the function
find_aimms_path
(available in the aimms.utils
module) to figure out the location of the path to the AIMMS
executable.
If you have already a working, licensed AIMMS setup on your computer, there is no need to specify the license_url
in the constructor of the Project
instance.
After having created a project instance, you can retrieve the actual model by calling get_model
. The returned
model instance provides access to the identifiers in the model.
import os
from aimmspy.project.project import Project, Model
from aimmspy.utils import find_aimms_path
# Initialize the AIMMS project
project = Project(
# path to the AIMMS Bin folder (on linux the Lib folder)
aimms_path=find_aimms_path("25.4.3.3"),
# path to the AIMMS project
aimms_project_file=R"C:\AimmsProjects\MyAimmsProject\MyAimmsProject.aimms",
# url
license_url=R"wss://licensing.aimms.cloud/license-ws/license?profile=community&license=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
)
aimms_model: Model = project.get_model(__file__)
Of course, in case you are using a free AIMMS community license, you will need to replace the license code in the above snippet, by the license code of your community license.
Important
You can only connect to a single AIMMS project from within a Python script.
Identifier references
Identifier references (as an attribute of an instance of the project class) are case-sensitive. For example, if your AIMMS project contains an identifier Pi
and,
in your Python script, you created a reference to the associated model project with name aimms_model
, you will need to write aimms_model.Pi.data()
to retrieve the data for the identifier Pi
. Writing aimms_model.pi.data()
will show a Python _AttributeError_ with
message
'Model' object has no attribute 'pi'. Did you mean: 'Pi'?.
Namespace prefixes (e.g. for AIMMS identifiers inside a library) need to be separated by a dot character character.
Note that you will need to declare the library identifiers that you want to access in your Python script as public identifiers (on the attribute form of the library node in the AIMMS model tree).
Return data types
In this section we will focus on the data types that are used in Python to represent AIMMS data. For simple or medium sized AIMMS data you can use Python dictionaries. For large sized AIMMS data you might be better of using Python data frames.
Indexed data can be represented in
using Python dictionaries (see Python dictionaries)
using Python data frames (see Python data frames)
using Arrow Tables (see Python data frames)
Python dictionaries are a simple and straight-forward way to contain data for a single identifier. However, when the data size gets really big and/or you want to store the data of multiple (similar) identifiers in the same data structure, it is recommended to use a data frame to store your data.
The default data return type for multi-dimensional data can be specified during project construction. It is possible to override the default return type on a per-statement level. More details about this in the section on [Retrieving data](#retrieving-data)
import os
from aimmspy.project.project import Project, Model
from aimmspy.utils import find_aimms_path
# Initialize the AIMMS project
project = Project(
# path to the AIMMS Bin folder (on linux the Lib folder)
aimms_path=find_aimms_path("25.4.3.3"),
# path to the AIMMS project
aimms_project_file=R"C:\AimmsProjects\MyAimmsProject\MyAimmsProject.aimms",
# default data type when retrieving multi-dimensional data
data_type_preference= DataReturnTypes.PANDAS
)
To communicate with scalar identifiers, plain Python string and float values are used. Not surprisingly, values of scalar AIMMS string parameters and scalar element parameters are represented as a string in Python. A scalar element parameter in a calendar however, will return a Python datetime value. Values of scalar numerical identifiers are represented as a float in Python.
Python dictionaries
When representing AIMMS data in a Python dictionary, we assume the following:
the keys in the dictionary are the tuples for which the identifier has non-default data
the value type of the values in the dictionary depends on the identifier type in the AIMMS model
Python data frames
When working with AIMMS data that is large or complex, it is often more efficient to use data frames.
For data transfer of large sized data, the aimmspy Python module has support for two popular (and comparable) data frame libraries, being
Note
Representation of special values
Special values (in AIMMS) are currently represented in Python in the following way
the AIMMS value
inf
is translated into a Pythonfloat
with value 1e+150the empty label (in AIMMS) is translated into an empty Python string
the AIMMS values
zero
,undf
andna
are translated to a Python float with value 0.0
Dealing with default values
The translation of AIMMS data to Python data will be executed sparse, meaning that only non-default values
will be retrieved from AIMMS. Currently you can only retrieve data for a single identifier at a time.
For multi-dimensional data this means that no default values will be communicated to Python.
Scalar data with a default value are communicated to Python as values of type float
or string
, even in
case these scalar represent a default value.
When retrieving data from multi-dimensional AIMMS data identifiers with a non-default default value, you will have to deal with that in your Python script yourself (to ensure that all calculations in your Python script are correct).
Dealing with units
Numerical values are communicated (both-ways) in the unit of the corresponding AIMMS identifier. As in any AIMMS model, you can use conventions to change the identifier unit. This would allow you to input data in, say, ‘kilometers’ while, after having changed the (model) convention, retrieve the same value in ‘miles’.
Reference manual
Project
- Project(aimms_path, aimms_project_file, exposed_identifier_set_name=None, license_url=None, data_type_preference=None)
Project
class constructor- Arguments
aimms_path – the path to the AIMMS executable. You can use the
find_aimms_path
utility function to find the path for a specific AIMMS versionaimms_project_file – the path the the AIMMS project file
exposed_identifier_set_name – optional, a model set (subset of
AllIdentifiers
) that contains all model identifiers that are available in your Python script. Defaults toAllIdentifiers
if not specifiedlicense_url – optional, a reference to an AIMMS community cloud license url of the form
wss://licensing.aimms.cloud/license-ws/license?profile=community&license=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
, whereas thexxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
part needs to be replaced by the code of your AIMMS community license.data_type_preference – optional, the default return type when retrieving data from non-scalar data identifiers (by calling
.data()
on the associatedIdentifier
instance). Should be one ofDataReturnTypes.PANDAS
,DataReturnTypes.POLARS
, orDataReturnTypes.ARROW
. Defaults toDataReturnTypes.DICT
if not specified
A Project
instance provides access to the AIMMS model through
- get_model()
returns an instance of the
Model
class
Important
In model with a lot of identifiers, you are advised to use the exposed_identifier_set_name
parameter to restrict
the accessible identifiers to those that are needed in your Python script.
Model
The Model
instance is retrieved through the get_model
method of the Project
instance.
During construction of the Model
instance, the aimmspy library will automatically create a stub
file that is can be to support you while editing your Python script using
Visual Studio Code.
To do so, when you are creating a Python script with name my_script.py
, the aimmspy library will create
a stub file with name
my_script.pyi
in the same directory as your Python script. This stub file contains all identifiers that are
available in the AIMMS model. To actually use the stub file, you should extend your script with something like
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from my_script import Model
The stub file will ensure that you can refer to any of the exposed identifiers as if it was an attribute of the Model
instance.
DataIdentifier
Assigning data
To assign data from Python objects to AIMMS identifiers two methods are available on instances of type DataIdentifier
- assign(data, options=None)
Assigns data to the identifiers
- Arguments
data –
data
should be anint
,float
, astring
, adatetime
object, a dictionary, a Pandas or a Polars data frame, or an Arrow tableoptions – a dictionary with options that can be used to control the behavior of the assignment
Available options
update
: If set toFalse
(default), the identifier will be emptied before the assignment is executed. If set toTrue
, the behavior is identical to that of theupdate
methodextendType
: Should be one of the following (string) values:extend
(default): Elements that are not in a domain set yet, will be automatically added (assumed the set is updatable)error
: AnAimmsPyException
will be thrown when encountering an element that is outside of one of the domain setsfilter
: Tuples with an element that is outside the domain will be silently ignored
mapping
: A string-to-string map in which the key should correspond to a column name in the date frame and the corresponding values should refer to the associated identifier name in the AIMMS model.The options can be combined in a dictionary, for example:
{ "update": True, "extendType": "filter", {"mapping": { "MyColumnName": "MyIdentifierName"} }
For example:
num_inhabitants_data_df = pd.DataFrame({ "c": [ 'Amsterdam', 'Haarlem' ], "Number of Inhabitants": [ 741636, 147590 ] }) aimms_model.NumInhabitants.assign(num_inhabitants_data_df, { "update": True, "mapping": { "Number of Inhabitants": "NumInhabitants"} })
- update(data, options=None)
Assigns data to the identifiers. Same as
data
withupdate
option set toTrue
Retrieving data
- data(options=None)
To retrieve data from AIMMS identifiers, you can use the
.data()
method on an instance of typeDataIdentifier
. The return value will be afloat
,string
or adatetime
object for scalar identifiers, and a dictionary, a Pandas or Polars data frame, or an Arrow table for multi-dimensional identifiers.- Arguments
options – optional, a dictionary with options that can be used to specify the return type of the data
For example, to enforce the return type to be a Pandas DataFrame, you can use the following for the options
argument
{ "return_type": DataReturnTypes.PANDAS }Using this for the
NumInhabitants
identifier that we used in the previous session would for example look likenum_inhabitants = aimms_model.NumInhabitants.data({ "return_type": DataReturnTypes.PANDAS }) print(f"num_inhabitants:\n{num_inhabitants}")which would yield the following output
num_inhabitants: c NumInhabitants 0 Amsterdam 741636.0 1 Haarlem 147590.0
The default return type for multi-dimensional identifiers can be set during the construction of the Project
instance.
Running procedures
Procedure can be executed directly using the Model
instance. Just create a reference to the procedure and add to brackets
(in case of a procedure without arguments) to trigger execution.
So far, we only support scalar input arguments for procedures. The order of the arguments needs to match the order in which they are declared in the model. The return value will be the return value of the AIMMS procedure.
For example:
retval = aimms_model.SetInterestRate(rate=1.06)
Exceptions
The aimmspy module introduces two custom Exceptions
AimmsException
: Exceptions that are generated in AIMMS itself, either raised from model or due to some other internal issue.AimmsPyException
: Exceptions that are raised in the aimmspy Python module, i.e. the part of the code that is responsible for the communication between Python and AIMMS.For example,
additional_num_inhabitants_data = pd.DataFrame({ "c": [ 'Invalid City' ], "NumInhabitants": [ 123456 ] }) try: aimms_model.NumInhabitants.assign(additional_num_inhabitants_data, {"update": True, "extendType": "error"}) except AimmsPyException as e: print(f"Invalid set element raised an AimmsPyException (as expected): {e}")
would yield the following output:
Invalid set element raised an AimmsPyException (as expected): Invalid element 'Invalid City