Code Generation and Export
An overview of functionalities available through the Code Generation and Export dialog box.
This dialog box provides access to code generation functionalities for different targets, and for each target, the corresponding code generation parameters, as defined by you..
In all cases, you must use the Super Bock full name parameter to select the super block for which code is generated. The super block must be present in the current model. The super block must be designated by its full name in the model. To do this, select the super block in the diagram and click Get Selected Block.
Note: The code generation must be applied to a super block. It cannot be applied to a model. If code generation is to be applied to the full model, its top diagram must first be placed in a super block and the code generation must be applied to the super block. The use of the Super Block menu facilitates this operation.
Code Generation Technologies
Two code generation technologies are available in Activate for different targets.
The standard code generator produces a C code relying on the functions associated with Activate blocks used during simulation. The code produced by this code generator realizes a behavior that is similar, if not identical, to the behavior of the original super block during simulation. The generated code consists of a series of function calls to block simulation functions, so it is dependent on several Activate core libraries. It is also not optimized or inlined. The dependency is not an issue if the resulting code is used within the Activate environment, but this code generator is not adapted to exporting code to other environments.
The inlined (or P) code generator is an alternative to the standard code generator. The code produced by P is highly optimized. The code is generated by considering the model parameter values. In particular, the generated code is specialized by using information on signal types and sizes, and constants are propagated as much as possible. This implies that, even though model parameters can be exposed by the generated code, such parameters cannot modify the types and sizes of the internal signals. P code generation is used for most applications where the generated code is to be exported to an outside environment, not only because the code is highly optimized but mainly because the generated code has virtually no dependency.
Supported Targets
The Code Generation and Export dialog box has the following targets: Activate Block, FMU, Host Standalone, Python, and Embed Block.
Activate Block
Code generation for this target creates a new Activate super block and replaces the selected super block with this new super block.
The generated super block contains mainly a CCustomBlock, which realizes the dynamics of the system corresponding to the content of the original super block. Other blocks, which might be present in the generated super block, are used to activate the CCustomBlock in a way that its behavior in Activate matches that of the original super block.
Both the standard and P code generators can be used for this target. The choice of the code generator depends on the application. When code generation is used to generate a new block for Activate (for performance or hiding the content), the standard code generator can be used since the library dependency of the generated code is not an issue. This code generator is used for simulation when a block is declared Atomic. But when code generation is used to test the generated code in the Activate environment (in the original model) before exporting the code, P code generation should be used since exported code, for the most part, is generated using P.
Several parameters can be set to impose the desired behavior on the generated code.
FMU
The FMU target produces code following the FMI 2 and 3 standards, both of ME (Model Exchange) and CS (Co-Simulation) types. Both code generation technologies can be used for this target but in most cases, P is used because it has no or little dependencies.
The FMU target has specific parameters. The FMU type, version number, and the name of the FMU file can be selected. Optionally the C source of the generated code can also be included in the FMU file.
Other parameters, available also for other targets, will be present later in this document.
Host standalone
For this target, a main file is generated on the host (Windows or Linux). The generated main file should be considered an example and must be adapted by the user to the specific application. In this file, the inputs of the system are obtained from the console and the outputs are written to the standard output. In most cases, it suffices to replace these read and write functions for specific applications.
The dynamics of the system is generated in a file called body.c. This file exports callback functions, which are used by different interfaces for different targets. The callbacks provide functions for taking a time step, initialization, and termination. If the system contains continuous-time dynamics and the embedded solver option is not selected, a time step function, step0, is provided to advance time in the absence of discrete events, up to the specified time. This is done using an explicit Euler solver. Depending on the number of different events through which the super block is activated, discrete-time step functions step1, step2…, are also provided.
There is no reason to edit the body.c file. The modifications for specific applications are done by adapting the interface functions, in this case, the main.c function.
A makefile is also generated for this target for creating the corresponding executable main.exe. Only P code generation is proposed for this target.
The folder where the generated files should be placed is a code generation parameter. Other parameters, common to other targets, are discussed later.
Python
Unlike what the name of the target might suggest, the main generated code is not in Python. The same body.c, used for Host standalone target and Embed Block target, is generated. However, an interface Python code is also provided to give access to exposed functions in Python language. The Python export is designed primarily for using Python’s specialized libraries for machine learning. It assumes that the Activate model is a continuous-time dynamical system. As for the Host standalone target, it implements an Euler solver and advances time based on that. Its support for discrete-time activations is mainly for resetting and restarting the execution.
Python target only uses the P code generation technology.
The folder where the generated files should be placed is a code generation parameter. Other parameters, common to other targets, are discussed later.
The generated files in the selected folder include a Python file named after the name of the super block for which code is generated with extension .PY. This file defines four Python functions to be used in Python to perform simulation:
- init(): This function should be called once at the beginning to dynamically link the generated C code with Pyton.
- finish(): This function is called once at the end to remove the link.
t, outputs = step(inputs, t, dt)
: This advances time to t using an Euler solver with step size dt.outputs = reset(inputs, t)
: When the super block for which code is generated has an activation input port, this function performs the actions associated with the corresponding activation after advancing time to t (taking a step to time=t). This is primarily used with t=0 to restart a simulation for usage with machine learning libraries. It can also be used to implement discrete-time event actions at other time instants.
The inputs and outputs represent Python arrays of inputs and outputs of the block. Their elements are numpy arrays representing individual inputs and outputs.
In common usage, the step function is used to advance time and reset is used with t=0 to restart the simulation. However, for discrete systems, reset can be used to update the system states and outputs at activation times. For example, if the embedded solver option is used when generating code for the Python target for a super block named SuperBlock, the function reset provided in the file SuperBlock.py realizes the complete dynamics of the system.
The generated Python code can be tested in the Activate environment using the PyCustomBlock. In this block, the simulation function of the block is defined in Python. In the case of the SuperBlock code discussed above, if the super block had one input and one output, the code can be tested in an Activate model as in the following image:
The period of the sample clock corresponds to the step size of the embedded solver. The simulation code of the block can be expressed as:
import hwx.activate.apis
import numpy
import sys
sys.path.append("SuperBlockFolder")
import SuperBlock
def PyBlockFunction(block,flag):
apis = hwx.activate.apis
u1=apis.vssGetInputData(block,1)
nevprt=apis.vssGetEventCode(block)
if flag == apis.vssBlockInitializeFlag():
SuperBlock.init()
elif flag == apis.vssBlockReinitializeFlag():
pass
elif flag == apis.vssBlockTerminateFlag():
SuperBlock
elif flag == apis.vssBlockOutputUpdateFlag():
y1 = SuperBlock.reset([u1], apis.vssGetTime(block))
apis.vssSetOutputData(block,1,y1[0],apis.vssGetOutputDataType(block,1))
The Generate package option generates a python package that builds a C/C++ extension via its setup.py script. To automatically compile the extension, install the package using the following:
import hwx.activate.apis as apis
apis.install_package('path to package')
After installation, you can run the
test run 'path to package'/test/test
.Embed Block
With this target, an Embed block, to be used in the Altair Embed product, is produced. The Embed block can readily be loaded in Altair Embed and used both for simulation and code generation. The code uses the same body.c used in other targets for initialization, termination, and taking time steps. Only discrete-time systems can be exported to Embed so diagrams that contain continuous-time dynamics should be exported with the embedded solver option. The exported block is a discrete-time Embed block.
The Embed Block target only uses the P code generation technology.
The folder, where the generated files should be placed, is a code generation parameter. Other parameters, common to other targets, are discussed later.
Code Generation Parameters
Parameters common to multiple targets are presented here.
Force Atomic Property
This property specifies the way the content of the block should be activated. In most cases, this option must be selected to generate a compact code. With this option, a discrete-time activation is considered for each activation input port of the super block. The corresponding external events are considered asynchronous. Without the atomic option, a discrete-time activation is considered for each possible combination of input activations. The super block has n ports, 2 to the power n minus one different codes are generated. An activation input port is also added to the existing activation ports for each regular input not declared always active (this property is discussed below). Except in exceptional situations for the Activate Block target, the atomic property must be forced.
Force inputs to always active
When code is generated for a super block, some of the properties of its ports are obtained from its configuration in the model environment. The port types and sizes are obtained from the environment. These properties can also be defined as block parameters of the corresponding block ports inside the super block.
Information on whether the inputs are continuous-time signals (always active signals), however, cannot be automatically obtained from the environment. This information can be defined for each input port as a block parameter of the corresponding block port inside the super block (time dependency property of the block). If all the super block inputs are always active, instead of setting the time dependency of all the input block ports, you can set the code generation Force input to always active property.
When the inputs are always active (continuous-time), it is important to set this property to reduce the complexity of the generated code.
Force embedded solver
With this option, the numerical solver algorithm used for the continuous-time dynamics of the system is included in the generated code. The result is a discrete-time system activated by a periodic clock with period corresponding to the solver time step.
When this option is selected, embedded solver parameters, time step and the choice of the solver, can be defined in the Embedded solver tab of the GUI. The choice to use an embedded solver and its parameters can also be defined inside the model. In that case, different embedded solver can even be used for different super blocks.
The embedded solver can be used both with the standard and P code generators. However, it is mainly used with the P code generator. The embedded solver option provides more solver choices than the Euler solver used when this option is not selected. With this option, the P code generator supports more blocks than without this option. So, even if the model has no continuous-time dynamics (no solver to implement), it is recommended to use this option.
To apply the embedded solver, the super block should not have external activation ports. Discrete-time activation sources, for example, sample clocks, can be used inside the super block.