Marc BUFFAT

Professeur au département de Mécanique, Lyon 1 e-mail

Blog scientifique et pédagogique utilisant des notebooks IPython et Linux

cours en ligne INPROS: chapitre 5


Ipython notebook : cours INPROS LyonHPC

Auteur: Marc BUFFAT, Pr dpt de Mécanique, UCB Lyon 1

Contributeurs: Violaine Louvet, Michel Kern, Loic Gouarin, Laurence Viry </h5>

Licence Creative Commons
Mise à disposition selon les termes de la Licence Creative Commons
Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 2.0 France
.
In [1]:
%matplotlib inline
%autosave 300
import numpy as np
from IPython.display import HTML,display
css_file = 'style.css'
try:
    display(HTML(open(css_file, "r").read()))
    print("using ",css_file)
except :
    print("using default css")
Autosaving every 300 seconds
using  style.css

LyonHPC LyonHPC
</div> cloud

Procédure

procédure: algorithme avec des paramètres d’entrée (données) et des paramètres de sortie (résultat)

Algorithme MaFonction(liste de paramètres)
    variables locales
    instructions
    retour (liste de valeurs)

# utilisation
val = MaFonction(param1,param2,..)

Fonction en Python

implémentation d’une procédure = function (C, Python) , subroutine (Fortran)

# définition
def MaFonction(liste arguments):
    instructions
    ....
    return (liste de valeurs)
# utilisation
(val1, val2, ..) = MaFonction(arg1, arg2, ...) 

Passage des arguments

on affecte aux arguments de la fonction la valeur des paramètres d’appel:

def MaFonction(a1, a2, a3):
    ....
    return (v1, v2)

(V1, V2) = MaFonction(A1, A2, A3)

  • lors de l’appel, on a a1=A1 a2=A2 a3=A3
  • valeur de retour = liste

    • lors du retour, V1=v1, V2=v2

