title: Du Vensim au Python url: https://broco.ga/python.html hash_url: ec4d74dd36e0dd7c730a567078e26e49
J’ai réussi aujourd’hui à porter une première version de World3 en Python depuis le modèle Vensim. J’ai noté deux différences techniques :
Voici, par exemple, le code généré par PySD représentant le fameux modèle de la tasse de thé :
# Le code généré par PySD est commenté
# j'ai enlevé les commentaires pour réduire la place que prend le code
@cache('run')
def final_time():
return 30
@cache('run')
def room_temperature():
return 70
@cache('run')
def characteristic_time():
return 10
@cache('run')
def initial_time():
return 0
@cache('run')
def time_step():
return 0.125
@cache('step')
def teacup_temperature():
return integ_teacup_temperature()
integ_teacup_temperature = functions.Integ(lambda: -heat_loss_to_room(), lambda: 180)
@cache('step')
def saveper():
return time_step()
@cache('step')
def heat_loss_to_room():
return (teacup_temperature() - room_temperature()) / characteristic_time()
En comparaison, voici le texte contenu dans un fichier .mdl (Vensim) :
Characteristic Time=
10
~ Minutes [0,?]
~ How long will it take the teacup to cool 1/e of the way to equilibrium?
|
Heat Loss to Room=
(Teacup Temperature - Room Temperature) / Characteristic Time
~ Degrees Fahrenheit/Minute
~ This is the rate at which heat flows from the cup into the room. We can \
ignore it at this point.
|
Room Temperature=
70
~ Degrees Fahrenheit [-459.67,?]
~ Put in a check to ensure the room temperature is not driven below absolute \
zero.
|
Teacup Temperature= INTEG (
-Heat Loss to Room,
180)
~ Degrees Fahrenheit [32,212]
~ The model is only valid for the liquid phase of tea. While the tea could \
theoretically freeze or boil off, we would want an error to be thrown in \
these cases so that the modeler can identify the issue and decide whether \
to expand the model.
Of course, this refers to standard sea-level conditions...
|
Le passage de l’un à l’autre se fait à l’aide d’un parser, un programme qui, dans un texte, compare des syntaxes, repère les éléments clés puis les traduits. Le parser utilisé par PySD est la bibliothèque parsimonious.
Chaque variable est représentée par une fonction qui prend soit une valeur constante, soit une valeur dépendante d’autres variables. On peut remarquer les entêtes @cache
au début avant chaque fonction : elle prend le paramètre 'run'
quand la variable est constante (la variable est conservée une bonne fois pour toute du début jusqu’à la fin d’une simulation), 'step'
quand la variable dépend des autres (la variable n’est conservée que la durée de l’état courant, pour calculer d’autres variables qui en ont besoin).
On peut aussi remarquer que les relations entre variables peuvent être simples et donc directement écrites au sein de la fonction représentant la variable (par exemple dans heat_loss_to_room
), ou plus complexes et faisant appel à des implémentations propres à PySD (integ_teacup_temperature
). Ces implémentations sont caractéristiques du logiciel de modélisation et peuvent différer d’un modèle à l’autre, au vu du caractère discret du calcul numérique et en fonction des besoins. Pour l’intégrale par exemple, il en existe au moins trois types, implémentés dans Vensim : l’intégrale d’Euler, l’intégrale par différence, l’intégrale de Runge-Kutta1. Normalement, les résultats de chacunes de ces méthodes doivent être proches.
Mis à part que Python est un langage libre, gratuit, accessible et très populaire (ce qui permet une diffusion rapide des implémentations dans ce langage), de nombreuses bibliothèques d’apprentissage, d’analyse et de fouille de données existent pour lui.
À terme et dans la pratique, une des solutions d’amélioration du modèle proposé par mon tuteur, Serge Fenet, est d’exécuter une simulation inverse depuis un état voulu dans le futur pour trouver des comportements à adopter dès aujourd’hui, ce qui se déroulerait en deux temps :