Hello there,

I was undecided about where to begin the posts about glueing FMI2 calls to the Scicos Block interface, mentioned in the last post, so I’ll take the most obvious route and start from the beginning…

… of block processing/simulation, I mean. And that is triggered by the Initialization flag (4):

flags inputs outputs description
... ... ... ...
4 t, x, z x, z, outptr initialize states and other initializations
... ... ... ...

The purpose of the Initialization job is more thoroughly described in the available documentation:

  • “Initialization: At the begining of the simulation, this job is called under flag=4 to initialize continuous and discrete states (if necessary). It also initialize the output port of the block. This function is not used in all of the blocks, it is used for blocks that needed dynamically allocated memory (the allocation is done under this flag), for blocks that read and write data from files, for opening a file, or by the scope to initialize the graphics window.”

So, aside from continuos/discrete state and output initialization (in arrays x, z and outptr, respectively), this flag also indicates the moment when all manual memory allocations (if needed) should be performed for a given block.

That’s our case, because FMI2 API involves allocation of components to hold internal state values.

But, as scicos_block is the only data structure provided at the computational function call, where will they be stored ?

Taking a look at scicos_block4.h header, it seems there is a field intended for this very role:

typedef struct
{
  /* ... */
  int nz;         // Size of discrete-time state vector
  double* z;      // Vector of discrete-time state
  /* ... */
  int nx;         // Size of continuous-time state vector
  double* x;      // Vector of continuous-time state
  /* ... */
  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
  /* ... */
  void** work;    // Table of pointers to the block workspace (if allocation done by the block)
  /* ... */
}
scicos_block;

Then, assuming that the work pointer is the one to be used, we initialized the block inside our Scicos/FMI2 wrapper:

#include <string.h>
#include <stdio.h>
#include <stdarg.h>

// Scilab Scicos block interface and definitions
#include "scicos_block4.h"
#include "scicos.h"
#include "dynlib_scicos.h"

// FMI2 API definitions
#include "fmi2TypesPlatform.h"
#include "fmi2FunctionTypes.h"
#include "fmi2Functions.h"

// Definitions based on model properties

#define BLOCK_FUNCTION_NAME /* ... */                      
#define MODEL_NAME /* ... */
#define MODEL_GUID /* ... */

static fmi2Real inputsList[] = /* ... */;
const static fmi2ValueReference INPUT_REFS_LIST[] = /* ... */;
static fmi2Real outputsList[] = /* ... */;
const static fmi2ValueReference OUTPUT_REFS_LIST[] = /* ... */;
static fmi2Real stateDetivativesList[] = /* ... */;
const static fmi2ValueReference STATE_REFS_LIST[] = /* ... */;
const static fmi2ValueReference STATE_DER_REFS_LIST[] = /* ... */;

// Simple terminal message logging function
static void print_log_message( fmi2ComponentEnvironment componentEnvironment, fmi2String instanceName, 
                               fmi2Status status, fmi2String category, fmi2String message, ... )
{
    va_list args;
    va_start( args, message );
    vprintf( (const char*) message, args );
    printf( "\n" );
    va_end( args );
}

// Utility function for setting Scicos block -> FMI2 model input
static void set_input( scicos_block* block )
{
    // Considering input only floating point continuous values
    double** u = (double**) block->inptr;
    // Set u inputs before calculation derivatives
    if( block->nin > 0 )
    {
        for( int i = 0; i < block->nin; i++ )
            inputsList[ i ] = u[ i ][ 0 ];
        fmi2SetReal( (fmi2Component) block->work, INPUT_REFS_LIST, block->nin, inputsList );
    }
}

// Utility function for getting FMI2 model -> Scicos block output
static void get_output( scicos_block* block )
{
    // Considering output only floating point continuous values
    double** y = (double**) block->outptr;
    // Retrieve output values
    if( fmi2GetReal( (fmi2Component) block->work, OUTPUT_REFS_LIST, block->nout, outputsList ) == fmi2OK )
    {
        // Setting outputs the same as continuous states
        for( int i = 0; i < block->nout; i++ )
            y[ i ][ 0 ] = outputsList[ i ];
    }
    
    // For now, we assume no discrete outputs
}

