Metafor

ULiege - Aerospace & Mechanical Engineering

User Tools

Site Tools


devel:python

Interface python

Qu'est ce que c'est?

Python est un langage interprété (comme le shell unix, matlab, etc) qui ressemble un peu à l'interpréteur intégré dans Oofelie. Contrairement à ce dernier, il est très complet, rapide, orienté objet, utilisant le typage dynamique, interfaçable avec plus ou moins n'importe quoi. De plus, c'est maintenant une référence vu le nombre d'utilisateurs à travers le monde (scientifique ou non).

Pour moi, c'est LA solution idéale pour interfacer un code de calcul avec la ligne de commande.

Pour la petite histoire, Python a été inventé par Guido van Rossum en 1990 (c'est-à-dire plus ou moins quand Igor a inventé Oofelie). On peut dire que Python est ce que l'interpréteur Oofelie serait devenu si Igor avait continué à travailler dessus (et s'il avait trouvé quelques centaines de petites mains pour l'aider). Parmi les organisations qui utilisent Python, citons Google, Yahoo et RedHat. Parmi les codes EF, Code_Aster et Abaqus. N'oublions pas que VTK et Qt sont aussi interfacés avec Python.

Pour en savoir plus sur ce fabuleux langage (n'ayons pas peur des mots):

Python vs l'interpreteur Oofelie (OE)

Le combat est assez inégal, et perdu d'avance pour OE:

OE Python
Extensible via les I_, bon courage ! oui (classes interprétées, API C). extension possible via des scripts (on peut dériver une classe interprétée d'une classe compilée)
Modulaire non - pas de notion de package - conflits de noms fréquents namespaces (modules)
Rapide bof (pas de hashMap) oui (fichiers compilés, portables .pyc, hashMaps intégrées)
Gestion des erreurs peu fiable. Il a la mauvaise habitude de ne pas s'arrêter lorsque des erreurs surviennent rendant difficile la localisation de celles-ci. très fiable (exceptions - callstack). Impossible de louper une erreur de syntaxe.
Extensions absentes - à programmer soi même. réseau, MPI, graphisme (Tcl, Qt), système… - voir p.expl ce site
Syntaxe pas claire (cfr “;”) claire et fiable
Gestion mémoire absente ou catastrophique, au choix compteurs de refs - destructeurs - entièrement customisable
Portable bof - chaque nouvelle machine nécessite un portage fastidieux. oui - toute plateforme
Maintenance non négligeable (à chaque mise à jour) quasi nulle pour nous
Bugs nombreux (nombre croissant) - corrigés par nous très peu - corrigés par d'autres
Debugueur non oui, intégré
Licence payant (pas pour nous mais…) gratuit - même encore mieux: on peut le revendre! (voir licence python)

Ajoutons à ca des petits “plus” comme l'éditeur IDLE qui permet, avec son “path browser”, de parcourir toutes les fonctions et les modules disponibles. L'interface Tk intégrée à la distribution est aussi très intéressante.

En fait, tout ce qui peut se faire avec l'interpréteur Oofelie peut se faire avec Python. J'ai donc essayé de recréer un environnement “Oofelie” sous Python avec la contrainte d'une syntaxe similaire (ce n'est pas simple parce que l'interpréteur OE cache souvent beaucoup de choses à l'utilisateur - comme des conversions implicites).

Une application très simple de la puissance de Python serait de programmer l'ouverture d'une fenêtre lors du chargement d'un jeu de données pour que l'utilisateur puisse entrer, charger/sauver ses paramètres et enfin pousser sur le bouton “start metafor”. C'est fait en quelques lignes sous Python et ça permet de créer rapidement une application “métier”. Il est même possible, dans ce cas, de ne distribuer que le fichier .pyc (version compilée et indépendante de la plateforme du .py).

metapython.jpg

Parmi les nombreuses choses que nous avions demandé à OE et qui nous sont maintenant accessible, citons (dans le désordre) :

  • plus de memory leaks venant de l'interpréteur !!!
  • les “packages”
  • arrêt de l'interpréteur lors d'erreur + callstack
  • les strings, les vecteurs, les tuples, les dictionnaires, etc
  • toutes les commandes systèmes (ouvrir, fermer un fichier par exemple ; accès réseaux)
  • le path browser
  • interface directe avec matlab via pyMat et NumPy
  • interface directe avec GNUplot via GnuPlot.py
  • interface directe avec Qt via pyQt, Fox via fXPy, …
  • possibilité de s'interfacer avec n'importe quel langage ou n'importe quelle autre librairie (même Fortran si on est maso) - voir glue it together with Python.
  • possibilité de distribuer des applications “métier” avec interface graphique intégrée (une interface graphique différente par cas-test si on veut).
  • plus de cast bidon lorsqu'on a de l'héritage multiple (expl: CurveDataFunct::castToFunct() est inutile!).
  • gestion automatique des pointeurs NULL via l'objet PyNone (expl: curveSet(5) donne pyNone si la courbe numéro 5 n'existe pas).

En un mot, on a d'un coup tout ce que Igor prétend faire pour la fin du mois dans sa présentation d'Oofelie :-)

Un exemple d'erreur (avec impression de la callstack):

  Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "/d50/users/boman/dev/oo_meta/toolbox/utilities.py", line 7, in meta
  module = __import__(domainTxt, globals(), locals(), ['getDomain'])
  File "/d50/users/boman/dev/oo_meta/apps/zqs/ale_5cotes.py", line 144, in ?
  excit.define(1,NODE_PO,Key(TX|GD|RE),0.0000000000E+00,ale_5cotes_fct1,EX_INCR); fixaset.define(1,NODE_PO,Key(TX));
  NameError: name 'EX_INCR' is not defined

