Several wrappers can be defined for the Modelverse, as the Modelverse is merely a service running externally. To communicate effectively and automatically, a programming language wrapper is recommended. Nonetheless, it is possible to communicatie manually as well. These are some of the implemented wrappers.


The simplest wrapper is the prompt wrapper, which merely sends the input and output directly to the user. This wrapper has almost no code, but requires users to manually decide which content to send next. It has no built-in integration with any model or action language compilers. Nonetheless, it is an easy way to test out the raw communication protocol manually.


The first real wrapper is the Python-based wrapper. It provides a set of functions for use by Python code. These functions wrap not only the interface, but also provides simple error handling through the use of Python exceptions and contains the model and action language compilers. Since it is relatively minimal and provides a complete implementation of a Modelverse Interface, it is considered as a reference implementation for the Modelverse protocol. An overview of all functions and associatied exceptions is provided below. All operations happen synchronously, meaning that they block until the Modelverse has performed the requested operation.

Note that some functions are only applicable in a certain context. In practice, this means that you should first issue the init and login operations, as otherwise your connection with the Modelverse will not have started up yet.

For each function, we provide an example to indicate how this operation can be used. In these first examples, we assume that all referenced elements are present, all permissions are granted, etc. Afterwards, we provide an overview of all exceptions, together with an example. In these examples, the problem is often caused by an non-existing element or unsatisfied permissions.

Initialization Function

init(address_param="", timeout=20.0)

Start up the connection to the Modelverse, residing at address_param. This connection is an XML/HTTPRequest and will start up a new task at the Modelverse. Retries for timeout seconds until giving up. The timeout includes all HTTP errors, and will therefore keep retrying even on failed attempts. As this request is synchronous, like all others, it will block until a connection has been established.


  • To create a connection to a local Modelverse.

    >>> init()
  • To create a connection to the Modelverse running remotely.

    >>> init("http://modelverse.uantwerpen.be:8001")


login(username, password)

Explanation Logs in the currently active Modelverse connection to the specified username and password. If the user does not exist, it will create a new user with the specified password. If the user already exists, it will try to log in with the provided password.


  • To login as user user1, with the specified password.

    >>> login("user1", "my_password")

Logs out the current user, thereby closing the task. Subsequent operations will no longer have any effect, as the task was terminated. To log in as a different user, the init operation has to be executed again.


  • To log out the current user and allow future logins by this user.

    >>> user_logout()
user_name(user, username)

Changes the username of user user to username. From this point on, the user has been renamed, and all old permissions are inherited. Similar to password management, users can only alter their own username. The administrator can also alter the username of all users. Active logins do not need to be refreshed, as the new username will automatically be used everywhere. From the next login, however, the new username has to be used, as the old one has been renamed.


  • To change the username of user1 to user2.

    >>> user_name("user1", "user2")
user_password(user, password)

Changes the password of user user to password. Permissions on this operation are of course rather strict: users can only modify their own password. Of course, an administrator can modify the password of every user. Active logins do not need to be refreshed, as the new username will automatically be used everywhere. From the next login, however, the new username has to be used, as the old one has been renamed.


  • To change the password of user user1 to my_password.

    >>> user_password("user1", "my_password")

Model Management


Create a folder with the specified name. If necessary, this operation recursively creates all parent folders as well. All created folders are owned by the creating users and permissions are set accordingly.


  • To create a folder my_folder in your user folder.

    >>> folder_create("users/user/my_folder")
  • To create a folder my_subfolder in the folder parent_subfolder, in your user folder.

    >>> folder_create("users/user/parent_subfolder/my_subfolder")

Returns typing information of the model. This information consists of a set of tuples, each tuple containing the metamodel, the type mapping model, and the conformance checking function used. Multiple entries might have the same metamodel or type mapping model, as there might be multiple ways in which a model conforms to a metamodel. Note that this only queries for existing typing relations, and does not consider potential relations. For example, all models will conform to formalisms/Bottom, but this will only be an entry in model_types if the relation was explicitly created (e.g., by opening the model as such). The type mapping model might be empty (i.e., None), in which case the type mapping model has to be dynamically constructed. If the conformance semantics is set to None, this means that the internal Modelverse semantics is used.


  • To query for the metamodels of model my_pn.

    >>> model_types("my_pn")
    set([("formalisms/PetriNets", "type mappings/1", None)])
  • To query for the metamodels of model my_pn, after it has been opened using formalisms/Bottom.

    >>> model_types("my_pn")
    set([("formalisms/PetriNets", "type mappings/1", None), ("formalisms/Bottom", "type mappings/20", None)])
  • To query for the metamodel of a metamodel formalisms/PetriNets.

    >>> model_types("formalisms/PetriNets")
    set([("formalisms/SimpleClassDiagrams", "type mappings/2", None)])
conformance_add(model_name, metamodel_name)

Add a new conformance relation between a model stored at model_name and a metamodel stored at metamodel_name. While this adds the conformance relation, it does not specify the type mapping model, meaning that it has to be subsequently resolved before use. Where possible, type mapping models can be automatically deduced (e.g., conformance bottom, traceability, and type mapping). If no type mapping can be deduced, an exception will be thrown during the execution of the first command that tries to open the model in this way. After the conformance_add operation, the relation can already be seen using model_types, as it is stored at the server. This is in contrast to alter_context, which is only stored locally in the client.


  • To consider a model my_pn as a conformance bottom model.

    >>> conformance_add("my_pn", "formalisms/Bottom")
conformance_delete(model_name, metamodel_name, type_mapping_name)

Delete a conformance relation between a model stored at model_name and a metamodel stored at metamodel_name, using the type mapping model stored at type_mapping_name. The type_mapping_name can be left empty (empty string) if no type mapping model is defined yet. All matching conformance relations are removed. After the conformance relation is removed, it might be possible that the relation lingers in the context, meaning that a new attempt will be made to deduce a typing relation from scratch.


  • To remove the previously considered conformance bottom relation for model “my_pn”, with type mapping model “type mappings/123”.

    >>> conformance_delete("my_pn", "formalisms/Bottom", "type mappings/123")
  • To remove the previously considered conformance bottom relation for model “my_pn”, without a constructed type mapping model.

    >>> conformance_delete("my_pn", "formalisms/Bottom", "")