**Exemple Python**(visualisation avec [http://www.pythontutor.com])

In [2]:
HTML('<iframe width="800" height="400" frameborder="0" src="http://pythontutor.com/iframe-embed.html#code=def+MaFonc(a1,a2,a3)%3A%0A++++v1%3Da1%2Ba3%0A++++v2%3Da2%2Ba3%0A++++return+(v1,v2)%0A%09%0AA1%3D1.%0A(V1,V2)%3DMaFonc(A1,2.,4*A1)&cumulative=false&heapPrimitives=false&drawParentPointers=false&textReferences=false&showOnlyOutputs=false&py=2&curInstr=8&codeDivWidth=350&codeDivHeight=400"> </iframe>')
Out[2]:

Passage des arguments

  • pour les variables scalaires: valeur = valeur de la variable

    • la variable n’est donc pas modifiée
  • pour les listes et tableaux: valeur = adresse des données

In [3]:
HTML('<iframe width="800" height="400" frameborder="0" src="http://pythontutor.com/iframe-embed.html#code=def+InitTab(X)%3A%0A%09X%5B%3A%5D%3D%5B0,0,0%5D%0A%09n%3Dlen(X)%0A%09for+i+in+range(n)%3A%0A%09++++++++X%5Bi%5D%3Di*i%0A%09return%09%0AZ%3D%5B1,1,1%5D%0AInitTab(Z)&cumulative=false&heapPrimitives=false&drawParentPointers=false&textReferences=false&showOnlyOutputs=false&py=2&curInstr=15&codeDivWidth=350&codeDivHeight=400"> </iframe>')
Out[3]:
In [4]:
HTML('<iframe width="800" height="400" frameborder="0" src="http://pythontutor.com/iframe-embed.html#code=def+InitTab(X)%3A%0A++++X%3D%5B0,0,0%5D%0A++++n%3Dlen(X)%0A++++for+i+in+range(n)%3A%0A++++++++X%5Bi%5D%3Di*i%0A++++return%09%0AZ%3D%5B1,1,1%5D%0AInitTab(Z)&cumulative=false&heapPrimitives=false&drawParentPointers=false&textReferences=false&showOnlyOutputs=false&py=2&curInstr=15&codeDivWidth=350&codeDivHeight=400"> </iframe>')
Out[4]:

Algorithme d’Euclide: calcul du PGCD de 2 entiers

# Calcul du PGCD
# paramètres d'entrée: 2 entiers positifs a et b
# en sortie: PGCD de a et b
#
Algorithme PGCD(a,b)
    tant que a # b 
        si a>b alors a = a-b 
        sinon b = b - a 
    retour a
#

Programme Python

In [5]:
def PGCD(a, b):
    """ 
    Calcul du PGCD de a et b (entiers positifs) 
    """
    while a != b:
        if a > b: 
            a = a - b
        else:   
            b = b - a
    return a
# utilisation
a0, b0 = 15623532, 252568
p = PGCD(a0, b0)
print("PGCD de ", a0, b0, "=", p)
PGCD de  15623532 252568 = 4

Algorithme: calcul de la somme d’une série

$$ S_n = \sum_{i=1}^n \frac{1}{i^2} \mbox{ et } \lim_{n\rightarrow\infty} S_n = \frac{\pi^2}{6}$$

# calcul de la somme Sn et de l'Écart par rapport à la limite
# entrée: n entier   sortie: somme, écart par rapport à limite
Algorithme Somme(n)
    S=0.
    pour i de 1 a n
        S = S + 1./i**2
    err = S - (pi**2/6.)
    retour S,err

Programme Python

In [7]:
# fonction Python
def Somme(n):
    """ 
    Calcul de la somme de la série 1/i**2  pour i de 0 a n 
    en sortie: somme et écart par rapport à la limite
    """
    S=0.
    for i in range(1, n + 1):
        S = S + 1. / (i*i)
    ecart = S - (np.pi**2/6.)
    return S, ecart
#
n = 100
S, err = Somme(n)
print(S, err)
print(Somme(1000))
print(Somme(10000))
1.6349839001848923 -0.009950166663334148
(1.6439345666815615, -0.0009995001666649461)
(1.6448340718480652, -9.999500016122376e-05)

Paramètres optionnels d’une fonction

  • En Python possibilité de définir des paramètres optionnels avec une valeur par défaut
  • Nommage des paramètres (ordre indifférent)
  • Très utilisés dans les fonctions des bibliothèques
    # Déclaration
    def MaFonction(par, par1=valdef1, par2=valdef2)

    # Appel
    MaFonction(val)
    MaFonction(val, par1=val1)
    MaFonction(val, par2=val2, par1=val1)

Exemple: algorithme de Héron pour calculer une racine carrée

Pour calculer une approximation $u$ de $\sqrt{a}$, on note que si $u\approx\sqrt(a)$ alors $a/u\approx\sqrt(a)$ et donc $\frac{u + a/u}{2}$ est sans doute une meilleur approximation de $\sqrt{a}$.
On peut vérifier que cette méthode babylonienne est en fait un cas particulier de la méthode de Newton pour calculer la racine de $f(x)=u^2-a$

Algorithme

  Algorithme Heron(a)
      u = a/2
      eps = 1.e-6
      tant que |u**2 - a| > eps
          u = (u + a/u)/2
      fin tantque
      retour u

Programme Python

In [9]:
import numpy as np
def Heron(a, eps=1e-03, itmax=100):
    """ 
    calcul une approximation de sqrt(a) avec une precision eps
    et une nombre maxi d'iterations itmax """
    u = a/2.
    it = 0
    while abs(u**2 - a) > eps:
        u  = (u + a/u) / 2.
        it = it + 1
        if it > itmax:
            print("Attention nbre maxi d'iterations atteint", it)
            break
    return u, it
# utilisation
b = 200.
print("sqrt(200) ~",Heron(b))
print("a 10^-8  pres sqrt(200) ~",Heron(b, 1.e-08))
print("a 10^-13 pres sqrt(200) ~",Heron(b, eps=1.e-13))
print("a 10^-14 pres sqrt(200) ~",Heron(b, eps=1.e-14))
print("a 10^-14 pres sqrt(200) ~",Heron(b, itmax=200, eps=1.e-14))
print("valeur avec numpy ",np.sqrt(b),repr(np.sqrt(b)))
sqrt(200) ~ (14.142135968022693, 6)
a 10^-8  pres sqrt(200) ~ (14.142135623730955, 7)
a 10^-13 pres sqrt(200) ~ (14.142135623730951, 8)
Attention nbre maxi d'iterations atteint 101
a 10^-14 pres sqrt(200) ~ (14.142135623730951, 101)
Attention nbre maxi d'iterations atteint 201
a 10^-14 pres sqrt(200) ~ (14.142135623730951, 201)
valeur avec numpy  14.1421356237 14.142135623730951

Fonction Lambda

  • permet de définir des mini-fonctions (à la Lisp)
  • utile pour des fonctions simples (mais pas obligatoire!!)
    F = lambda args : expression(args)
In [11]:
import numpy as np
F = lambda x: np.cos(x**2)
print(F(1))
G = lambda x, y: F(x) + y**2
print(G(1, 1))
0.540302305868
1.54030230587

Méthodes, classes et fonctions de bibliothéque

Définition

classe = type de structure de données définissant des données + fonctions pour manipuler ces données

méthode = fonction associée à une classe pour manipuler les données de la classe

objet = instance de classe = variable [données + méthodes]

sous Python toute variable (list, string, real, int) est un objet et possède des méthodes (p.e pour l’afficher)

Syntaxe d’utilisation d’une méthode sous Python

pour un objet A avec un methode func1

A.func1(arg1,arg2,...)

équivalent à

func1(A,arg1,arg2,..)


Exemple en Python

In [12]:
S="mon nom"
print(S.upper())
L=[1,2,3]
L.reverse()
print(L)
X=3.14157
print(X.as_integer_ratio())
MON NOM
[3, 2, 1]
(7074186740679165, 2251799813685248)

Aide sous Ipython

Sous Ipython, pour avoir la liste des méthodes associées à un objet A

A. <tab>   # liste des methodes
A.meth?    # aide sur la methode meth
In [13]:
L
Out[13]:
[3, 2, 1]

Fonctions d’une bibliothéque

espace de nom = nom de la bibliothéque bibli

import bibli

nom de la fonction func dans la bibliothéque

bibli.func
bibli.sous_bibli.func

simplification du nommage

import bibli as bib
import bibli.sous_bibli as sbib

importation d’une fonction

from bibli import func
In [15]:
import numpy as np
print(np.sqrt(3))
from numpy import tanh
print(tanh(1))
1.73205080757
0.761594155956

Programmation structurée

principe: “Divide and Conquer”

découpage du problème en une suite de problèmes plus simples

Analyse algorithmique “Top-Down design”

Analyse

Principes de l’analyse top down

  • on découpe le problème en une série de sous-problèmes plus simples (si possible indépendant)
  • on spécifie ce qui doit résolu dans chacun des sous-problèmes sans forcément dire comment
  • puis on itère au niveau des sous problèmes.

Programmation “Botton-up programming”

  • on part des sous-problèmes élémentaires que l’on programme sous forme de fonctions (ou procédures)
  • on valide les fonctions
  • puis on réitère en remontant dans l’arbre jusqu’au programme principal

règles réutiliser les fonctions déjà écrites et validées (bibliothèques): principe du moindre effort !

Exemple: simuler l’alunissage de Neil Amstrong (Apollo 11)

  • basé sur une simulation d’alunissage du module lunaire écrit en basic en 1969, puis popularisé en 1979 sur ATARI (Lunar Lander).
    Moon Lander

jeu “Lunar Lander”:

Poser le module lunaire (LEM) sur la lune en arrivant avec une vitesse quasiment nulle. Pour cela on dispose de rétro-fusées permettant de ralentir la chute du LEM.

  • On contrôle manuellement ces rétro-fusées en sélectionnant une poussée (de 0 à 9), correspondant à l’éjection de carburant avec un débit $Qe$ variable et une vitesse $Ue$ fixe.
  • Mais on dispose d’une quantité limitée de carburant que l’on doit utiliser avec modération pour pouvoir atterrir en douceur.

modèle physique:

Le LEM, de masse initiale $M0$, est soumis à la gravité $g$ de la lune et à la poussée des rétro-fusées, correspondant à l’éjection d’un débit de fuel $Qe$ à un vitesse $Ve$ .

LEM

modèle mathématique:

Equation du mouvement: $$ \frac{d^2 Z}{dt^2} = -g + \frac{Qe*Ue}{M0-Qe*t}$$

en intégrant sur la durée d’une commande $T$ $\rightarrow$ vitesse $V$ $$ V = V0 + g*T + Ue * \ln{(1 - \frac{Qe*T}{M0})} $$ La masse du LEM $M$ diminue $$M = M0 -Qe*T$$ Approximation par DL car $X=\frac{Qe*T}{M0} \ll 1$ $$ V = V0 + g*T - Ue*(X + \frac{X^2}{2} + \frac{X^3}{3} + \frac{X^4}{4} + \frac{X^5}{5})$$ d’où l’altitude $Z$ $$ Z = Z0 - V0*T - g \frac{T^2}{2} + Ue*T*(\frac{X}{2} + \frac{X^2}{6} + \frac{X^3}{12} + \frac{X^4}{20} + \frac{X^5}{30})$$ expression utilisée dans les premiers programmes en BASIC.

cas particuliers

  1. Si le fuel est épuisé ($Qe=0$), le LEM atteins la surface lunaire au bout d’un temps $T$ solution de l’équation du 2nd degré: $$ 0 = Z0 - V0*T -g \frac{T^2}{2}$$ soit $T = (-V0 + \sqrt{V0^2 + 2 g Z0})/g$

  2. Près de la surface, $T$ trop grand $\rightarrow$ prédiction $Z0 < 0$

    • calcul $T$ donnant l’altitude $Z=0$, solution d’une équation du 6ième degré.
    • calcul par approximations successives en utilisant un DL de $Z(t)$
      1. estimation $T0$ de $T$ $$ T0 = \frac{-V0 + \sqrt{V0^2 + 2 (g-\frac{Ue*Qe}{M0}) Z0}}{g-\frac{Ue*Qe}{M0}} $$
      2. recalcule $V0$ et $Z0$, puis recommence.

Algorithme: analyse top-down

problème global

Algorithme

sous-probleme Lecture_cde

Algorithme

sous-problème VitesseAltitude

applications des formules mathématiques de l’analyse précédente

$$ V = V0 + g*T - Ue*(X + \frac{X^2}{2} + \frac{X^3}{3} + \frac{X^4}{4} + \frac{X^5}{5})$$$$ Z = Z0 - V0*T - g \frac{T^2}{2} + Ue*T*(\frac{X}{2} + \frac{X^2}{6} + \frac{X^3}{12} + \frac{X^4}{20} + \frac{X^5}{30})$$

sous-probleme Alunissage

Algorithme

In [17]:
import numpy as np
import sys
# constantes en unité SI (kg/m/s)
g  = 1.6     # gravité
Ue = 2900.   # vitesse d'ejection
#
def Lecture_Cde(Me,dt):
    """ lecture de la commande (poussée) avec test carburant """
    T=dt
    if Me>0 :
        # lecture poussee
        while True:
            ch = input("Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = ")[0]
            if (ch>='0') and (ch<='9') :
                Qe = int(ch)*10 # pousse en kg/s
                break;
            if (ch=='Q') : sys.exit(1)
            print("Erreur: entrer un chiffre entre 0 et 9 ")
        # test si reserve de fuel suffisante
        if (Me-Qe*T)<0 :
            # temps restant d'utilisation du fuel
            T = Me/Qe
    else:
        print("Plus de fuel ")
        Qe = 0
        # calcul du temps T pour parcourir Z0 (alunissage)
        T = (-V0 + np.sqrt(V0*V0 + 2*g*Z0)) / g
    return Qe,T
In [18]:
def VitesseAltitude(v0,Z0,X,T):
    """ calcul de la nvlle vitesse en fonction de la vitesse init V0
        un débit sans dimension X=Qe*T/M0 de fuel, pendant un temps T
        ainsi que de la nouvelle altitude du LEM
    """
    global g,Ue
    V = V0 + g*T - Ue*(X + X*X/2. + X**3/3. + X**4/4. + X**5/5.)
    Z = Z0 - V0*T - g*T*T/2. + Ue*T*(X/2. + X*X/6. + X**3/12. + X**4/20. + X**5/30.)
    return V,Z
In [19]:
def Alunissage(V0,Z0,M0,Qe):
    """ calcul etat a Z=0 a partir d'une CI V0,Z0,M0 et une poussée Qe """
    global g,Ue
    # calcul du temps T pour alunissage par approximation successive
    T0 = 0
    while np.abs(Z0) > 1.e-2:
            T= (-V0 + np.sqrt(V0*V0 + 2*(g-Ue*Qe/M0)*Z0)) / (g-Ue*Qe/M0)
            V0,Z0 = VitesseAltitude(V0,Z0,Qe*T/M0,T)
            T0 = T0+ T
    return V0,Z0,T0
In [20]:
# conditions initiales
Z0 = 190000. # position
V0 = 1580.   # et vitesse
M0 = 15000.  # masse initiale du LEM
Me = 8000.   # dont une masse de fuel
t  = 0.      # temps simulation
dt = 10.     # pas en temps entre chaque commande
#
print("Simulation alunissage")
while np.abs(Z0)>1.e-2 :
    Qe,T = Lecture_Cde(Me,dt)
    # calcul de la nouvelle position du LEM
    V1,Z1 = VitesseAltitude(V0,Z0,Qe*T/M0,T)
    # test si alunnissage
    if Z1 < 1.e-2 :
        V1,Z1,T = Alunissage(V0,Z0,M0,Qe)
    # mise a jour de la position du LEM
    Z0 = Z1;      V0 = V1
    Me = Me-Qe*T; M0 = M0-Qe*T
    t  = t + T
    print("t=",int(t),"s Z=",int(Z0),"m V=",int(V0),"m/s fuel=",int(Me),"kg")
# fin simulation
print("Alunissage avec une vitesse ",int(V0)," m/s")
if V0<=0.5 :
    print("Alunissage parfait")
elif V0<=5. :
    print("Bon alunissage, mais perfectible")
elif V0<=27.:
    print("Accident à l'alunissage. Attendez les secours en esperant que vous avez assez d'oxygene !!!")
else :
    print("Crash fatal: aucun survivant")
Simulation alunissage
Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = 9
t= 10 s Z= 175007 m V= 1416 m/s fuel= 7100 kg
Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = q
Erreur: entrer un chiffre entre 0 et 9 
Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = 9
t= 20 s Z= 161708 m V= 1241 m/s fuel= 6200 kg
Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = 9
t= 30 s Z= 150227 m V= 1052 m/s fuel= 5300 kg
Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = 9
t= 40 s Z= 140710 m V= 848 m/s fuel= 4400 kg
Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = 9
t= 50 s Z= 133324 m V= 625 m/s fuel= 3500 kg
Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = 9
t= 60 s Z= 128268 m V= 381 m/s fuel= 2600 kg
Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = 0
t= 70 s Z= 124370 m V= 397 m/s fuel= 2600 kg
Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = 9
t= 80 s Z= 121717 m V= 128 m/s fuel= 1700 kg
Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = 9
t= 90 s Z= 121908 m V= -172 m/s fuel= 800 kg
Entrez la valeur de la poussée (0-9) (en 10eme kg/s) = 9
t= 98 s Z= 124747 m V= -471 m/s fuel= 0 kg
Plus de fuel 
t= 886 s Z= 0 m V= 788 m/s fuel= 0 kg
Alunissage avec une vitesse  788  m/s
Crash fatal: aucun survivant

Exercice: simulation d’un lancer de balle

Angry-Birds

Modèle physique:

Algorithme

Modèle mathématique:

Ce modèle admet une solution analytique simple pour la position $x_0$

$$ x_0 = \frac{ 2 V_0^2 \sin{\alpha} \cos{\alpha}}{g} $$

Objectif du programme

Écrire un programme Python qui simule le jeux angry birds en mode texte. On pose un bloc à une distance $L$ du joueur, qui doit envoyer une balle pour le démolir (i.e. atteindre $L$ avec une précision donnée).

Le joueur choisit 2 paramètres à valeurs discrètes:

  • l’angle $\alpha$ (de 1 à 8 pour un angle de 10 à 45 degré par pas de 5)
  • la vitesse initiale $V_0$ (de 1 à 5 par pas 1).

Le joueur a 3 essais, et pour chaque essai, le programme indique la distance atteinte par la balle par rapport au bloc visé.

Bibliographie

In [ ]: