Baniere Saphir Control
http://www.saphir-control.fr/

bar


BACK

Scilab : un logiciel libre pour le calcul scientifique
7e Partie - Introduction à Scicos (2/2)



Tous ces articles en ligne sur Scilab ont été écrits pour LINUX MAGAZINE FRANCE par les développeurs du Scilab Group. Les articles sont sous licence FDL (Free Documentation Licence)

On a vu dans le numéro précédent l'utilisation de Scicos pour la construction de systèmes dynamiques sous forme schéma-blocs à partir des blocs existants. Dans ce numéro, on présente une façon de constuire de nouveaux blocs.

Les blocs prédéfinis dans les palettes standard de Scicos permettent de construire des schémas très divers mais, dans certains cas, une fonctionnalité décrite à partir de plusieurs blocs peut être réalisée par un seul bloc ce qui donne souvent une simulation plus efficace. On a déjà vu une façon de remplacer plusieurs blocs par un seul en utilisant le <<Super Block>>. Cela permet de construire des schémas hiérarchiques plus lisibles mais n'améliore en rien l'efficacité de la simulation car la première étape de la compilation dans Scicos consiste à mettre à plat le diagramme. En fait, un <<Super Block>> n'est qu'une facilité graphique pour masquer des sous-diagrammes ; ce n'est pas un bloc de base.

Un nouveau bloc de base peut être défini de plusieurs façons, mais dans tous les cas on a besoin de deux fonctions. Une fonction d'interface, presque toujours écrite en langage Scilab, pour gérer l'interface avec l'éditeur Scicos, et une fonction de simulation réalisant le comportement dynamique du bloc. Cette dernière est utilisée par le simulateur et doit donc être rapide. Elle est souvent en C ou Fortran, mais elle peut aussi être en langage Scilab, en particulier dans la phase de mise au point.

Dans cet article on ne considère que les blocs <<réguliers>> ; il existe dans Scicos d'autres types de bloc, par exemple les blocs Zero-crossing et les blocs utilisés pour le conditionnement d'événements. On a rarement besoin de définir de nouveaux blocs de tels types.

La fonction d'interface
La fonction d'interface d'un bloc détermine non seulement sa géométrie, sa couleur de fond, son icône, les nombres et les tailles de ses ports d'entrées-sorties, etc..., mais aussi ses états initiaux et ses paramètres. De plus, elle gère le dialogue qui permet de modifier ses propriétés ; ce que l'utilisateur peut faire en cliquant sur le bloc.

Si le bloc qu'on veut définir n'a pas un aspect très particulier (c'est-à-dire s'il est rectangulaire, avec des ports placés aux endroits habituels et une icône ne contenant qu'un texte), il est assez facile de construire sa fonction d'interface en copiant celle d'un bloc prédéfini. La seule partie qu'il faut adapter est la gestion de dialogue avec l'utilisateur, utilisé pour modifier les paramètres du bloc.

Dans la palette Others on trouve le bloc GENERIC. Ce bloc a une fonction d'interface générique qui peut être utilisée dans la plupart des cas pour tester et mettre au point une fonction de simulation. Cette fonction d'interface suppose cependant que le dialogue avec l'utilisateur (suite au clic sur le bloc) est générique : elle propose la possibilité de modifier tous les paramètres du bloc, même ceux qui ne sont pas utilisés dans le bloc concerné, et ne fournit presqu'aucun test de validité sur les réponses de l'utilisateur. Cette fonction doit être modifiée en particulier pour protéger et informer les autres utilisateurs de ce bloc qui ne connaîtraient pas forcément son fonctionnement interne. Nous verrons plus tard sur un exemple comment cela peut se faire.

La fonction de simulation
Cette fonction est appelée au cours de la simulation pour effectuer les tâches suivantes : Un <<flag>> dans la liste d'appel de la fonction de simulation indique la tâche à effectuer :
Flag Tâche
0 Calcul de la dérivée de l'état continu
1 Calcul des sorties
2 Mise à jour des états
3 Calcul des dates des événements de sortie
4 Initialisation
5 Terminaison
6 Ré-initialisation
Les autres arguments de la liste d'appel de la fonction de simulation sont des pointeurs sur les états, les entrées, les sorties, les paramètres, etc..., et leurs dimensions. On y trouve aussi un entier (indicateur d'activation) indiquant dans quelle condition la fonction a été appelée (le bloc correspondant a été activé).

