Python 3 Scripting Extension inside Dynamics Content
This document describes how to extend and add additional functionality to a dynamics content by using the built in scripting extension.
- 1 Supported Use Case
- 2 Basic Concepts
- 3 Workflow
- 3.1 Setting the Python Interpreter
- 3.1.1 Python 3 Environment Parameters
- 3.1.1.1 Interpreter
- 3.1.1.2 Shared Python Modules
- 3.1.1.3 Python Module Search Paths
- 3.1.2 Embedded Dynamics Scripts
- 3.1.3 External Modules
- 3.1.1 Python 3 Environment Parameters
- 3.2 Adding a Dynamics Script extension to your content
- 3.3 Adding Fields
- 3.4 Writing a Dynamics Script's Code
- 3.4.1 Available Callbacks
- 3.4.1.1 Code template
- 3.4.1.1.1 Template
- 3.4.1.1 Code template
- 3.4.2 Available helper functions to write scripts
- 3.4.2.1 The Application Context
- 3.4.2.1.1 The Application Context
- 3.4.2.2 VxMath::Transformation
- 3.4.2.2.1 Transformation Helpers
- 3.4.2.3 Accessing Contact Data
- 3.4.2.3.1 Accessing Contacts
- 3.4.2.3.2 Contact Data
- 3.4.2.3.3 Part Data
- 3.4.2.1 The Application Context
- 3.4.3 Getting the full Vortex.py documentation
- 3.4.1 Available Callbacks
- 3.5 Using the Python Output Window
- 3.6 Example: Writing a simple script to automatically change the gears of a car
- 3.1 Setting the Python Interpreter
- 4 Accessing an Extension's Fields via Python
- 4.1 control.py
- 5 Converting Python 2 scripts to Python 3 Dynamics Script
- 6 Reference Information
Supported Use Case
The Dynamics Script Extension (Python 3) allows you to use the Python 3 programming language to modify the behaviour of dynamic elements of your simulation while it is running.
For example, if a mechanism consists of two parts that are constrained by a hinge, adding tension to that mechanism will not cause the hinge to break. However, a simple script that reads the tension and disables the constraint if it reaches a certain threshold will accurately simulate breaking the hinge.
A Dynamics Script is part of your content. The extension can be added to either a scene, mechanism or assembly document.
Basic Concepts
Python Interpreter Support
The Dynamics Script extension makes it easy to work with a Python 3 interpreter installed on your system. Vortex Studio comes bundled with a default interpreter but if you want to use third party packages or a different Python distribution you should point your Vortex application
towards an external interpreter that you previously installed.
The only restriction is that the supported Python version is Python 3.8. The default interpreter currently bundled with Vortex Studio is 3.8.6.
Workflow
(Optional) Setting the Python Interpreter
Adding a Dynamics Script extension to your content
Adding Fields
Writing a Dynamics Script's Code
Setting the Python Interpreter
Vortex provides a default Python Interpreter. If you need your own, you can specify it at the root level of a setup document, under the container Python 3.
Python 3 Environment Parameters
Interpreter
You can select which Python 3 environment you want to use on your system from the Setup document. Please note that these settings are only available on the root item of your Setup document.
By default, the "Interpreter Directory" parameter is not specified. In this case, a version of Python 3 packaged with Vortex Studio will be used(you can find it under "C:\CM Labs\Vortex Studio 2020b\resources\Python3DefaultInterpreter").
If a directory is specified, Vortex Studio will use the Python 3 environment in this directory. Please note that the given directory must point to the directory where "python.exe" can be found.
The current recommended Python 3 interpreter is Python 3.8.6 64-bit, as Vortex Studio provides. Any distribution should be compatible as long as it uses Python 3.8 64-bit.
However, there may be incompatibilities between Vortex Studio and some Python modules present in your Python distribution.
Shared Python Modules
After specifying your preferred Python 3 environment, you can also specify Python modules that you want to share between all Python 3 scripts running in Vortex Studio. This can be done by using the "Shared Python Modules" parameter.
The Vortex module is always shared, but additional user modules can be added to the list.
Adding modules to this list can save memory for heavy modules. It also provides a unique initialization entry point for some modules. For most Python modules, it is not necessary to add them in this "Shared Python Modules" parameter.
However, some Python modules cannot be imported, or cannot be imported for multiple Python sub-interpreters. It can result in a freeze of the application or even a crash. This is a limitation for some Python modules. In this case, it is needed to add this Python module in the "Shared Python Modules" parameter.
Here is a list of known Python 3 modules that must be added to the Shared Python Modules list before they can be used:
numpy
asyncio
doctest (relies on asyncio)
unittest (relies on asyncio)
Python Module Search Paths
By default, the Python 3 integration in Vortex Studio will look in the following directories when importing Python modules:
default sub-directories in the selected Python 3 interpreter's installation folder
Vortex Studio's "bin" directory
When developing content with Vortex Studio, it can be useful to share Python code between scripts. In this case, it is recommended to create a custom Python module where this reusable code can be defined. Here is an example of such a Python module:
custom_module
__init__.py
helpers.py
This folder structure represents a Python module named "custom_module" having a "helpers" script. This Python module can be packaged alongside the Vortex Studio content using it. Here is an example of DynamicsScript using "myTestFunction" defined in helpers.py:
from custom_module import helpers
def on_simulation_start(extension):
helpers.myTestFunction()
The path to the "custom_module" can be specified in the Setup document under the "Python 3" section under the field named "Python Module Search Paths":
Embedded Dynamics Scripts
Dynamics script extensions can be added to the content to modify dynamics behavior and will use the Python modules available in the Python 3 distribution specified in your Setup document.
External Modules
To use Python modules that are not included with the default version of Python installed with Vortex (for example, NumPy), you can install and use those modules from your system's Python interpreter instead. To do so, follow these steps:
Install the new module to your system's Python interpreter
Follow the instructions on this page in the section Setting the Python Interpreter to change the version of Python that Vortex will use to your system's Python interpreter
Check the list of modules on this page that must be added to the Shared Python Modules. If your module requires it, add it to the list. Failing to do so will result in Vortex freezing or even crashing.
Adding a Dynamics Script extension to your content
Prerequisites: If you need to use another Python 3 interpreter than the one bundled with Vortex Studio, follow the steps labeled Setting the Python Interpreter above.
Launch the Vortex Studio Editor.
Open your content file or create a new assembly, mechanism, or scene.
In the Toolbox panel, under the Scripting category, add a Dynamics Script extension.
In the Properties panel, set the path to the script you want to use to the parameter Script Path.
Your script will be automatically analyzed and any syntax error will be reported to you as warnings on the extension.
Every time, you edit and save your script in your code editor, it will be reimported into Vortex.
Adding Fields
The most direct way to communicate between Vortex and your Python script is to use fields (inputs, outputs, and parameters) on the Dynamics Script extension. Both the Vortex application and the Python script can access those fields.
But before accessing fields you must first add them to the extension.
Using the Vortex Studio Editor
Right-click on the dynamics script extension and select "Edit"
In the window that appears you can add inputs, outputs, and parameters to your script.
There are 3 field containers on each script,
Inputs
Outputs
Parameters
in Python access the fields using the syntax
"extension.inputs.field_name"
"extension.outputs.field_name"
"extension.parameters.field_name"
See the code below as an example of accessing fields created earlier.
Accessing fields in Python 3
def pre_step(extension):
# The boolean input field in the screenshot above is named "boolean input"
# Notice that spaces are replaced by underscores
my_field = extension.inputs.boolean_input
# my_field is a boolean input, you can read its value like so
bool_value_of_my_field = my_field.value
if bool_value_of_my_field:
extension.outputs.an_output_double.value = 3.1415
else:
parameter_number = extension.parameters.number.value
extension.outputs.an_output_double.value = parameter_number
Using Python Code only
Fields can be created directly from Python code if you prefer. The following code is functionally the same as the previous example.
Creating fields from a script in Python 3
import Vortex
def on_simulation_start(extension):
# Creating an input of type boolean
extension.createInput("boolean input", Vortex.Types.Type_Bool)
# Creating an output of type double
extension.createOutput("an output double", Vortex.Types.Type_Double)
# Creating a parameter of type double
extension.createParameter("number", Vortex.Types.Type_Double)
def pre_step(extension):
# The boolean input field we created on simulation start is named "boolean input"
# Notice that spaces are replaced by underscores
my_field = extension.inputs.boolean_input
# my_field is a boolean input, you can read its value like so
bool_value_of_my_field = my_field.value
if bool_value_of_my_field:
extension.outputs.an_output_double.value = 3.14159265359
else:
parameter_number = extension.parameters.number.value
extension.outputs.an_output_double.value = parameter_number
More information on creating and getting fields in Python
The functions createInput()
, createOutput(),
and createParameter()
are utility functions each encapsulating a frequent use case of this API. There are several things to mention about these functions.
The new functions have the following signatures
create field functions signatures
The available types for the parameter
type
are inVortex.Types
enumeration. If this parameter is an invalid value, this function will fail.The available types for the parameter
physicalDimension
are inVortex.VxPhysicalDimension
enumeration. By default, Vortex will assign a value corresponding toVxSim.VxPhysicalDimension.kNone
to fields whose physical dimensions are not defined. This means the functiongetPhysicalDimension()
will never returnNone
even if explicitly declared in the call tocreateInput()
,createOutput(),
orcreateParameter()
. This behavior also is consistent with theaddInput()
,addParameter()
,and addOutput()
functions.The functions
createInput()
andcreateParameter()
ignore the parameterdefaultValue
if the field already exists. This allows experimenting with different values in the editor's interface without having to change the script.The function
createOutput()
resets the output field's value todefaultValue
even if the field already exists. This behavior is different fromcreateInput()
andcreateParameter()
.
Each function follows the pseudo-code presented in the following code snippet.
create field functions pseudo-code
Writing a Dynamics Script's Code
The Dynamics Script extension supports the Python 3 programming language and gives the user access to all the rich features of this language.
Available Callbacks
Function | Description | Parameters |
---|---|---|
| Called when the application mode changes from editing to simulating. |
|
| Called when the application mode changes from simulating to editing. |
|
| Equivalent to IDynamics::preStep. Called before the collision detection and the dynamic solver. |
|
| Equivalent to IDynamics::postStep. Called after the collision detection and after the dynamic solver. |
|
| Equivalent to IDynamics::pausedUpdate. Called at every update in editing mode and when the |
|
| Equivalent to IExtension::onStateSave. |
|
| Equivalent to IExtension::onStateRestore. |
|
Code template
Below is a sample code that can be used as a template for any dynamics script extension.
Template
Available helper functions to write scripts
The Application Context
To get information relative to the application context, use getApplicationContext(). The method is available via the extension in dynamics Python scripts extension. Application context API is relatively simple and can be used for time/frame-based related logic.
The Application Context
VxMath::Transformation
A series of global helpers exist at the VxSim
level to simplify matrix computation. They are the equivalent of the C++ Global helpers in namespace VxMath::Transformation
.
Transformation Helpers
Accessing Contact Data
With Python 3, contacts generated during the simulation can now be accessed in a Dynamics Script Extension. By calling the function getDynamicsContacts() as shown below, the DynamicsContactCollection is returned. This structure has two useful member functions, __len__(), which returns the number of contacts, and __iter__() which implements a Python iterator on the collection. This can be used as shown below to examine various properties of each contact that has been generated.
Accessing Contacts
Two sets of get functions are available, the first set returns information relevant to the contact interaction, allowing examination of the position and normal direction, as well as the primary and secondary friction directions at the contact point.
Contact Data
The second set returns information for each part involved in the contact interaction, allowing examination of the contact force and torque on each part. These functions take a partID as an argument, which is just an integer labeling each part involved.
Part Data
Getting the full Vortex.py documentation
If you wish to see more of the Vortex API you can generate the pydoc. Simply open a command-line tool in the bin folder of your vortex installation and run the following command:
python -m pydoc -w Vortex
The file Vortex.html will be generated in the bin folder and you can browse an up-to-date list of all the functions available in the Vortex API.
Using the Python Output Window
In the editor, a new window was added to help you see what is happening in your Python scripts
The Python Output panel is a text box where the output of all of your scripts is written. Any print statement in your script will print in this window and any error in execution will also show.
Make sure that you turn on the listening mode:
Now if you introduce an error in your code
If you want to know which script is the culprit for the error, you can click the error message and then the "bulls-eye" to locate the extension with the error in the explorer tree or click the open source script icon to open the text file directly.
You can also use the print statement:
Example: Writing a simple script to automatically change the gears of a car
For convenience, this example starts with the sample file that you can find in this location: C:\CM Labs\Vortex Studio Content <version>\Samples\Vehicles\Car - Sedan\Car - Sedan.vxmechanism
Add a Dynamics Script to the mechanism
Next to the mechanism file, create an empty text file and name it control.py
In the toolbox, locate the Dynamics Script extension and add it to the mechanism
In the properties panel, set the Script Path to the empty script you created in step 1.
Now add inputs and outputs to the script extension. Find the extension in the explorer tab, right-click it → edit
Add an input
rpm - double
Add 3 outputs,
engine - boolean
throttle - double
gear - int
Add a parameter
redline - double
You should have something like this:
Press OK to confirm your fields
Now, your script extension should appear as follows:
Writing the code to start the car
Open the file control.py, that you created, with any code editor of your choice
Start by adding code that will be run when we start the simulation
control.py
Run the simulation, and see the output fields' values change
Nothing happens to the car!
We have to connect the outputs of the script to the vehicle interface
Add a connection container
connect all the fields of the dynamics script to the corresponding fields of the vehicle interface
Run the simulation again, the car moves and starts driving forward but it doesn't change gear, see the rpm maxed out at 6500:
Changing gears at runtime whenever we hit the rpm redline
Set the dynamics script redline parameter value to 6000
We will add the pre_step method, this callback is called before every physics update of the simulation
control.py
Press play and look at what happens
We gear up a lot as soon as we hit 6000rpm, the pre_step method is called 60 times per second by default, sometimes it's too quick for our needs. Something needs to be corrected so we don't shift up more than once before the rpm drops.
We want to add two things, first, our car has only 6 gears, so we should never gear up above this, second, we want to add a minimum time delay between each gear change
control.py
The final script should look like this:
control.py
Now, if you play the simulation, you will have a car that accelerates and gears up gradually, also you will find out that this sedan is very very fast easily reaching over 300km/h!
Accessing an Extension's Fields via Python
In some cases you may want to access a field, or a nested field of an extension via a Python script. An extension's inputs, outputs, and parameters can be accessed by passing their name (as a string) into one of the following functions:
extension.getInput('Example Name')
extension.getOutput('Example Name')
extension.getParameter('Example Name')
The value of that field can then be read or written to by accessing it like so:
extension.getInput('Example Name').value
In some cases, the field that you would like to access may be nested inside of another field. Fields function as containers, and you can access fields within them via an integer-based index or a string-based key that represents the field's name.
For example, here the "Number Of Cables" field is contained within the "Cables" output:
It can be accessed via the following syntax:
control.py
Converting Python 2 scripts to Python 3 Dynamics Script
If you have existing Python 2 scripts in your content you can easily convert it to a Dynamics Script in a few steps.
Convert the extension to Dynamics Script
The first thing you will want to do is use the "Convert to Dynamics Script" action in the editor.
To do so, right-click on the desired script:
The "Convert to Dynamics Script" action will change the extension type of the Python 2 script to the Python 3 dynamics script extension.
Doing this will keep all the fields, connections, and references of the extension.
The Convert to Dynamics Script action will have created a duplicate version of your script file with the suffix _DynamicsScript or if the script was embedded there will be a new .py file in the folder next to the current document.
Converting multiples scripts
The editor API does not permit converting multiple scripts at once. It can be done using the Python API. For more information, see Python 3 API: Upgrading Vortex Files
Converting Python 2 code to Python 3
Here is an example code conversion that covers most cases for Vortex scripts, all changes are highlighted
Python 2 script | Python 3 conversion |
---|---|
Converting Vortex script code
Change import VxSim to import Vortex
Change all uses of VxSim in the code to Vortex
Change the callbacks to their new names
(optional) Replace the
self
identifier withextension
We recommend changing self
to extension
, because self refers to the current instance of a class. However, in the case of Vortex scripts, what you receive in each callback is really the Vortex extension.
We also recommend not using global variables. It is generally bad practice and might not be supported in future versions. Instead, you should store all data in the extension that is given as a parameter in each callback.
This means replacing all uses of "global my_var" to "extension.my_var".
Note that we removed access to the "universe" parameter in the new script callbacks. This parameter was only used for rare cases such as the creation of Intersection Sensors and Sensor Triggers. In the Python 3 API, the universe is no longer required in this case. For more information and Python code examples refer to the Sensors documentation.
Converting generic Python code
This part is trickier, it depends on the complexity of your code, if your scripts are not too complex you probably do not require any code changes.
Here are two simple examples that you might see in your code:
The print statement requires parentheses in Python 3.
Integer division returns a float in Python 3, whereas it would always return an integer in Python 2
3/2 = 1 in Python 2
3/2 = 1.5 in Python 3
There are more differences between Python 2 and Python 3, but there are better resources online for this information.
Reference Information
See https://www.python.org/ for help getting started if you are new to Python.
See Integrating Vortex Studio using Python 3 for more documentation about scripting with Vortex