L'interface Python de Metafor

Python possède une API écrite en C qui permet d'écrire des “modules” (comme les packages en java, ou namespaces en C++). Metafor est donc un module de Python…. enfin, presque. En effet, n'oublions pas que grâce au magnifique travail d'Igor sur les matériaux et les éléments, il n'est pas possible d'initialiser correctement ceux-ci quand ils sont regroupés dans une lib. J'ai dû donc choisir d'intégrer Python dans l'exécutable Metafor.

Cette interface n'est pas simple à écrire parce que l'API Python est complexe. J'étais donc un peu découragé jusqu'au jour où j'ai vu le logiciel Swig (Simplified Wrapper and Interface Generator) qui se charge du boulot. Mieux encore, Swig crée des “shadow classes” qui permettent d'obtenir des objets Python malgré l'API écrite en C. Swig gère aussi tous les problèmes de cast (ce qui pose beaucoup de problèmes dans l'interpréteur Oofelie).

Swig n'est pas qu'un simple traducteur: il gère également la surcharge des fonctions membres (absentes dans Python puisque les types d'objets ne sont pas explicites) et l'héritage multiple. Tout ceci via un système de cast dynamique assez bien fait et relativement bien optimisé (j ai jeté un œil, c'est largement mieux fait que OE). Enfin, Swig gère correctement les variables globales (qui, elles, ne sont pas des références Python).

L'interface Python de Metafor se réduit donc à des fichiers input de Swig (fichiers .i et .swg). Les fichiers .i donnent des .cpp via Swig qui sont compilés un à un pour former des modules. Les fichiers .swg sont des fichiers inclus par les .i qui ne correspondent pas à un module. A l'heure actuelle, ces fichiers interfacent toutes les fonctions membres (publiques) des classes Metafor et Oofelie utilisées dans le célèbre cas-test “cont2”.

Lorsqu'on lance Metafor, on se retrouve en ligne de commande Python. Le module Metafor est déjà chargé en mémoire ainsi que le module utilitaire (toolbox.utilities). Il est donc possible de lancer un cas-test (myTest.py) par la simple commande:

  meta('myTest')

Contrairement à Oofelie, Python ne va pas commencer à parcourir les répertoires pour trouver myTest.py. J'ai pourtant reprogrammé cette fonction mais je trouve que c'est pas très intéressant vu les conflits possibles. J'ai donc choisi que pour lancer cont2.py, par exemple, qui se trouve dans oo_meta/apps/qs, on tape:

  meta('apps.qs.cont2')

c'est propre et net!

Qu'est ce qui m'a pris du temps si c'est si simple?

Avoir une première interface est très simple. Le premier problème était les cast implicites cachés derrière les get_properties(). Heureusement, Swig peut gérer ça facilement (un peu comme le I_::new_child_from_id() de OE). J'ai dû aussi gérer les std::string et les UserNo via des “typemaps” de Swig (un système qui permet de faire des casts implicites automatiques).

Après ceci, le premier résultat ne ressemblait pas trop à l'interpréteur Oofelie. Par exemple, les Locks étaient stockées dans une variable cvar. Il fallait donc écrire:

  loadingset.define(1,NODE_PO, Key(cvar.TX|cvar.GD|cvar.RE), 0, f)

ce qui est lourd. J'ai donc réussi à “descendre” les variables cvar dans chaque namespace (et ceci de manière automatique - sans écrire). Bref, si on ajoute une Lock, ça marche tout seul).

Même remarque pour les noms d'éléments, de matériaux, de Elementproperties, de MaterialProperties, identificateurs, etc.

Comment se mettre à Python?

  • Facile: il suffit de lire la doc ou faire le tutorial. J'ai aussi une chouette revue. J'ai synthétisé le tutorial pour mon usage perso. Si vous voulez lire mon résumé, demandez-moi…
  • Autre moyen: regarder ce que j'ai fait dans apps/qs/cont2.py
  • La syntaxe est extrêmement proche de celle d'Oofelie (ouf). Le passage des .e aux .py est presque immédiat.
  • Je vous conseille aussi de jeter un œil à Swig.
  • Vous pouvez également regarder l'interface Z-Mesh.

Pour démarrer:

  • charger un fichier : execfile('fichier.py') ou import fichier
  • dir(module) : “status” de matlab
  • dir(variable) : donne les fonctions membres
  • PYTHONPATH : équivaut au LIBOOFE
  • type(variable) : donne le type de variable
  • print 'variable=', variable : imprime variable = valeur
  • tout est objet (même une fonction).

Différences par rapport à l'interpréteur OE:

  • il faut importer des modules via la commande import (from wrap.oofelie import * par expl)
  • déclaration de variable inutile - et interdite ! (int typemat; par exemple)
  • toutes les variables sont des réfs (Refer est inutile).
  • syntaxe des constructeurs (Domain dom; devient dom = Domain() )
  • PhySet::get_properties() doit être mentionné explicitement (pas de raccourcis avec les crochets)
  • []() pour les numéros utilisateurs (= syntaxe C++ ; expl : curset(3).mesh(nx) )
  • LoadingSet.define(), FixationSet.define() prennent des Keys au lieu de Locks. Utiliser Key(TO|RE) au lieu de TO|RE
  • une fonction getDomain() doit retourner le domaine pour chaque cas-test.
  • “#” est le seul caractère de commentaire
  • pas de blocs “{ }” mais des blocs basés sur l'indentation
  • pas de “;” nécessaires
  • pour quitter CTRL-Z [ENTER] sous windows et CTRL-D sous Unix ou CTRL-C sous Cygwin (ou quit() )
devel/python.txt · Last modified: 2024/03/01 16:40 by boman

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki