Hey everyone,
I should have started posting updates for GSoC Working Period a week ago, but some difficulties in grasping how to do things properly, along with some of my academic research tasks, prevented me from having something worth publishing. I apologize for that.
On the bright side, when I decided to look for some material about Xcos blocks (what I should have done from the beginning), instead of trying to figure out everything by looking at the code, I quickly stumbled upon this document detailing pretty much anything I needed on Scilab’s side.
Scilab/Xcos block structure
As it turns out, the way each type of Xcos block operates is basically defined by 2 functions: a interfacing and a computational one. Both implement their respective standardized interfaces, in a way that the simulator handles any block as a black box with inputs and outputs, without being aware of its internals.
The interfacing function, always written in Scilab language, builds the pop-up window listing the modifiable properties for a given block. It also acts as a glue between generic simulation (time updates and numerical solvers) code and the specific computation code for that block. Its implementation for OpenModelica integration will be covered in more detail in the future.
(Properties window for "Random Delay" Xcos block)
Computational funtions could be written in either C or Scilab language, which defines the block type (4 or 5, respectively, with lower values being used for legacy block types). In the case of Modelica blocks, as there is no Scilab code generation available for OMCompiler, we must implement a C function with signature like:
void sim_func( scicos_block* block, int flag );
Where scicos_block is a generic block data structure type, declared in the header scicos_block4.h:
typedef struct
{
int nevprt; // Binary coding of activation inputs, -1 for internal activation
voidg funpt; // Pointer to the computational function
int type; // Type of the computational function, in this case type 4
int scsptr; // Not used for C programming
int nz; // Size of discrete-time state vector
double* z; // Vector of discrete-time state
int noz; // Number of object states
int* ozsz; // Vector of sizes of object states
int* oztyp; // Vector of data types of object states
void** ozptr; // Table of pointers to object states
int nx; // Size of continuous-time state vector
double* x; // Vector of continuous-time state
double* xd; // Vector of the derivative of the continuous-state, same size nx
double* res; // Only used for internally implicit blocks, vector of size nx
int *xprop; // Pointer to the continuous state properties register
int nin; // Number of regular input ports
int* insz; // Vector of sizes of regular input ports
void** inptr; // Tables of pointer to the regular input ports
int nout; // Number of regular output ports
int* outsz; // Vector of sizes of regular output ports
void** outptr; // Tables of pointers to the regular output ports
int nevout; // Number of event output ports
double* evout; // Delay time of output events
int nrpar; // Size of real parameters vector
double* rpar; // Vector of real parameters
int nipar; // Size of integer parameters vector
int* ipar; // Vector of integer parameters
int nopar; // Number of object parameters
int* oparsz; // Vector of sizes of object parameters
int* opartyp; // Vector of data types of object parameters
void** oparptr; // Table of pointers to the object parameters
int ng; // Size of zero-crossing surfaces vector
double* g; // Vector of zero-crossing surfaces
int ztyp; // Boolean, True only if the block may have zero-crossing surfaces
int* jroot; // Vector of size ng indicates the presence and the direction of the crossing
char* label; // Block label
void** work; // Table of pointers to the block workspace (if allocation done by the block)
int nmode; // Size of modes vector
int* mode; // Vector of modes
}
scicos_block;
The integer flag informs the current calculation stage, as our function can be called many times during a single simulator iteration, for different purposes. Each stage flag value is indicative which fields from the scicos_block parameter should be considered as inputs, and how the function is expected to fill the outputs:
flags | inputs | outputs | description |
---|---|---|---|
0 | t, nevprt, x, z, inptr, mode, phase | xd, res | compute the derivative of continuous time state |
1 | t, nevprt, x, z, inptr, mode, phase | outptr | compute the outputs of the block |
2 | t, nevprt>0, x, z, inptr | x, z | update states due to external activation |
2 | t, nevprt=-1, x, z, inptr, jroot | x, z | update states due to internal zero-crossing |
3 | t, x, z, inptr, jroot | evout | program activation output delay times |
4 | t, x, z | x, z, outptr | initialize states and other initializations |
5 | x, z, inptr | x, z, outptr | final call to block for ending the simulation |
6 | t, nevprt, x, z, inptr, mode, phase | x, z, outptr | reinitialization (if needed) |
7 | only used for internally implicit blocks | ||
9 | t, phase=1, nevprt, x, z, inptr | g, mode | compute zero-crossing surfaces and set modes |
9 | t, phase=2, nevprt, x, z, inptr | g | compute zero-crossing surfaces |
10 | t, nevprt, x, z, inptr, mode, phase | res | compute jacobian matrix |
Hooking things up
Currently in Scilab, its customized modelicac compiler is able to generate an implementation of the computational function directly from Modelica code. For OMCompiler, one is only able to generate a FMI2 interface implementation, so some generic wrapper code is needed.
From now on, I plan to (as soon as possible) start writing this wrapper and showing you how FMI2 calls are performed for each one of the simulation stages.
Thanks for sticking with me one more time. See Ya !