La liste d'appel de la fonction de simulation d'un bloc régulier n'a pas une syntaxe unique ; Scicos accepte plusieurs types de fonctions de simulation ayant des listes d'appel différentes.
Type de fonction Scilab Fortran C Commentaires
1 non oui oui liste d'appel de longueur variable
2 non non oui liste d'appel de longueur fixe
3 oui non non les entrées-sorties sont des list Scilab
On ne va pas faire ici une présentation exhaustive de tous les types de fonctions de simulation ; on ne considérera que le type 1 qui est utilisée dans 90% des blocs prédéfinis de Scicos. Les fonctions de simulation associées à ce type de bloc ont une liste d'appel de longueur variable en fonction des nombres d'entrées et de sorties. Par exemple un bloc ayant deux entrées et une sortie aura une fonction de simulation en C qui serait comme suit :

void mafonc(int* flag,int* nevprt,double* t,double* xd,
        double* x,int* nx,double* z,int* nz,double* tvec,
        int* ntvec,double* rpar,int* nrpar,int* ipar,int* nipar,
        double* u1,int* nu1,double* u2,int* nu2,double* y1,int* ny1)
{
................
}

Pour plus d'information sur les arguments de cette fonction voir Tableau 1.

I/O Args. Description
I flag 0,1,2,3,4,5 ou 6
I nevprt indicateur d'activation
I t temps
O xd dérivée de l'état continu
I/O x état continu
I nx taille de x
I/O z état discret
I nz taille de z
O tvec dates des événements de sorties (pour flag=3)
I ntvec taille de tvec
I rpar paramètres
I nrpar taille de rpar
I ipar paramètres
I nipar taille de ipar
I ui iième entrée, i=1,2,..
I nui taille de ui
O yj jième sortie, j=1,2,..
I nyj taille de uj

Table 1:

La fonction de simulation est appelée quand le bloc correspondant est activé. Après les initialisations et au cours de la simulation, cela se produit parce que le bloc reçoit un événement sur l'un de ses ports d'entrées d'événement ou dans le cas d'absence de ce type de port, il hérite d'un événement par l'une ou plusieurs de ses entrées. L'indicateur d'activation indique par quelle voie le bloc a été activé (1 si l'événement d'activation a été reçu sur le premier port d'entrée d'événement, 2 si sur le deuxième, 3 si sur le premier et sur le deuxième simultanément, 4 si sur le troisième, etc...). Le bloc peut aussi être activé sans qu'il reçoive d'événement d'activation s'il a été déclaré comme étant <<temps-dépendant>>, c'est-à-dire actif en permanence. Dans ce cas, l'indicateur d'activation vaut 0.

À chaque activation du bloc en cours de simulation, la fonction de simulation peut être appelée une ou plusieurs fois. Dans le cas d'un simple bloc réalisant une fonction immédiate, par exemple le bloc sinus, la fonction n'est appelée qu'une seule fois avec flag 1. Un bloc contenant un état discret sera aussi appelé avec flag 2, et si de plus le bloc a des ports de sorties d'événement, il sera appelé aussi avec flag 3. L'ordre des appels, dans le cas d'une activation par événement, est flag 1 suivi par flag 3, et enfin flag 2. Dans le cas d'activation continue, c'est 1 puis 0.

Exemple
Dans l'article précédent on a construit un système dynamique modélisant la régulation de la population des requins et des sardines (voir Figure 1).

{short description of image}

Figure 1: Le modèle de la régulation de la population des requins et des sardines de l'article précédent (Linux Magazine 18).