model_add(model_name, metamodel_name, model_code=None)

Upload a new model that can later be referred to as model_name, conforming to metamodel_name. The model itself is stored in the string model_code. This string is parsed using the HUTN compiler and subsequently sent to the Modelverse. When model_code is empty, an empty model is created.


  • To create a new model called PetriNets, conforming to SimpleClassDiagrams, and load the model stored in models/PetriNets.mvc.

    >>> model_add("formalisms/PetriNets", "formalisms/SimpleClassDiagrams", open("models/PetriNets.mvc", "r").read())
  • To create a minimal instance of the language afterwards, which only contains a single place (and no attributes).

    >>> model_add("models/my_pn", "formalisms/PetriNets", "Place p1 {}")
  • To create a less minimal instance of the language, stored in models/my_pn2.mvc.

    >>> model_add("models/my_pn2", "formalisms/PetriNets", open("models/my_pn2.mvc", "r").read())

Upload a string of code in the Action Language formalism. This piece of code is compiled with the HUTN compiler and sent to the Modelverse directly. Makes the assumption that the construct_function() operation is currently running on the Modelverse, as otherwise the data will be misinterpreted. This is normally only useful in model transformations, where you want to upload a piece of code on-the-fly (e.g., adding a breakpoint in Action Language).


  • To upload the file models/code.alc.

    >>> upload_code(open("models/code.alc", "r").read())
  • To upload a code fragment inline.

    >>> upload_code("Void function a():\nreturn!")

Delete the model referred to by name model_name. This is a non-cascading delete, with almost no checks: model transformations depending on this model will likely become corrupted.


  • To delete a previously created model.

    >>> model_delete("models/my_pn2")
  • Or to delete a metamodel, which is itself just a model.

    >>> model_delete("formalisms/PetriNets")
  • To delete a full folder.

    >>> model_delete("formalisms")

Returns a list of all models existing in the specified folder. Sub-folders can be recognized because they have a trailing forward slash in their name.


  • To get a list of all models in the formalisms directory.

    >>> model_list("formalisms")
    ["PetriNets", "SimpleClassDiagrams", "Bottom", "Tracability", ...]

Returns a detailed list of all models existing in the specified folder in the Modelverse. This list includes information on permissions, owner, and group.


  • To get a detailed list of all currently present models.

    >>> model_list_full("models")
    [("my_pn", "user1", "users", "200"), ("my_pn2", "user1", "users", "200"), ...]
verify(model_name, metamodel_name)

Verify whether model_name conforms to metammodel_name, as both stored in the Modelverse. Returns either “OK” if the model conforms, or a string specifying the reason for non-conformance.


  • Verifying a conforming model.

    >>> verify("formalisms/PetriNets", "formalisms/SimpleClassDiagrams")
  • Or verify using the alternative conformance relation (conformance bottom).

    >>> verify("formalisms/PetriNets", "formalisms/Bottom")
  • Verifying a non-conforming model.

    >>> verify("models/my_pn")
    Lower cardinality violation for attribute "name" at Place p1.
model_overwrite(model_name, new_model_code=None)

Overwrites the model previously known under the name model_name with the model code in new_model_code. This operation differs from first deleting the model and then recreating it, as all metadata of the model is kept, such as access permissions. The new model can be kept empty, in which case the model will be cleared.


  • To overwrite the PetriNets metamodel with a newer version, thereby also updating the metamodel of all existing instances (“my_pn” and “my_pn2”).

    >>> model_overwrite("formalisms/PetriNets", open("models/PetriNets2.mvc", "r").read())
  • To overwrite an existing PetriNets instance.

    >>> model_overwrite("models/my_pn", """Place p2 {}""")


model_render(model_name, mapper_name)

