Skip to content

Recommended Workflow

Tyler edited this page Nov 3, 2020 · 1 revision

Recommended Workflow

There's a few tips you can use to optimize your usage of Pypeline.

On calling defined objects (functions, classes, etc) / using local imports

While Pypeline allows you to seamlessly execute Python code inside AnyLogic, it should ideally be used in a way that most of your algorithms and function definitions are written outside of AnyLogic.

The reasoning for this is for both organizational purposes and to be able to effectively debug your code outside of AnyLogic. Writing the bulk of your Python code in a standard Python IDE also allows you to utilize code completion and macros.

To elaborate with an example, let's say you have a list of numbers and you want to get the indices less than a given value. For example, given the list: [1, 2, 9, 4] and the value 5, the function would return the list [0, 1, 3]. Creating this function directly inside AnyLogic using Pypeline would look like the following:

Using run for multi-line function definition

While this is valid and works okay for small examples, there is a more streamlined, flexible, and easier-to-manage alternative: any Python file in your model's directory can be imported like any global library. For this example, I created a new file in my model's folder called "helper.py" (as seen below):

Model directory with py file

Inside this file is the function I had to previously pass as strings:

Contents of helper.py

In lieu of the function definition placed on my model's startup, I can now directly import the function from the file, like so:

Using run to import

Now, this function is called in exactly the same way as with the string-defined version.

This is possible because in addition to being able to import built-in and installed libraries, Python also supports local imports. For more on the import system, you can read the official Python documentation. For a more beginner-friendly explanation, see "The Definitive Guide to Python import Statements".

On passing data to Python

In most situations, you will want to pass data from your model to the active Python environment. As Pypeline works by communicating via strings, any data will need to be converted to strings. There are two primary ways to go about doing this:

  1. Using the string concatenation operator (+)
  • When Java detects you are using the plus operator between a string and non-string variable, it will automatically convert the non-string variable.
  • For example, say you have a variable of type double defined in AnyLogic called "al_value", and you want to create a Python variable called "py_value" assigned to the same number. Wherever you want this action to take place in your AnyLogic model, you would have the code: pyCommunicator.run("py\_value = " + al\_value);
  1. Using the function String.format
  • This is a built-in Java function that allows you to first specify a string with placeholders and then pass the objects to replace the placeholders with as additional arguments. When printing to the console or a file, the placeholders can be configured to perform rounding, alignment, padding, and more to the values passed. Because none of these are relevant, the only placeholder needed to be remembered is "%s" (where s = string). The same example used in #1 above can be written as: pyCommunicator.run(String.format("py\_value = %s", al\_value));

Note: For some data types, simply specifying the variable may not convert it as intended. This happens when the data type does not override a function called toString. Two common examples of this are primitive arrays and Agents. Directly passing primitive Java arrays will output seemingly garbage text (e.g., [D@3343c8b3).

Additionally, AnyLogic agents by default will just output their internal ID number. Primitive arrays can be formatted by calling the built-in function Arrays.toString (or for multi-dimensional arrays: Arrays.deepToString).

For custom AnyLogic agents, you can create a toString function that returns a string; you can have one auto-generate for you by going in the agent's properties and clicking the "Create toString() functions with parameters" button at the bottom, under the Advanced section.

Another method for agents (or populations) would be to use JSON. More information is available on the advanced usage page.