Dans ce cas, presque la moitié des blocs sont utilisés pour implanter le modèle de la population des requins et des sardines : {
x0'(t)=a x0(t)-b x0(t)x1(t)+f x0(t)      a,b > 0,  f<0,
x1'(t)=c x0(t)x1(t)-d x1(t)      c,d > 0
. (noter qu'il y a un petit changement de notation par rapport à l'article précédent : ici x0(t) représente le nombre de sardines et x1(t) représente le nombre de requins).

Les autres blocs sont dédiés à la partie régulation et à la visualisation. Il serait donc interessant de définir avec un seul bloc le modèle de la population. Ce bloc aurait comme entrée f (l'effort de pêche), et le vecteur x=(x0,x1) comme état continu et sortie. Les paramètres seraient rpar=(a,b,c,d). La fonction de simulation (en C) pourrait être écrite comme suit :
void pred_prey(int* flag,int* nevprt,double* t,double* xd,
        double* x,int* nx,double* z,int* nz,double* tvec,
        int* ntvec,double* rpar,int* nrpar,int* ipar,int* nipar,
        double* u1,int* nu1,double* y1,int* ny1,double* y2,int* ny2)

{   
    if (*flag == 1 || *flag == 6) {
      /*  y=x  */
      *y1=x[0];
      *y2=x[1];
    } else if (*flag == 0) {
      /*     x0d = a*x0-b*x0*x1+f*x0 */
      /*     x1d = c*x0*x1-d*x1 */
      xd[0]=rpar[0]* x[0]-rpar[1]* x[0]* x[1] + *u1* x[0];
      xd[1]=rpar[2]* x[0]* x[1]-rpar[3]* x[1];      
    }
    return ;
} 

Ce bloc ne sera appelé qu'avec les flags 0, 1, 4, 5 et 6. Pour les flags 4 et 5, il n'y a clairement rien à faire. Pour les flags 1 et 6, il faut calculer les sorties, c'est-à-dire recopier l'état dans les sorties. Enfin, pour le flag 0, il faut calculer la dérivée de x que l'on place dans xd (c'est-à-dire x').

Une fois cette fonction compilée (et mise par exemple dans le fichier pred_prey.o), on peut la linker dans Scilab et l'utiliser dans Scicos. Pour la linker, on utilise la fonction link :
-->link('pred_prey.o','pred_prey','C')
La fonction de simulation pred_prey est maintenant utilisable dans Scicos pour la construction du bloc Pred-Prey. Il ne nous reste donc qu'à écrire la fonction d'interface.

Dans un premier temps, on utilise la fonction d'interface canonique GENERIC. On commence par supprimer tous les blocs qu'on veut remplacer dans le diagramme. Puis on copie le bloc GENERIC qui se trouve dans la palette Others et on le place dans le diagramme. En cliquant dessus, on ouvre une fenêtre de dialogue qui permet de choisir ses paramètres (voir les Figures 2 et 3).

{short description of image}

Figure 2: Le dialogue du bloc GENERIC : le nom de la fonction de simulation, son type, les tailles et les nombres d'entrées-sorties, etc...


{short description of image}

Figure 3: Le dialogue du bloc GENERIC (suite).

Noter les deux dernières questions. La première concerne l'existence d'une dépendence directe entrée-sortie. Dans ce cas, les sorties x0 et x1 ne dépendent pas directement de l'entrée f (seulement la dérivée de x0 en dépend, on considère cela comme une dépendance indirecte). La deuxième question concerne la propriété de temps-dépendance. La réponse est oui car ce bloc doit être actif en permanence, il n'est pas activé par des signaux d'activation.

Après avoir cliqué sur OK, on constate que le bloc GENERIC se redessine avec un port d'entrée et deux ports de sortie. On peut alors brancher les entrées et les sorties pour obtenir le schéma de la Figure 4.


{short description of image}

Figure 4: L'utilisation du bloc GENERIC.

La simulation du nouveau schéma montre qu'il est identique au précédent en fonctionnalité et un peu plus efficace en ce qui concerne la vitesse de simulation.

Ce bloc peut maintenant être copié, placé dans une palette, etc..., comme n'importe quel autre bloc Scicos. Mais l'utilisation de ce bloc n'est pas conviviale. On peut bien entendu adapter le bloc en changeant sa couleur et son icône, mais quand on clique dessus, on a toujours le même dialogue canonique dont la moitié des questions ne sont pas pertinentes.

Il faut donc construire une fonction d'interface pour présenter un dialogue spécifique avec éventuellement des tests de validité (par exemple ne pas accepter des paramètres a, b, c, d négatifs). Une fonction d'interface qui permet de faire cela est la suivante :
function [x,y,typ]=P_Prey(job,arg1,arg2)
x=[];y=[];typ=[]
select job
case 'plot' then
  standard_draw(arg1)
case 'getinputs' then
  [x,y,typ]=standard_inputs(arg1)
case 'getoutputs' then
  [x,y,typ]=standard_outputs(arg1)
case 'getorigin' then
  [x,y]=standard_origin(arg1)

case 'set' then
  x=arg1
  graphics=arg1(2);model=arg1(3);label=graphics(4)
  while %t do
    [ok,a,b,c,d,x0,label]=getvalue('Definissez les parametres du bloc',..
        ['a';'b';'c';'d';'Etat initial'],..
        list('vec',1,'vec',1,'vec',1,'vec',1,'vec',2),label)
    if ~ok then break,end
    if a<0 | b<0 | c<0 | d<0 then 
      message('les parametres doivent etre positifs')
    else
      if ok then
        graphics(4)=label;
        model(6)=x0(:);model(8)=[a;b;c;d];
        x(2)=graphics;x(3)=model
        break
      end
    end
  end
case 'define' then
  x0=[2;1];a=2;b=1;c=.3;d=1;  //default
  model=list(list('pred_prey',1),1,[1;1],[],[],x0,[],[a;b;c;d],[],..
      'r',[],[%f %t],' ',list())
  label=[string(a);string(b);string(c);string(d);strcat(sci2exp(x0))]
  gr_i=['xstringb(orig(1),orig(2),''Pred_prey'',sz(1),sz(2),''fill'');']
  x=standard_define([3 2],model,label,gr_i)
end
On a utilisé ici les fonctions standard pour les cas plot (dessiner le bloc), getinputs (retourner les coordonnés des entrées), getoutputs (retourner les coordonnés des sorties), getorigin (retourner les coordonnés du bloc dans le schéma). Cela peut se faire pour tous les blocs rectangulaires. Les cas set et define sont par contre spécifiques. Le premier gère le dialogue avec l'utilisateur et le deuxième définit les valeurs initiales des paramètres.

Pour comprendre cette fonction, il faut savoir qu'un bloc Scicos est défini par une list Scilab comme suit :
 list('Block',graphics,model,unused,interf_function)
Les variable graphics et model sont aussi des list. La structure graphique graphics contient des informations sur l'aspet graphique du bloc comme sa taille, son emplacemement, son orientation, etc..., et model des informations nécessaires pour la simulation comme le nom de la fonction de simulation et son type, les nombres et les tailles des ports d'entrées-sorties, les valeurs des états, des paramètres, etc... Enfin, interf_function est une chaîne de caractères Scilab contenant le nom de la fonction d'interface associée à ce bloc.

La variable model est initialisée dans le cas define. On constate alors que le nom de la fonction de simulation est pred_prey, qui est de type 1, que le bloc a une seule entrée de taille 1 et deux sorties de taille 1 chacune, qu'il n'a ni entrée, ni sortie de type événement, que son état continu initial est [2;1], qu'il n'a pas d'état discret, qu'il a comme paramètre réel le vecteur [2;1;.3;1] et pas de paramètre entier et qu'il s'agit d'un bloc <<régulier>>. À noter aussi l'élément [%f %t] du model de notre exemple qui indique que ce bloc ne contient pas de dépendence directe entrée-sortie, mais qu'il est temps-dépendant. Les autres champs sont utilisés pour la documentation et identification.

Pour utiliser ce bloc, il suffit maintenant d'utiliser le bouton AddNewBlock du menu Edit. On peut alors remplacer le bloc GENERIC par ce bloc (Pred_prey). La simulation donne alors exactement le même résultat qu'avant, mais maintenant en cliquant sur le bloc on obtient :
{short description of image}

Pour construire des blocs plus sophistiqués, on peut toujours chercher un bloc prédéfini ayant des caractéristiques semblables à ce qu'on cherche à construire et examiner les sources de ses fonctions correspondantes. Les fonctions d'interfaces des blocs prédéfinis se trouvent dans le répertoire <SCI>/macros/scicos_blocks et les fonctions de simulation dans <SCI>/routines/scicos. Pour plus d'information, en particulier sur le formalisme de base et le fonctionnement interne de Scicos, voir http://www-rocq.inria.fr/scilab/doc/scicos_html/.

Ramine Nikoukhah
Scilab Group
scilab@inria.fr


This document was translated from LATEX by HEVEA.