// The computational function itself
SCICOS_IMPEXP void BLOCK_FUNCTION_NAME( scicos_block* block, const int flag )
{
    static fmi2EventInfo eventInfo;

    switch( flag )
    {
        /* ... */
        
        // Flag 4: State initialisation and memory allocation
        case Initialization:                    
        {
            // User provided functions for FMI2 data management
            const fmi2CallbackFunctions functions = { .logger = print_log_message,
                                                      .allocateMemory = calloc,
                                                      .freeMemory = free, 
                                                      .stepFinished = NULL,
                                                      .componentEnvironment = NULL };
                                                      
            // Store FMI2 component in the work field     
            block->work = (void**) fmi2Instantiate( MODEL_NAME,            // Instance name 
                                                    fmi2ModelExchange,     // Exported model type
                                                    MODEL_GUID,            // Model GUID, have to be obtained
                                                    "",                    // Optional FMU resources location
                                                    &functions,            // User provided callbacks
                                                    fmi2True,              // Interactive mode
                                                    fmi2True );            // Logging On
            
            // Need to set input references based on model description (to be implemented)

            set_input( block );           // Set initial inputs
            
            // Define simulation parameters. Internally calls state and event setting functions, 
            // which should be called before any model evaluation/event processing ones
            fmi2SetupExperiment( (fmi2Component) block->work,              // FMI2 component   
                                 fmi2False,                                // Tolerance undefined
                                 0.0,                                      // Tolerance value (not used)
                                 0.0,                                      // Start time
                                 fmi2False,                                // Stop time undefined 
                                 1.0 );                                    // Stop time (not used)
              
            // FMI2 component initialization
            fmi2EnterInitializationMode( (fmi2Component) block->work );
            fmi2ExitInitializationMode( (fmi2Component) block->work );
              
            // Event iteration
            eventInfo.newDiscreteStatesNeeded = fmi2True;
            while( eventInfo.newDiscreteStatesNeeded )
            {
                // update discrete states
                fmi2NewDiscreteStates( (fmi2Component) block->work, &eventInfo );
                // if( eventInfo.terminateSimulation ) goto TERMINATE_MODEL;
            }
              
            // Enable iterative derivatives calculation and integration
            fmi2EnterContinuousTimeMode( (fmi2Component) block->work );
              
            fmi2GetContinuousStates( (fmi2Component) block->work, block->x, block->nx );
            
            get_output( block );          // Get initial outputs
              
            break;
        }

        /* ... */
    }

    return;
}

(Error handling is not shown to simplify visualization)

[De/Re]initialization

As we are describing initialization procedures for FMI2-based blocks, it makes sense to also talk about ending and reinitializing them:

flags inputs outputs description
... ... ... ...
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)
... ... ... ...

From the documentation:

  • “Ending: This job is called when flag=5 at the end. This case is used to close files opened by the block at the begining or during the simulation, to free the allocated memory, etc.”

  • “Reinitialization: This job is called under flag=6. In this case, the values of the inputs are available and the function create a fixed point iteration for the outputs of the block. On this occasion, the function can also reinitialize its initial states.”

/* ... */

void fmi2_block( scicos_block* block, const int flag )
{
    /* ... */

    switch( flag )
    {
        /* ... */
        
        // Flag 5: simulation end and memory deallocation
        case Ending:                    
        {
            // Get final state and output
        
            fmi2SetTime( (fmi2Component) block->work, get_scicos_time() );
            
            fmi2GetContinuousStates( (fmi2Component) block->work, block->x, block->nx );
            
            get_output( block );
            
            fmi2Terminate( (fmi2Component) block->work );       // Terminate simulation for this component
            
            // Deallocate memory            
            fmi2FreeInstance( (fmi2Component) block->work );
            
            break;
        }
        // Flag 6: Output state initialisation
        case ReInitialization:
        {
            // Set reinitialized model state and get output
            
            fmi2SetTime( (fmi2Component) block->work, get_scicos_time() );
            
            fmi2SetContinuousStates( (fmi2Component) block->work, block->x, block->nx );
            
            get_output( block );
            
            break;
        }

        /* ... */
    }

    return;
}

Be aware that this implementation is subject to change, as I better understand of how to deal with the API. But, for now, that’s all I have to show you.

Thanks for sticking with me one more time. See Ya !