Marc BUFFAT, dpt mécanique, Université Lyon 1
%matplotlib inline
# bibliotheque
import numpy as np
from matplotlib import pyplot as plt
plt.rc('font', family='serif', size='14')
from validation.valide_markdown import test_markdown, test_code
from validation.validation import info_etudiant, bib_validation, test_function
bib_validation('cours','MGC2028L')
from Oscillation import test1,test2,test3,test4,test5,test6
from IPython.display import display, Markdown, clear_output
def printmd(string):
display(Markdown(string))
# test si numero étudiant spécifier
try: NUMERO_ETUDIANT
except NameError: NUMERO_ETUDIANT = None
if type(NUMERO_ETUDIANT) is not int :
NOM, PRENOM, NUMERO_ETUDIANT = info_etudiant()
# parametres spécifiques
notebook_name='TP_EDO.ipynb'
_uid_ = NUMERO_ETUDIANT
np.random.seed(_uid_)
_U0_ = np.random.randint(10,30)
_omega0 = np.random.randint(1,15)
_m = np.round(0.1+2*np.random.rand(),2)
_k = _m*_omega0**2
###
printmd("**Etudiant** {} {} id={}".format(NOM,PRENOM,NUMERO_ETUDIANT))
Etudiant NBGrader Course id=2592889824
On se propose de résoudre numériquement une EDO d'ordre 1, i.e trouver $y(t)$ t.q. $$ \frac{dy}{dt} = f(y,t) \mbox{ avec la condition initiale } y(0)= y_0 $$
Pour cela on se fixe un pas en temps $\Delta t$ et un nombre de pas en temps $n$, ce qui permet de définir un vecteur T contenant les temps où on calcule numériquement la solution, et qui est donnée par :
$$ T_i = i\Delta t \mbox{ pour } i=0,n $$On calcule ensuite la solution numérique sous forme d'un vecteur Y correspondant à la valeur numérique de y(t) aux temps T
$$ Y[i] \approx y(t=T[i]) \mbox{ pour } i=1,n \mbox{ et } Y[0] = Y_0$$Pour calculer les valeurs de Y, on utilise la méthode de Runge Kutta 2, dont les étapes sont données ci-dessous:
on calcule ensuite itérativement $Y_i$ à partir de la valeur précédente $Y_{i-1}$ pour i de 1 à n de la façon suivante:
$$ Y_m = Y_{i-1} + \frac{(T_{i} - T_{i-1})}{2} \, f(Y_{i-1}, T_{i-1})$$
$$ Y_i = Y_{i-1} + (T_{i} - T_{i-1}) \, f(Y_m, T_m) \mbox{ avec } Tm = \frac{T_{i-1}+T_i}{2}$$
Cette méthode demande la création d'une fonction F(y,t) qui renvoie la valeur du second membre de l'EDO $f(y,t)$ pour des valeurs de y et t données. Le calcul de Y se fait ensuite à l'aide d'une boucle itérative en utilisant les relations ci-dessus.
On tire verticalement un projectile avec une vitesse initiale $v_0$, dont la valeur est affichée ci-dessous
La vitesse $v(t)$ est solution de l'équation différentielle suivante:
$$ \frac{dv}{dt} = -g - signe(v) C_f v^2 $$avec $g=9.81$ et $C_f = 0.02$. La fonction signe(v) vaut +1 si $v>0$ et -1 sinon. Elle assure une force de frottement toujours opposée au mouvement.
L'objectif est de calculer quelle est la hauteur maximale $h_{max}$ atteinte par le projectile et à quel instant $t_{max}$.
Pour cela on résout d'abord numériquement cette équation pour calculer la vitesse. On détermine ensuite à quel instant cette vitesse s'annule, ce qui correspond à l'instant où le projectile atteint sa hauteur max. Ensuite on calcule sa trajectoire (en utilisant la méthode vue dans le TP précédent).
Pour résoudre numériquement cette EDO:
print("Vitesse initiale v0={} m/s".format(_U0_))
Vitesse initiale v0=17 m/s
# fonction F(y,t)
## BEGIN SOLUTION
def F(y,t):
g = 9.81
Cf = 0.02
if y < 0:
Cf = -Cf
return -g - Cf*y**2
## END SOLUTION
# verification
F0 = None
## BEGIN SOLUTION
Y0 = _U0_
F0 = F(Y0,0.0)
print(Y0,F0)
if type(F0)== float and F0 < 0:
print("OK")
#le projectile va décélerer
## END SOLUTION
17 -15.59 OK
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
_Y1_ = np.random.rand()
_t1_ = np.random.rand()
assert(np.isclose(F(_Y1_,_t1_), -9.81 - 0.02*_Y1_**2))
### END HIDDEN TESTS
On veut calculer la solution approchée dans un tableau V de taille N fixé. On se fixe aussi le temps Tmax de la simulation.
On part des valeurs de N et Tmax proposées
linspace ( la fonction np.linspace(a,b,N) renvoie un tableau de N valeurs équi-réparties entre a et b)Tmax = 5
N = 101
T = None
V = None
dt = None
## BEGIN SOLUTION
T = np.linspace(0,Tmax,N)
V = np.zeros(N)
dt = T[1]-T[0]
V[0] = _U0_
for i in range(1,N):
Vm = V[i-1] + F(V[i-1],T[i-1])*dt/2.
V[i] = V[i-1] + F(Vm,T[i-1]+dt/2.)*dt
print(V.min(), V.max())
print(V)
## END SOLUTION
-20.27335613382166 17.0 [ 17. 16.23359959 15.49166832 14.77235966 14.07398098 13.39497653 12.73391257 12.08946437 11.46040471 10.84559371 10.24396985 9.65454195 9.07638205 8.50861897 7.95043263 7.40104883 6.85973459 6.32579388 5.79856378 5.27741093 4.76172824 4.25093194 3.74445876 3.24176331 2.74231568 2.24559911 1.75110779 1.2583448 0.76682004 0.27604831 -0.21445263 -0.70474133 -1.19433931 -1.68276895 -2.16955715 -2.65423722 -3.13635061 -3.61544862 -4.09109405 -4.56286268 -5.03034473 -5.49314616 -5.9508899 -6.40321686 -6.84978692 -7.29027972 -7.72439531 -8.15185469 -8.57240023 -8.9857959 -9.39182741 -9.79030226 -10.18104956 -10.56391988 -10.93878488 -11.30553691 -11.66408851 -12.01437182 -12.3563379 -12.68995605 -13.01521303 -13.33211225 -13.64067289 -13.94092911 -14.23292907 -14.51673407 -14.79241767 -15.06006473 -15.31977054 -15.57163994 -15.81578645 -16.05233141 -16.28140317 -16.50313629 -16.71767079 -16.92515138 -17.12572683 -17.31954923 -17.50677342 -17.68755637 -17.86205663 -18.03043382 -18.19284814 -18.34945992 -18.50042923 -18.64591546 -18.78607701 -18.92107094 -19.05105271 -19.17617593 -19.2965921 -19.41245041 -19.5238976 -19.63107773 -19.73413212 -19.83319918 -19.92841435 -20.01990999 -20.10781534 -20.19225644 -20.27335613]
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert (T.size == N) and (V.size == N)
assert (np.isclose(dt, Tmax/(N-1)))
assert (np.isclose(V[1], V[0] + F(V[0]+F(V[0],0)*dt/2.,0.)*dt))
### END HIDDEN TESTS
## BEGIN SOLUTION
plt.figure()
plt.plot(T,V,'b-',label="V")
plt.hlines(0.,T[0],T[-1],colors='r')
plt.title("Solution numérique")
plt.xlabel('t en sec.')
plt.ylabel('vitesse en m/s')
plt.legend()
## END SOLUTION
<matplotlib.legend.Legend at 0x7f3e73b512b0>
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(test_code(notebook_name,'cell-verif12',['plt.plot','plt.title',
'plt.xlabel','plt.ylabel','plt.legend']))
### END HIDDEN TESTS
On obtient une estimation du temps tmax $t_{max}$ en déterminant où dans le tableau des vitesses V la valeur de la vitesse devient, en valeur absolue, minimale.
Calculer cet indice k en utilisant la fonction numpy argmin: la fonction `np.argmin(X) renvoie les indices du ou des minimums du tableau X
Mettre dans la variable k l'indice et en déduire $t_{max}$ que l'on mettra dans Tmax
Affichez la valeur de Tmax et la valeur de la vitesse à cet instant (précision) et vérifiez par rapport au graphe précédent.
Optionnel: comment pourrait-on améliorer simplement la précision de ce calcul (sans le refaire) ?
Mettre dans un tableau Va les vitesses V entre 0 et k (inclus) et de même dans Ta les temps associés. Cela correspond à la partie ascendante de la trajectoire. Vérifier le type et la dimension de ces tableaux
k = None
Tmax = None
### BEGIN SOLUTION
k = np.argmin(np.abs(V))
Tmax = T[k]
print(k,Tmax,V[k])
# ou mieux : interpolation linéaire
#
Ta = T[0:k+1]
Va = V[0:k+1]
print("Verification : ",Ta.size==Va.size)
### END SOLUTION
30 1.5 -0.21445263153507987 Verification : True
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert((Tmax>0) and (Tmax<T[-1]))
assert(Ta.size == Va.size)
### END HIDDEN TESTS
D'après l'étude précédente sur chaque intervalle de temps $[t_i,t_{i+1}]$, on connait la vitesse $u_i$ en $t_i$ et la vitesse $u_{i+1}$ en $t_{i+1}$. On peut donc en déduire la distance parcourue $d_i$ par l'objet entre $t_i$ et $t_{i+1}$ en utilisant la vitesse moyenne avec la relation :
$$ d_i = \frac{u_{i}+u_{i+1}}{2} (t_{i+1} - t_{i}) $$
A partir de la position $x_i$ à $t_i$, on peut alors en déduire la position $x_{i+1}$ à $t_{i+1}$
$$ x_{i+1} = x_i + d_i $$
Connaissant la position $x_0$ à l'instant initiale, on a donc une méthode itérative pour calculer la position du solide au cours du temps.
En créant un tableau numpy Xa pour stocker la position aux instants Ta, calculer les valeurs de Xa en utilisant cette méthode avec une boucle for .
Afficher les valeurs min et max de Xa et en déduire la hauteur $h_{max}$ à mettre dans Hmax
Xa = None
Hmax = 0
## BEGIN SOLUTION
Xa = np.zeros(Ta.size)
for i in range(Ta.size-1):
Xa[i+1] = Xa[i] + (Ta[i+1]-Ta[i])*(Va[i+1]+Va[i])/2.
Hmax = Xa[-1]
print(min(Xa),max(Xa),Hmax)
## END SOLUTION
0.0 11.581217479245128 11.581217479245128
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(Xa.size == Ta.size)
assert(Hmax == Xa[-1])
### END HIDDEN TESTS
La loi de Newton appliquée au système masse ressort sans frottement donne:
\begin{equation} -k x = m \ddot{x} \end{equation}avec les conditions initiales: $x(t=0)=x_0$ et $\dot{x}(t=0)=0$
Montrer que l'équation du mouvement peut s'écrire:
\begin{equation} \ddot{x} + \omega_0^2 x = 0 \end{equation}On définiera le paramètre $\omega_0$ (et son interprétation) et on justifiera dans le compte rendu que dans notre cas cette équation différentielle du second ordre possède une solution analytique qui représente un mouvement harmonique simple (dont on donnera les caractéristiques période, amplitude):
$$x(t) = x_0 \cos(\omega_0 t)$$Pour résoudre numériquement une équation différentielle du second ordre, il faut tout d'abord la transformer en un ensemble de deux équations du premier ordre: dans ce cas, respectivement pour la position et la vitesse:
\begin{eqnarray} \dot{x} &=& v \nonumber\\ \dot{v} &=& -\omega_0^2 x \end{eqnarray}Comme vu en cours, nous écrivons l'état du système comme un vecteur $Y(t)$,
\begin{equation} \mathbf{Y}(t) = \begin{bmatrix} x \\ v \end{bmatrix}, \end{equation}et l'équation differentielle sous forme vectorielle:
\begin{equation} \dot{\mathbf{Y}} = \mathbf{F}(\mathbf{Y}(t),t) \mbox{ avec } \mathbf{F}(\mathbf{Y}(t),t) = \begin{bmatrix} v \\ -\omega_0^2 x \end{bmatrix}. \end{equation}Écrire une fonction MasseRessort pour calculer le second membre $F(Y(t))$ de ce système d'équations différentielles masse-ressort.
MasseRessort(Y,t)
On définira la valeur de $\omega_0$ du problème dans une variable globale omega0, pour pouvoir ensuite l'utiliser dans la fonction en le spécifiant dans la fonction
global omega0
On vérifiera ensuite la fonction en l'appelant avec un vecteur d'état $Y_0$ correspondant à $x=0.1$ et $v=0.0$ pour t=0 et en mettant le résultat
dans F0 . On affichera F0 et son type pour vérifier.
On remarque que l'on passe le temps t comme second argument de la fonction même si $Y$ ne dépend pas explicitement du temps, car on en aura besoin dans la suite pour traiter le cas avec un forcage
printmd("**Paramétres de l'étude:** masse m={} [kg] raideur k={} [N/m]".format(_m,_k))
Paramétres de l'étude: masse m=1.11 [kg] raideur k=187.59 [N/m]
# definition de omega0 et de la fonction MasseRessort(Y,t)
omega0 = 0
### BEGIN SOLUTION
omega0 = np.sqrt(_k/_m)
def MasseRessort(Y,t):
global omega0
'''
calcul le second membre du système masse ressort sans amortissement
renvoie dY: derivée du vecteur d'etat [v ,- ω*ω*x]
'''
global omega0
dY = np.array([Y[1], -omega0**2*Y[0]])
return dY
### END SOLUTION
# Verification: appel de la fonction pour la CI
Y0 = None
F0 = None
### BEGIN SOLUTION
Y0 = [0.1,0.0]
F0 = MasseRessort(Y0,0)
print(F0,type(F0))
### END SOLUTION
[ 0. -16.9] <class 'numpy.ndarray'>
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(test_function(MasseRessort,test1,omega0))
assert(test_code(notebook_name,'cell-verif1','MasseRessort('))
assert(np.abs(omega0/_omega0-1.0)<1.e-6)
assert(np.allclose(MasseRessort([1,1],0),[1,-omega0**2]))
## END HIDDEN TESTS
Pour calculer la solution numérique du système d'EDO
$$\dot{\mathbf{Y}} = \mathbf{F}(\mathbf{Y}(t),t)$$on utilise la méthode de Runge Kutta, mais sous forme matricielle puisque la solution est un vecteur (de dimension 2) et non plus un scalaire.
Pour cela on se fixe le temps de simulation $t_max$ et un nombre de pas en temps $n$, ce qui permet de définir un vecteur T contenant les temps où on calcule numériquement la solution, et qui est donnée par :
$$ T_i = i\Delta t \mbox{ pour } i=0,n $$On calcule ensuite la solution numérique sous forme d'une matrice Y de dimension (n+1,2) qui pour chaque ligne i contient la valeur numérique de $\mathbf{Y}(T_i)$ au temps $T_i$
$$ Y[i,:] \approx \mathbf{Y}(t=T[i]) \mbox{ pour } i=1,n \mbox{ et } Y[0,:] = \mathbf{Y}_0$$On calcule la valeur de ce vecteur solution $Y[i,:]$ avec la méthode numérique de Runge Kutta 2, qui s'écrit sous forme matricielle (en notant $t_m = \frac{t_i + t_{i-1}}{2}$) \begin{align} \mathbf{Y}_m & = \mathbf{Y}[i-1,:] + \frac{\Delta t}{2} \mathbf{F}(\mathbf{Y}[i-1,:],t_{i-1}) \\ \mathbf{Y}[i,:] & = \mathbf{Y}[i-1,:] + \Delta t \,\, \mathbf{F}(\mathbf{Y}_m,t_m). \end{align}
Pour appliquer cette méthode, on demande d'écrire une fonction RK2(F,T,Y0)
qui prend pour arguments les paramètres du problème : la fonction second membre F de l'EDO, le vecteur T des pas en temps et la condition initiale Y0.
Cette fonction calcule la valeur numérique de la solution Y (qui est une
matrice). En utilisant une boucle itérative, on calcule chaque ligne
de Y en utilisant la méthode précédente. A la fin, la fonction renvoie
la valeur Y.
## Fonction RK2(F,T,Y0)
## BEGIN SOLUTION
def RK2(F,T,Y0):
n = T.size
Y = np.zeros((n,Y0.size))
Y[0,:] = Y0
for i in range(1,n):
dt = T[i]-T[i-1]
Ym = Y[i-1,:] + F(Y[i-1,:],T[i-1]) * dt*0.5
tm = (T[i]+T[i-1])/2
Y[i,:] = Y[i-1,:] + F(Ym, tm) *dt
return Y
## END SOLUTION
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(test_function(RK2,test6,MasseRessort))
### END HIDDEN TESTS
Vérification
En utilisant comme vecteur T=[0,0.1,0.2] , utilisez la fonction précédente pour calculer Y en ces instants. On vérifiera le type et la taille de Y.
# Verification
T = np.array([0,0.1,0.2])
Y0 = None
Y = None
### BEGIN SOLUTION
T = np.array([0,0.1,0.2])
Y0 = np.array([0.1,0.])
Y = RK2(MasseRessort,T,Y0)
print(Y,Y.shape,type(Y))
### END SOLUTION
[[ 0.1 0. ] [ 0.0155 -1.69 ] [-0.1665975 -0.5239 ]] (3, 2) <class 'numpy.ndarray'>
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(Y.size == T.size*2)
assert(test_code(notebook_name,'cell-verif1b',['MasseRessort','RK2']))
#assert(test_function(iterationRK2,test3,MasseRessort))
### END HIDDEN TESTS
On étudie le mouvement pour une durée tfin égale à 10 périodes avec la valeur de la condition initiale Y0 donnée précédemment.
Pour étudier la précision de la méthode, on va faire le calcul en choisissant différents pas en temps, pour ensuite comparer les solutions calculées.
Pour cela on choisit de construire 3 tableaux de temps, pour calculer 3 tableaux solution:
Calculer tfin et définir ces 3 tableaux T1,T2,T3 avec la fonction
numpy linspace . Afficher le pas en temps pour chacun
de ces tableaux et vérifier qu'ils sont bien multiples de 2.
Puis calculer la valeur de la numérique avec chacun de ces tableaux en utilisant la fonction RK2 précédente et en mettant la solution
dans les matrices Y1, Y2 et Y3. Pour chaque cas, afficher la taille et
le type de la solution calculée.
# parametres
Y0 = None
tfin = 0
T1 = None
T2 = None
T3 = None
### BEGIN SOLUTION
Y0 = np.array([0.1,0.0])
period = 2*np.pi/omega0
tfin = 10*period
T1 = np.linspace(0,tfin,10*20+1)
T2 = np.linspace(0,tfin,10*40+1)
T3 = np.linspace(0,tfin,10*80+1)
dt = period/100
print(T1[1]-T1[0],T2[1]-T2[0],T3[1]-T3[0])
Y1 = RK2(MasseRessort,T1,Y0)
print(Y1.shape,type(Y1))
Y2 = RK2(MasseRessort,T2,Y0)
print(Y2.shape,type(Y2))
Y3 = RK2(MasseRessort,T3,Y0)
print(Y3.shape,type(Y3))
### END SOLUTION
0.0241660973353061 0.01208304866765305 0.006041524333826525 (201, 2) <class 'numpy.ndarray'> (401, 2) <class 'numpy.ndarray'> (801, 2) <class 'numpy.ndarray'>
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(Y1.size == 2*T1.size)
assert(Y2.size == 2*T2.size)
assert(Y3.size == 2*T3.size)
### END HIDDEN TESTS
En utilisant matplotlib, comparer sur 2 figures la position $x(t)$ et la vitesse $v(t)$ calculées dans les 3 matrices Y1,Y2 et Y3.
# tracer
### BEGIN SOLUTION
fig = plt.figure(figsize=(12,10))
plt.subplot(1,2,1)
plt.plot(T1,Y1[:, 0], linewidth=2, label='T1')
plt.plot(T2,Y2[:, 0], linewidth=2, label='T2')
plt.plot(T3,Y3[:, 0], linewidth=2, label='T3')
#plt.xlabel('Temps [s]')
plt.ylabel('$x$ [m]')
plt.title("Position : système masse ressort.\n")
plt.legend();
plt.subplot(1,2,2)
plt.plot(T1,Y1[:, 1], linewidth=2, label='T1')
plt.plot(T2,Y2[:, 1], linewidth=2, label='T2')
plt.plot(T3,Y3[:, 1], linewidth=2, label='T3')
plt.xlabel('Temps [s]')
plt.ylabel('$u$ [m/s]')
plt.title("Vitesse : système masse ressort.\n")
plt.legend();
### END SOLUTION
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(test_code(notebook_name,'cell-verif22',['plt.plot','plt.title',
'plt.xlabel','plt.ylabel','plt.legend']))
### END HIDDEN TESTS
Pour comparer les 3 solutions, on va calculer la norme de l'écart sur la position et la vitesse à partir des 2 tableaux d'écart: dY1 entre Y1 et Y3 et dY2 entre Y2 et Y3.
Attention Y1 , Y2 et Y3 non pas la même dimension, mais ont des points de calcul en commun. On calculera donc dY1 et dY2 en ces points uniquement.
Pour calculer la norme de l'écart sur la position et sur la vitesse à partir de dY1 et dY2, on pourra utiliser la fonction numpy suivante pour calculer la norme d'un vecteur X en normalisant par la dimension de X
np.linalg.norm(X)/X.size
Mettre les résultats dans ErrX1, ErrX2 , ErrU1, ErrU2 et afficher la valeur de ces écarts.
On calculera aussi la valeur maximum des écarts en valeur absolue sur la position dans ErrXmax1 et ErrXmax2.
Que peut on en conclure ?
dY1 = None
dY2 = None
ErrX1 = None
ErrU1 = None
ErrX2 = None
ErrU2 = None
ErrXmax1 = None
ErrXMax2 = None
## BEGIN SOLUTION
dY1 = Y1 - Y3[::4,:]
dY2 = Y2 - Y3[::2,:]
ErrX1 = np.linalg.norm(dY1[:,0])/T1.size
ErrU1 = np.linalg.norm(dY1[:,1])/T1.size
ErrX2 = np.linalg.norm(dY2[:,0])/T2.size
ErrU2 = np.linalg.norm(dY2[:,1])/T2.size
print("Erreur X ",ErrX1,ErrX2)
print("Erreur U ",ErrU1,ErrU2)
ErrXmax1 = np.max(np.abs(dY1[:,0]))
ErrXmax2 = np.max(np.abs(dY2[:,0]))
print("Erreur max ",ErrXmax1,ErrXmax2)
## END SOLUTION
Erreur X 0.0029687696468651274 0.00039909091186068473 Erreur U 0.03937391875118082 0.005215428233671283 Erreur max 0.10238352920032573 0.019184425427542458
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert((dY1.size == Y1.size) and (dY2.size == Y2.size))
assert((ErrX1 > ErrX2 >0.0) and (ErrU1 > ErrU2 >0.0))
assert(ErrXmax1 > ErrXmax2 > 0.0)
### END HIDDEN TESTS
A partir de la solution la plus précise Y3, on veut déterminer la période de la solution numérique, pour comparer avec la solution analytique.
Pour cela on remarque qu'avec la condition initiale, la vitesse est tout d'abord négative ou nulle, avant de devenir positive au bout d'une demi période. On va donc déterminer l'indice k de la première ligne du tableau Y3
correspondant à une vitesse calculée positive, puis à l'aide de T3
la valeur estimée de la période que l'on mettra dans periode_num,
que l'on comparera avec la valeur exacte calculée dans periode
k = 0
periode_num = 0
periode = 0
## BEGIN SOLUTION
k = np.where(Y3[:,1]>0)[0][0]
periode_num = 2*T3[k]
periode = 2*np.pi/omega0
print("periode :", periode_num, periode)
## END SOLUTION
periode : 0.483321946706122 0.483321946706122
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert((periode > 0.0) and (np.abs(periode-periode_num)<1.e-4))
### END HIDDEN TESTS
# version
from platform import python_version,uname,platform
print("Systeme :",uname())
print("OS :",platform())
print("version python:",python_version())
print("version numpy :",np.__version__)
Systeme : uname_result(system='Linux', node='l2-nbgrader', release='6.1.0-37-amd64', version='#1 SMP PREEMPT_DYNAMIC Debian 6.1.140-1 (2025-05-22)', machine='x86_64') OS : Linux-6.1.0-37-amd64-x86_64-with-glibc2.36 version python: 3.9.13 version numpy : 1.23.0