Hey, here I am again !

After dealing with [de/re]initialization procedures for the Scicos/FMI2 wrapper block, I guess the most likely next step would be to deal with regular updates, dealing with integration of continuous states and setting block outputs (x and y vectors, respectively).

However, due to some confusion regarding how these things work in Scilab (that my mentor Clément is helping me to clear up), I started looking at state discontinuities before that.

Inside Scicos block computational function, those cases are handled by two types of jobs:

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

which could be related to FMI2 API state events.

State Events

Suppose that you simulating a bouncing ball model: every now and then, the continuous and smooth movement of the falling mass have to be abruptly changed on collision with the ground, by resetting its velocity to a ascending one.

One could detect these events by keeping track of a variable (indicator) that reaches zero (or has a sign change) on the discontinuity situation (like, in this case, ball height relative to the ground). That’s why those rough state changes are also known as zero-crossings.

(Domain/sign change for state event indicator z)

So, to trigger a non-integration state update (flag 2) we can verify zero-crossings in event indicator variables (on flag 9) after each integration step, as described in the available documentation:

  • States update: This job is called when flag=2, it is called only if the block has event input. In this case, the computation function update the value of the continuous or discrete state. This flag is called only if the block has an event activation (nevprt=0), or an internal zero-crossing (nevprt=−1). In the second case a vector jroot specifies which surface has been crossed and in which direction. for exemple, if the ith entry of jroot is equal to 0 it means that there is no crossing, whereas if it is equal to +1 (respectively −1), then the crossing is with a positive (respectivly negative) slope.

  • Mode and zero-crossing: This job is called when flag=9. In this case, we set the mode and evaluate the zero-crossing. To set the mode, information about the nonsmoothness of the model must be given to the solver.

And then we try to implement that in our code:

/* Includes and definitions */

SCICOS_IMPEXP void BLOCK_FUNCTION_NAME( scicos_block* block, const int flag )
{
    static fmi2EventInfo eventInfo;
  
    switch (flag)
    {
        /* ... */
  
        // Flag 2: Handle discrete internal events (implicit blocks do not handle external events for now)
        case StateUpdate:
         {
             // In the case of state update triggered by zero-crossing (indicated by block->jroot)
             // Redundant, as Modelica blocks have no external event inputs (block->nevptr > 0)
             if( block->nevprt == -1 )
             {
                 if( eventInfo.valuesOfContinuousStatesChanged == fmi2True )
                 {
                     // The model signals a value change of states, retrieve them
                     fmi2GetContinuousStates( (fmi2Component) block->work, block->x, block->nx );
                 }
             }
             
             break;
         }

        /* ... */
    
        // Flag 9: Zero crossing computation
        case ZeroCrossing:
        {        
            // Get event indicators. If sign changes for one of block->g vector values, block->jroot 
            // and block->nevptr will be automatically set before StateUpdate job
            fmi2GetEventIndicators( (fmi2Component) block->work, block->g, block->ng );
            // Inform the model about an accepted step
            // The second parameter informs that the model simulation won't be set to a prior time instant (fmi2False otherwise)
            int enterEventMode, terminateSimulation;
            fmi2CompletedIntegratorStep( (fmi2Component) block->work, fmi2True, &enterEventMode, &terminateSimulation );
            // if terminateSimulation then goto TERMINATE_MODEL
            // Consume FMI2 component internal events
            if( enterEventMode == fmi2True )
            {
                fmi2EnterEventMode( (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;
                }
                // Return to Continuous-Time Mode
                fmi2EnterContinuousTimeMode( (fmi2Component) block->work );
            }
            
            // In this case, we are not using block->modes vector, as discontinuities 
            // are already handled by the FMI2 component 
            
            break;
        }
    
        /* ... */
    }
}

Events Scheduling

As pointed in code comments above, Modelica blocks do not have to deal with an equivalent to Scicos blocks external event inputs and outputs:

(Xcos diagram showing the usage of event inputs and outputs, in red)

Inside a regular non-Modelica block, a external event input would be handled by StateUpdate job when its nevprt field has a value higher than 0 (which is ignored in our case), while the outputs would be generated during the Event Scheduler job (flag 3):

flags inputs outputs description
... ... ... ...
3 t, x, z, inptr, jroot evout program activation output delay times
... ... ... ...

Again, from documentation:

  • Events scheduler: This job is called when flag=3. In this case, the simulator updates the next event output time of the block.

The same way, as we also do not produce event outputs here, this job flag is just bypassed in our computational function.

That’s it for now. I guess I need to accelerate things a little now thar we’re approaching the first GSoC evaluation, so expect more posts soon.

As always, be aware that the implementation presented here is subject to change, if I get a better grasp of how things should work.

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