Render the model by name of model_name using the mapper by name of mapper_name. Both parameters have to be known models in the Modelverse. Outputs a JSON representation of the rendered model. This is basically just a shortcut for executing the specified operation and reading out the resulting model with a JSON representation.


  • To render the PetriNets instance using the PetriNetsMapper.

    >>> model_render("models/my_pn", "formalisms/PetriNetsMapper")
    [{"id": "__12345", "type": "Ellipse", "x": 100, "y": 150, "height": 20, "width: "20"}]
transformation_between(source, target)

List all transformations that take at least all elements in source as input, with an exact match in tags, and that generate at least all elements in target, again with the same tags. An empty dictionary means that there is no constraint on that part, but there must be at least one tag specified.


  • To fetch all endogenous transformations on PetriNets on the PN tag, assuming that some were previously defined.

    >>> transformation_between({"PN": "formalisms/PetriNets"}, {})
    ["PN_simulate", "PN_optimize", "PN_to_matrix"]
  • To fetch all endogenous transformations on PetriNets on the PN tag that modify the model in-place, assuming that some were previously defined.

    >>> transformation_between({"PN": "formalisms/PetriNets"}, {"PN": "formalisms/PetriNets"})
    ["PN_simulate", "PN_optimize"]
transformation_add_MT(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None)

Create a new model transformation operation. The new transformation takes source_metamodels as input, and generates target_metamodels as output. Both parameters are dictionaries of the form {name: metamodel_name}. The name is used later on in the model transformation as a prefix to the type. A single metamodel_name can be used for multiple names. Note that the target metamodel names may overlap with the source metamodel names, but the metamodel type should be identical. The operation is henceforth known by operation_name and is provided as a model in the string code. Optionally, a callback is defined which performs some operations on the merged metamodel, for example to define tracability links between (previously unrelated) metamodels. In the background, this operation does all necessary RAMification and model merging.


  • To create a new model transformation for PetriNets simulation.

    >>> transformation_add_MT({"pn": "formalisms/PetriNets"}, {"pn": "formalisms/PetriNets"}, "models/pn_simulate", open("models/PN_simulate.mvc", "r").read())
  • To create a model transformation from a DSL to PetriNets, which requires tracability links.

    >>> def tracability_links():
    ...     instantiate("Association", ID="Tile2Place", ("dsl/Tile", "pn/Place"))
    ...     instantiate("Association", ID="Dirrection2Transition", ("dsl/Direction", "pn/Transition"))
    >>> transformation_add_MT({"dsl": "formalisms/RPGame"}, {"pn": "formalisms/PetriNets"}, "models/denotational_1", open("models/denotational_1.mvc", "r").read(), tracability_links)
  • To create a multi-input model transformation.

    >>> transformation_add_MT({"pn_1": "formalisms/PetriNets", "pn_2": "formalisms/PetriNets", "architecture: "formalisms/Architecture"}, {"result": "formalisms/PetriNets"}, "models/PN_merge", open("models/PN_merge.mvc", "r").read())
transformation_add_AL(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None)

Creates a new action language operation. Similar to transformation_add_MT, but now does not require RAMification. The code parameter also is not specified as a Modelverse model (.mvc), but as action language (.alc).


  • To create a new action language operation for PetriNets reachability analysis.

    >>> transformation_add_AL({"pn": "formalisms/PetriNets"}, {"graph": "formalisms/ReachabilityGraph"}, "models/pn_analyze", open("models/PN_reachability.alc", "r").read())
  • To create an action language operation from a Scheduling DSL to a list, which requires tracability links.

    >>> def tracability_links():
    ...     instantiate("Association", ID="Task2Event", ("schedule/Task", "list/Event"))
    >>> transformation_add_AL({"schedule": "formalisms/SchedulingDSL"}, {"list": "formalisms/EventList"}, "models/sequentialize", open("models/sequentialize_schedule.alc", "r").read(), tracability_links)
transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=lambda: None)

Creates a new manual operation. Identical to transformation_add_AL, but does not take any code as content.


  • To create a manual refinement operation on PetriNets.

    >>> transformation_add_MANUAL({"pn": "formalisms/PetriNets"}, {"pn": "formalisms/PetriNets"}, "models/pn_refine")
  • To create a multi-input refinement operation on PetriNets.

    >>> transformation_add_MANUAL({"pn": "formalisms/PetriNets", "requirements": "formalisms/Requirements"}, {"pn": "formalisms/PetriNets"}, "models/pn_refine_req")
transformation_execute_AL(operation_name, input_models_dict, output_models_dict, statechart=None, traceability_model="", fetch_output=True)

Executes the Action Language model operation_name with input_models_dict as inputs and output_models_dict as outputs. For both dicts, the contents describe the mapping between the parameter names of the operation to the names in the Modelverse. Values in input_models_dict must be existing models, whereas values in output_models_dict can be non-existing upon invocation. A statechart tuple can be defined when the action language model requires user input or output. This is used to communicate with the executing code directly. For more information about this, refer to the execution of transformations. The traceability model can optionally be specified, which then loads in the selected model to create the traceability links between all models, and all changes are stored to this model as well. The fetch_output parameter ensures that the output thread is closed after this call. This is useful if the activity is forever running and no other communication has to happen with the MvI.


  • To execute reachability analysis on an existing petri net.

    >>> transformation_execute_AL("models/pn_analyze", {"pn": "models/my_pn"}, {"graph": "models/my_pn_reachability"})
transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None)

Executes the manual model operation operation_name. Furthermore, this is identical to transformation_execute_AL, with the exception of the callback function. In this case, the callback function can be just another series of Modelverse operations, though pertaining to a single model, which is passed as a parameter. As such, the model_name parameter of these operations MUST be set to the parameter.


  • To execute a manual operation, which requires you to refine a PetriNets instance.

    >>> def callback(model):
    ...     p1 = instantiate(model, "pn/Place")
    ...     t1 = instantiate(model, "pn/Transition")
    ...     instantiate(model, "pn/P2T", (p1, t1))
    >>> transformation_execute_MANUAL("models/pn_refine", {"pn": "models/my_pn"}, {"pn": "models/my_pn"}, callback)
transformation_execute_MT(operation_name, input_models_dict, output_models_dict, statechart=None, traceability_model="", fetch_output=True)

Executes the model transformation operation operation_name. Identical to transformation_execute_AL.


  • To execute a model transformation on a PetriNets instance, thereby putting the result in a different model.

    >>> transformation_execute_MT("models/pn_simulate", {"pn": "models/my_pn"}, {"pn": "models/my_simulated_pn"})

Returns the signature of the transformation transformation. The signature consists of two dictionaries: the first specifies the input signature, and the second one specifies the output signature. Both dictionaries have the same structure: the keys represent the names of the model while invoking the operation, and the values represent the expected type of the model.


  • To read out the signature of the “plant_refine” operation.

    >>> transformation_read_signature("models/plant_refine")
    ({"req": "formalisms/Requirements", "plant": "formalisms/Plant"}, {"plant": "formalisms/Plant"})


process_execute(process_name, model_mapping, callbacks={})

Execute the process model stored as process_name. Models are retrieved and stored in the Modelverse based on the names stored in the process model. When a name is present in the model_mapping, the dictionary value is used as the location where the model is retrieved or stored. If the name is not present in the mapping, the model is considered to be anonymous. Optionally, a dictionary of callbacks can be defined with as key the operation that is being executed, and value the actual callback. The callbacks must be similarly defined just like how they were defined in the individual transformation_execute operations. This means that the callback might very well be a tuple for a statechart instance.


  • To execute a process model for the power window example, thereby binding the “requirements” model to the Modelverse location “models/requirements”.

    >>> process_execute("models/pm_powerwindow", {"requirements": "models/requirements"})
  • To execute a process model for the power window example, which requires user input for some operations.

    >>> def refine_architecture(model):
    ...     # Do some operation on the architecture model here
    ...     node1 = instantiate(model, "Node")
    ...     node2 = instantiate(model, "Node")
    ...     instantiate(None, "Connection", (node1, node2))
    >>> def refine_control(model):
    ...     # Do some operation on the control model here
    ...     s1 = instantiate(model, "State")
    ...     s2 = instantiate(model, "State")
    ...     instantiate(model, "Transition", (s1, s2))
    >>> def refine_query(model):
    ...     # Do some operation on the safety query model here
    ...     p1 = instantiate(model, "Place")
    ...     attr_assign(model, p1, "tokens", 2)
    >>> process_execute("models/pm_powerwindow", {}, {"models/refine_plant": refine_plant, "models/refine_control": refine_control, "models/refine_query": refine_query})

Fetch the process signature, specifying the list of all possible models and their metamodel. The returned dictionary works in two directions, as it is used for retrieving models and storing them again. All Data instances are listed here, with their name attribute as key and their type attribute as value.


  • To fetch all data artefacts from the process pn_reachability.

    >>> process_signature("pn_reachability")
    {"pn": "users/user/test/PetriNet", "reachability": "users/user/test/ReachabilityGraph"}

User Access Control


Read the permissions of the current user for a specific model. In contrast to the model_list_full operation, which gives general permission information (e.g., owner and permissions for owner), this operation gives tailored information for this specific user (i.e., none, read, or write). As such, this operation does the necessary resolution of group membership and owning group deduction. The result is a string containing letters indicating permissions: “R” for read and “W” for write. If the model even cannot be read, the string is empty.


  • To determine the permissions for the users/user/my_pn model, which we own, for which we have read and write permissions.

    >>> read_permissions("user/user/my_pn")
  • To determine the permissions for the formalisms/SimpleClassDiagrams metamodel, for which we have read permissions, but no write permissions.

    >>> read_permissions("formalisms/SimpleClassDiagrams")
  • To determine the permissions of the administration/core model, which we are not allowed to read or write.

    >>> read_permissions("administration/core")
permission_modify(model_name, permissions)

Change the permissions of model_name to permissions. The permissions is a string of three characters, each between 0 and 2. The format is similar to the UNIX permission system: the leftmost character is the permission for the owning user, the middle character for members of the ownin group, and the rightmost character for all other users. Character 0 signifies no access, 1 read-only access, and 2 full read/write access. Note that changing permissions trickle down to the instances as well: if the metamodel is no longer readable to the user, all models conforming to it become unreadable in this specific context.


  • To modify the permissions of the PetriNets metamodel, allowing only the owner to read and write to it.

    >>> permission_modify("formalisms/PetriNets", "200")
  • To modify the permissions of a PetriNets model, granting everyone read/write access.

    >>> permission_modify("formalisms/PetriNets", "222")
permission_owner(model_name, owner)

Change the owner of the model model_name to owner. Changing permissions trickles down to the instances as well: if the metamodel is no longer readable to the user, all models conforming to it become unreadable in this specific context.


  • To change the owning user of the PetriNets metamodel to user2.

    >>> permission_owner("formalisms/PetriNets", "user2")
permission_group(model_name, group)

Change the owning group of the model model_name to group. The same remarks hold as for all other permission operations.


  • To change the owning group of the PetriNets metamodel to group1.

    >>> permission_group("formalisms/PetriNets", "group1")

Create a new group named group_name. You automatically become an administrator for this group.


  • To create a new group called group2.

    >>> group_create("group2")

Delete the group named group_name. All users will automatically be kicked from the group, and all permissions previously granted by this group become void.


  • To delete the group group2.

    >>> group_delete("group2")
group_owner_add(group_name, user_name)

Add user user_name as an owner of group group_name. This automatically makes the user join the specified group if this was not yet the case.


  • To add user user1 as a new owner, or group administrator, to group group1.

    >>> group_owner_add("group1", "user1")
group_owner_delete(group_name, user_name)

Remove user user_name as an owner of group group_name.


  • To delete user1 as an owner, or group administrator, from group group1.

    >>> group_owner_delete("group1", "user1")
group_join(group_name, user_name)

Have user user_name join the group group_name as an ordinary user.


  • To make user user1 join the group group1, of which the current user is an owner.

    >>> group_join("group1", "user1")
group_kick(group_name, user_name)

Remove user user_name from the group group_name. If the user was an owner of the group, these permissions are also revoked.


  • To kick user user1 from group group1, of which the current user is an owner.

    >>> group_kick("group1, "user1")



Promote user user_name to admin status. Admin status grants users access to all operations, and provides all permission. Effectively, all groups and files have read/write access for the admin user, independent of the stored permissions.


  • To promote user1 to admin states.

    >>> admin_promote("user1")

Demote user user_name to ordinary user.


  • To demote user1 to a normal user.

    >>> admin_demote("user1")



Returns a complete representation of a model in a compact dicationary-like representation. This is basically a list of dictionaries, each dictionary containing the values usually obtained with read_attrs. Additionally, some special keys are added: id (the identifier returned upon instantiation), __source (the source of the association, if it was an association), and __target (the target of the association, if it was an association).


  • To read out a list of a PetriNets instance model.

    >>> element_list_nice("models/my_pn")
    [{"id": "p1", "name": "a place", "tokens": 1}, {"id": "t1", "name": "a transition"}, {"id": "p1_to_t1", "name": "transition", "__source": "p1", "__target": "t1", "weight": 1}]

Returns a list of all elements and their type specified in the model named model_name. This list can contain much more than only simple elements, but includes anonymous edges and attributes as well. It is therefore not recommended for general purpose use; use element_list_nice instead.


  • To get a list of all elements in the PetriNets metamodel.

    >>> element_list("formalisms/PetriNets")
    [("Place", "Class"), ("Transition", "Class"), ("P2T", "Association"), ("T2P", "Association"), ...]

Returns a list of all types usable in the model named model_name. This is similar to executing element_list on the metamodel of this model.


  • To get a list of all types usable in the PetriNets metamodel (i.e., when altering the metamodel itself).

    >>> types("formalisms/PetriNets")
    ["Class", "Association", "SimpleAttribute", ...]
read_info(model_name, ID)

Read the content of model element ID in model model_name. This returns the type of the element, and the set of source and destination if the element is an edge (None otherwise).


  • To read out the P2T link in the PetriNets metamodel.

    >>> read_info("formalisms/PetriNets", "P2T")
    ["Association", ("Place", "Transition")]
  • To read out the Place node in the PetriNets metamodel.

    >>> read_info("formalisms/PetriNets", "Place")
    ["Class", None]
  • To read out some P2T instance in a PetriNets model.

    >>> read_info("my_pn", "p1_to_t1")
    ["P2T", ("p1", "t1")]
  • To read out some Place instance in a PetriNets model.

    >>> read_info("my_pn", "p1")
    ["Place", None]
read_attrs(model_name, ID)

Return a dictionary of all attributes of model element ID in model model_name, containing their values. All values in the Modelverse are primitive types, and as such, this is also the case in this operation. The value is None in case the attribute is not set.


  • To read out the attributes of the Place class.

    >>> read_attrs("formalisms/PetriNets", "Place")
    {"lower_cardinality": None, "upper_cardinality": None}
  • To read out the attributes of a Place instance.

    >>> read_attrs("models/my_pn", "p1")
    {"name": "critical_section", "tokens": 1}
instantiate(model_name, typename, edge=None, ID="")

Instantiate a new instance of typename in the model model_name. If the instance is an edge, provide a tuple containing the source and target as edge. A preferred ID can be specified, though there is no guarantee that this name is actually used (e.g., if it is already taken by another element). This operation returns the actually assigned ID. It is this ID that is used for all other operations on the model.


  • To create a new Place instance in a PetriNets model.

    >>> instantiate("models/my_pn", "Place")
  • To create a new Place instance with a preferred ID, which is granted.

    >>> instantiate("models/my_pn", "Place", ID="critical_section")
  • To create a new Place instance with a preferred ID, which is not granted.

    >>> instantiate("models/my_pn", "Place", ID="critical_section")
  • To create a new P2T instance in a PetriNets model.

    >>> instantiate("models/my_pn", "P2T", ("p1", "t1"))
  • To create a new concept in the PetriNets metamodel, which can later on be used in all instances immediately.

    >>> instantiate("formalisms/PetriNets", "Association", ("Place", "Transition"), ID="InhibitorArc")
    >>> instantiate("models/my_pn", "InhibitorArc", ("p1", "t1"))
delete_element(model_name, ID)

Delete the element ID in the model model_name. This is a recursive delete, and all incoming and outgoing edges will be removed (recursively) as well.


  • To delete an existing element in a PetriNets model.

    >>> delete_element("models/my_pn", "critical_section")
  • To delete an existing exdge in a PetriNets model.

    >>> delete_element("models/my_pn", "p1_to_t1")
  • When deleting an element “p1”, the arc “p1_to_t1” is also removed automatically.

    >>> delete_element("models/my_pn", "p1")
    >>> delete_element("models/my_pn", "p1_to_t1")

Attribute Manipulation

attribute_optional(model_name, node, attr_name, optionality)

Alter the optionality of a defined attribute attr_name on a class node in the model model_name to optionality. The optionality has to be a boolean value.


  • To make the number of tokens in formalisms/PetriNets optional.

    >>> attribute_optional("formalisms/PetriNets", "Place", "tokens", True)
  • To make the number of tokens in formalisms/PetriNets mandatory.

    >>> attribute_optional("formalisms/PetriNets", "Place", "tokens", False)
attribute_name(model_name, node, attr_name, new_name)

Change the name of a defined attribute attr_name on a class node in the model model_name to new_name. The new name has to be a string value, and no similarly named attribute should exist on that element yet.


  • To change the name of the tokens attribute to nrTokens in formalisms/PetriNets.

    >>> attribute_name("formalisms/PetriNets", "Place", "tokens", "nrTokens")
attribute_type(model_name, node, attr_name, new_type)

Change the type of a defined attribute attr_name on a class node in the model model_name to new_type. The new type has to be a string value, refering to a type in the metamodel that defines the new type of the attribute.


  • To change the type of the tokens attribute Integer in formalisms/PetriNets, where Integer is defined as a SimpleAttribute instance.

    >>> attribute_name("formalisms/PetriNets", "Place", "tokens", "Integer")
read_defined_attrs(model_name, node)

Retrieve a list of all defined attributes in class node in model model_name. The return value consists of a pair of dictionaries. The first dictionary containing all mandatory attributes, and the second dictionary containing all optional attributes. Both dictionaries are constructed the same way, consisting of the name of the attribute as key, and the name of the type as value.


  • To fetch all defined attributes of the Place class in formalisms/PetriNets, which has a mandatory name and number of tokens, with an optional capacity.

    >>> read_defined_attrs("formalisms/PetriNets", "Place")
    ({"name": "String", "tokens": "Natural"}, {"capacity": "Natural"})
define_attribute(model_name, node, attr_name, attr_type)

Defines a new attribute on the element node in the metamodel identified by model_name. The attribute is named attr_name and must conform to attr_type. Note that, while there are other ways of creating attributes than this function, this function is by far the most user friendly. The type being assigned needs to be specified in the metamodel as well, by instantiating the SimpleAttribute node. As models are updated immediately, without any recompilation step inbetween, all instances can immediately make use of this new attribute.


  • To define a new attribute “tokens” on a PetriNet Place, which is of a Natural type.

    >>> define_attribute("formalisms/PetriNets", "Place", "tokens", Natural)
undefine_attribute(model_name, node, attr_name)

Removes a defined attribute attr_name on node in model model_name. Upon removal, all information about the attribute is also removed, such as optionality, name, and typing information. Instances of the metamodel which make use of this attribute, will become non-conforming.


  • To undefine the tokens attribute in the Place class for formalisms/PetriNets.

    >>> undefine_attribute("formalisms/PetriNets", "Place", "tokens")
attr_assign(model_name, ID, attr, value)

Assign the value value to the attribute named attr of the element ID in the model named model_name. If the attribute already has an assigned value, the previous value is removed first.


  • To assign some attributes to a Place instance.

    >>> attr_assign("models/my_pn", "p1", "name", "my first place")
    >>> attr_assign("models/my_pn", "p1", "tokens", 1)
  • To assign some attributes to the Place class itself.

    >>> attr_assign("formalisms/PetriNets", "Place", "upper_cardinality", 1)
attr_assign_code(model_name, ID, attr, code)

Assign the code block code to the attribute named attr of the element ID in the model named model_name. If the attribute already has an assigned value, the previous value is removed first. The assigned code is compiled to Action Language by the HUTN compiler.


  • To assign a piece of action code to a Statecharts transition, loaded from file.

    >>> attr_assign_code("models/my_sc", "t1", "script", open("models/t1_script", "r").read())
  • To assign a piece of action code to a Statecharts transition, defined inline.

    >>> code = \
    ...   """
    ...   Void function action(attributes : Element):
    ...        dict_overwrite(attributes, "counter", 1)
    ...        return!
    ...   """
    >>> attr_assign_code("models/my_sc", "t1", "script", code)
attr_delete(model_name, ID, attr)

Unset the attribute attr of model element ID in the model model_name. This is not necessary when assigning a new value, as attr_assign automatically does this when required. As such, this only is useful when dealing with an optional attribute, or when you want to create a non-conforming model.


  • To unset the name attribute of a place.

    >>> attr_delete("models/my_pn", "p1", "name")

Model Query

all_instances(model_name, type_name)

Returns a list of all instances of type type_name in the model model_name. This includes indirect instances, which are only typed by type_name through inheritance.


  • To get all places in a PetriNet model.

    >>> all_instances("models/my_pn", "Place")
    ["p1", "__12345"]
  • To get all nodes that can be instantiated directly for PetriNets.

    >>> all_instances("formalisms/PetriNets", "Class")
    ["Place", "Transition", "P2T", "T2P"]
connections_between(model_name, source_element, target_element)

Returns a list of all allowed connection types between source_element and target_element in a specified model. Both elements need to be part of the same model, as otherwise a merged model should be created beforehand.


  • To read out the allowed connections between elements “p1” and “t1”.

    >>> connections_between("models/my_pn", "p1", "t1")
  • To read out the allowed connections from the Place class to itself.

    >>> connections_between("formalisms/PetriNets", "Place", "Place")
    ["Association", "Inheritance"]
read_outgoing(model_name, ID, typename)

Returns a list of all outgoing associations of ID, typed by typename, in model model_name. Typename can be set to the empty string to indicate that all types must match. Note that this returns the association itself, NOT the destination.


  • To get all arcs starting in place p1.

    >>> read_outgoing("models/my_pn", "p1", "P2T")
  • To get all allowed connections starting in a Place.

    >>> read_outgoing("formalisms/PetriNets", "Place", "Association")
    ["P2T", "InhibitorArc"]
read_incoming(model_name, ID, typename)

Returns a list of all incoming associations of ID, typed by typename, in model model_name. Typename can be set to the empty string to indicate that all types must match. Note that this returns the association itself, NOT the source.


  • To get all arcs going to place p1.

    >>> read_incoming("models/my_pn", "p1", "T2P")
  • To get all allowed connections going to a Place.

    >>> read_incoming("formalisms/PetriNets", "Place", "Association")
read_association_source(model_name, ID)

Returns the identifier of the source of association ID in model model_name.


  • To read out the source of the P2T link.

    >>> read_association_source("formalisms/PetriNets", "P2T")
  • To read out the source of an arc.

    >>> read_association_source("models/my_pn", "p1_to_t1")
read_association_destination(model_name, ID)

Returns the identifier of the target of association ID in model model_name.


  • To read out the target of the P2T link.

    >>> read_association_destination("formalisms/PetriNets", "P2T")
  • To read out the target of an arc.

    >>> read_association_destination("models/my_pn", "p1_to_t1")

Service Management

service_register(name, function)

Registers the current client-side thread as a service in the Modelverse. The specified function will be executed when users make a request to the service specified by name. This function will be executed on a different thread for each incoming request for a new instance of the service. If required, synchronization must be built in manually. After making this call, the thread should stay alive, but can do whatever other operation is required. Note that, upon termination, this client MUST call the service_stop() function, as otherwise the Modelverse is not notified of this service deregistering.

The service is initially invoked with a Modelverse port to use for communication. This port must be used in all requests made to the Modelverse in the context of this service. The only two supported operations now are service_set (send data to the Modelverse) and service_get (fetch data from the Modelverse).


  • To register a Fibonacci service.

    >>> def fibonacci_service(port):
    ...     def fibonacci(value):
    ...         if value <= 2:
    ...             return 1
    ...         else:
    ...             return fibonacci(value-1) + fibonacci(value-2)
    ...     service_set(port, fibonacci, service_get(port))
    >>> service_register("fibonacci", fibonacci_service)

Stops the current service from being available to the Modelverse. Existing connections will not be forcibly terminated, and these run on separate threads.


  • To stop the currently executing service.

    >>> service_stop()

When running a serivce, it is often necessary to request data from the Modelverse. This data can be sent whatever way in the Modelverse. Different Modelverse executions are identified by their differing port, which is received upon the client invoking the external service. Note that this call is blocking. That is, only when data is available, will this function return.


  • To get data from a currently executing Modelverse client.

    >>> service_get(port)
service_set(port, value)

When running a service, it is often necessary to send data back to the Modelverse. This can be either for conversing with the user (e.g., prompting), or for sending the result back. Any primitive value can be sent to the Modelverse as a value.


  • To send the result back to the Modelverse.

    >>> service_set(port, 5)

Polls whether there is any input for the service we are hosting for communication handle port. This returns either True or False, to get the actual data, a service_get(port) is required. This function does not block when there is not data to be read.


  • To check whether there is any data for our port, when there is none.

    >>> service_poll(port)
  • To check whether there is any data for our port, when there is some.

    >>> service_poll(port)

Context Management

alter_context(model_name, metamodel_name)

Registers the model model_name to be interpreted in the context of metamodel_name as its metamodel. All future requests to the model will be made, using the assumption that the model is typed by the specified metamodel. When this is not the case, the request will throw an exception. Most of the time, this call must not be manually made, unless switching between metamodels, or when it is the first time using the model. The context is automatically deduced from the previous commands: adding the model with a specific metamodel, automatically puts the model in that context. When the model is opened for the first time, and it was not created by the client itself (e.g., by someone else, or through a model transformation), the metamodel needs to be specified.


  • To change the default metamodel of PetriNets to Bottom.

    >>> alter_context("formalisms/PetriNets", "formalisms/Bottom")

Clears all previously registered context models. This is useful for when a model is removed automatically, without the knowledge of the client. When the context is reset, no metamodel is stored, and opening a model will then cause searching for the registered metamodel. Note that if a model still has an old metamodel registered, this might cause exceptions when opening the model, since the old metamodel is tried.


Below is a list of all exceptions that the wrappers can raise, together with a summary of the kind of error that occured. For each exception, its superclasses are also indicated after the colon.

exception ModelverseException

Generic Modelverse Exception, of which all others inherit. This should be the only type of exception that the wrapper can raise. General Python exceptions could theoretically still occur, though this likely is a bug in the wrapper. This exception itself will never occur: it is abstract.

SuperclassAttribute : ModelverseException

The attribute that is being modified, was defined in a superclass, even though it is also available for this class. To prevent unexpected changes, the change to the attribute is disallowed and should be invoked on the superclass that actually defines the attribute.


  • Modifying the type of the weight attribute is disallowed, as the attribute was defined on an abstract class for both the P2T and T2P associations.

    >>> attribute_type("formalisms/PetriNets", "P2T", "weight", "Integer")
CallbackOnEmptySignature : ModelverseException

An activity is being added which has an empty signature for input and output metamodels, while it still defines an operation on the merged metamodel. As there is no merged metamodel, since there are no models being manipulated, it is impossible to perform the specified operation


  • When adding a manual operation without signature, but providing an operation

    >>> def operation(model):
    ...     pass
    >>> transformation_add_MANUAL({}, {}, "users/user/test/a", operation)
NotAModel : ModelverseException

The requested location does not contain a model, but is instead a folder, while a model was expected.


  • When creating a new instance of a “folder” metamodel

    >>> model_add("my_new_model", "formalisms")
UserNotInGroup : ModelverseException

The specified user is not part of the group, whereas it should be a member of this group.


  • When kicking a user from a group, while the user is not even a member.

    >>> group_kick("group_A", "user_B")
IncorrectFormat : ModelverseException

An incorrect format is specified for some input.


  • When setting the permissions of a model, this should be a string containing three characters representing either 0, 1, or 2.

    >>> permission_modify("formalisms", "003")
  • When querying for all transformations satisfying an input and output signature, they cannot both be empty, as that would return all transformations in the Modelverse.

    >>> transformation_between({}, {})
SignatureMismatch : ModelverseException

Generic exception for when the signature that is provided does not match the expected signature in the Modelverse. This is superclass for many more specific exceptions, but can also occur in its own right.


  • When executing a process with a model mapping that contains an entry that is not used by the process.

    >>> process_execute("users/user/test/empty_process", {"a": "models/a"})
EmptySignature : SignatureMismatch

Adding a model transformation with an empty input and output signature is not allowed, as no metamodel can be determined for RAMification and future execution.


  • When adding an empty model transformation.

    >>> transformation_add_MT({}, {}, "users/user/test/a", "...")
SourceModelNotBound : SignatureMismatch

Executing an activity with an incomplete input signature.


  • When executing a reachability analysis activity, without specifying the petri net model as input.

    >>> transformation_execute_AL("users/user/reachability_analyse", {}, {"reachability_graph": "users/user/reachability_graph"})
TargetModelNotBound : SignatureMismatch

Executing an activity with an over-specified output signature.


  • When executing a reachability analysis activity, a second (undefined) output key is specified in the invocation.

    >>> transformation_execute_AL("users/user/initialize_pn", {}, {"pn": "users/user/pn", "reachability": "users/user/reachability"})
DifferingModelsForKey : SignatureMismatch

When the input and output signature of an activity are in conflict, because a key has a different type in the output than the input.


  • If the key A switches type in the input and output signature of a to be created activity.

    >>> transformation_add_MANUAL({"A": "users/user/A"}, {"A": "users/user/B"}, "users/user/activity")
TypeMismatch : SignatureMismatch

The type of a model in the input or output signature does not match with the expected type.


  • When executing an activity that took an instance of “formalisms/PetriNets” as input, gets an instance of “formalisms/ProcessModel” instead.

    >>> transformation_execute_AL("users/user/reachability_analyse", {"PN": "users/user/my_pm"}, {})
UnknownError : ModelverseException

Unknown exception has occured. This is likely something wrong with the connection, such as the Modelverse that suddenly disconnected, or the request timed out. Note that this exception cannot be raised during init, as then the connection is nicely wrapped in a ConnectionError.


  • When the Modelverse is suddenly killed during execution, while there was an outstanding request.

    >>> element_list("formalisms/PetriNets") # <-- Modelverse killed during execution
UnknownM3 : ModelverseException

The M3 level of the model could not be determined, indicating that the metametamodel is not recognized as such.


  • When instantiating a model at the M1 level again

    >>> model_add("M2", "formalisms/SimpleClassDiagrams")
    >>> model_add("M1", "M2")
    >>> model_add("wrong", "M1")
UnknownIdentifier : ModelverseException

The specified identifier could not be resolved in the Modelverse. This is an abstract exception, for which several specializations exist. The exception always contains the identifier causing the problem, as there might be multiple identifiers used in a single request.

CompilationError : ModelverseException

Error in the HUTN compiler during compilation. This is mostly caused by a malformed expression in the specified code. The compilation error is contained in the content of the exception.


  • When assigning a code block which cannot be parsed as action language.

    >>> attr_assign_code("models/my_pn", "p1", "tokens", "1")
    CompilationError("Parsing error at line 1: ...")
NotAnActivity : ModelverseException

An activity model was expected, but the provided model was not an executable activity.


  • When an ordinary model is passed to the transformation execution.

    >>> transformation_execute_AL("formalisms/PetriNets", {}, {})
NotAProcess : ModelverseException

A process model was expected, but the provided model was not typed as such.


  • When executing an ordinary model as if it were a process.

    >>> process_execute("formalisms/PetriNets", {})
NotAValidProcess : ModelverseException

A process model was expected, and received, but it did not completely conform to the metamodel. For example, some constraints were not valid, no initial node was found, and so on.


  • When a process model is being executed that has not a single “Start” instance.

    >>> process_execute("models/my_empty_pm", {})
UnknownAttribute : UnknownIdentifier

The specified attribute does not exist for this element. While the attribute might exist as a type for the lower meta-level, this only looks at the current level.


  • When assigning a non-existing attribute.

    >>> attr_assign("models/my_pn", "p1", "capacity", 2)
UnknownElement : UnknownIdentifier

The specified model element is unknown in the model.


  • When instantiating an element of which the type element does not exist.

    >>> instantiate("models/my_pn", "CapacityConstrainedPlace")
UnknownModel : UnknownIdentifier

The specified model can not be resolved in the Modelverse. While the model might technically exist, this exception merely indicates that it cannot be referred to with the specified identifier.


  • When trying to execute a non-existing transformation.

    >>> transformation_execute_MT("models/pn_optimize", {"pn": "models/my_pn"}, {"pn": "models/my_optimized_pn"})
UnknownLocation : UnknownIdentifier

The specified location could not be resolved, indicating that no element exists at that location. Note that there is no information as to the level at which the resolution goes wrong (e.g., a parent folder might already not exist).


  • When reading out the permissions of a non-existing location.

    >>> read_permissions("a/b/c/d/e/f/g")
UnknownGroup : UnknownIdentifier

The specified group does not exist.


  • When deleting a group that doesn’t exist.

    >>> group_delete("no_such_group")
UnknownUser : UnknownIdentifier

The specified user does not exist.


  • When making a (non-existing) user join a group.

    >>> group_join("existing_group", "non_existing_user")
ConnectionError : ModelverseException

Error during initialization of the connection to the Modelverse. The actual error is enclosed in the exception content. Despite possibly multiple errors, the init call will try for at least the amount of time specified in the timeout. This is done as the Modelverse might still be booting up and is not yet listening to incoming connections.


  • When trying to connect to a server which doesn’t exist.

    >>> init("http://www.modelverse.be")
ExistsError : ModelverseException

Abstract exception that indicates that a requested element to be created already exists. Never occurs on its own.

AttributeExists : ExistsError

A to-be-created attribute already exists, and could therefore not be created (or renamed to this).


  • Renaming an attribute to one that already exists.

    >>> attribute_name("formalisms/PetriNets", "Place", "tokens", "name")
ElementExists : ExistsError

A to-be-created element already exists, and could therefore not be created.


  • Instantiating another element with ID Place, which already exists.

    >>> instantiate("formalisms/PetriNets", "Class", ID="Place")
FolderExists : ExistsError

The folder you want to create already exists.


  • When creating a folder that already exists.

    >>> folder_create("formalisms")
GroupExists : ExistsError

The group already exists.


  • When creating a new group that already exists.

    >>> group_create("users")
UserExists : ExistsError

The user already exists or is already a member of the group.


  • When adding a user to a group, while the user is already in there.

    >>> group_join("users", "user")
PermissionDenied : ModelverseException

Permission denied to a specific artefact. This is an abstract exception that is further subclassed by other exceptions.

ReadPermissionDenied : PermissionDenied

Current user has no permissions to read the specified model or folder.


  • When reading the administration model, for which we have no permission as it contains user password hashes.

    >>> element_list("administration/core")
  • When instantiating a non-readable metamodel, as we require access to the enclosed information.

    >>> model_add("my_model", "non-readable-model")
  • When listing a folder that we do not have read permissions for.

    >>> model_list("users/admin/")
WritePermissionDenied : PermissionDenied

Current user has no permissions to write to the specified model or folder.


  • When writing changes to a core formalism, which ordinary users cannot modify.

    >>> instantiate("formalisms/SimpleClassDiagrams", "Class")
  • When creating a model in a non-writeable folder.

    >>> model_add("formalisms/abc", "formalisms/SimpleClassDiagrams")
ExecutePermissionDenied : PermissionDenied

Current user has no permissions to execute the specified activity or process. For now, execute permissions equal read permissions, although this is prone to change in the future.


  • When executing a model transformation that we are not allowed to execute.

    >>> transformation_execute_MT("users/admin/my_transformation", {}, {})
UserPermissionDenied : PermissionDenied

Current user has no permission to make changes to the specified user or to the permissions of models (which can only be modified by the owner).


  • When changing the password of another user.

    >>> user_password("admin", "other_one")
  • When modifying the permissions of a non-owned model.

    >>> permission_modify("formalisms/SimpleClassDiagrams", "222")
GroupPermissionDenied : PermissionDenied

Current user has no permission to modify properties of the specified group.


  • When adding a member to a group that we are not an administrator of.

    >>> group_join("some_group", "other_user")
AdminPermissionDenied : PermissionDenied

Current user has no administrator privileges and cannot change the administrator level of another user.


  • When promoting another user as admin, while we are not an administrator ourselves.

    >>> admin_promote("other_user")
UnknownMetamodellingHierarchy : ModelverseException

The requested metamodelling hierarchy could not be resolved. This is likely because no typing relation could be found between the specified model and metamodel, or if this typing relation is only a partial mapping that cannot be automatically resolved. Note that this exception should never occur when opening the model using formalisms/Bottom.


  • When opening an arbitrary model as a SimpleClassDiagrams model.

    >>> alter_context("models/my_pn", "formalisms/SimpleClassDiagrams")
    >>> element_list("models/my_pn")
NotAnAssociation : ModelverseException

The requested element is not an association or link, although it should be.


  • When instantiating an association with a type that is not an association.

    >>> instantiate("models/my_pn", "Place", edge=("p1", "p2"))


An SCCD interface is created, which has exactly the same interface. Actually, the Python interface presented before is built using this SCCD interface. The operations are identical and have the same signature, although they are invoked differently.

To make a request to the Modelverse, an event has to be raised called action. The first parameter of this event is the operation to invoke, as a string (e.g., "read_info"). The second parameter specifies an ID that can be used in the response event to identify which event this is the response to, or set to None. The third parameter is a list of all the parameters to this function (e.g., ["my_pn"]). For example:

<raise event="action">
    <parameter expr="'read_info'"/>
    <parameter expr="None"/>
    <parameter expr="['my_pn']"/>

When this request is processed, the Modelverse will reply with the mv_result event, containing the result. The first parameter is the ID passed on in the original request, or None if the ID was set to None. The second parameter contains the result. It is possible to wait on this event as follows:

<transition event="mv_result" target=".">
    <parameter name="ID"/>
    <parameter name="result"/>
        print("Response: " + str(result))

If something went wrong, the event mv_exception will be raised instead:

<transition event="mv_exception" target=".">
    <parameter name="ID"/>
    <parameter name="exception_name"/>
    <parameter name="exception"/>
        print("Exception: " + str(exception_name))

The use of the SCCD interface is particularly interesting in case asynchronous communication is required. In contrast to the Python interface, requests don’t block, making it possible to do additional computations in the background, or even work in a multi-threaded context (using the ID parameter). Note, however, that the SCCD interface is more difficult to use with callbacks and other statecharts, as you will have to manually make the linkage with ports. For this, we refer to the implementation.


Other wrappers can be made as desired, in whatever language required. This is due to the fact that the Modelverse communicates only through XML/HTTPRequests. As such, all languages that support this, can simply mimic the interface used by any of the implemented wrappers. We refer to the Python wrapper as the reference implementation.