Marc BUFFAT, dpt mécanique, Université Lyon 1
inspiré par le cours "Engineering Computations" du Pr L. Barba (Washington Univ.)
# bibliothéque
%matplotlib inline
import numpy as np
from matplotlib import pyplot as plt
plt.rc('font', family='serif', size='14')
# parametres
from validation.validation import info_etudiant, bib_validation, test_function
from validation.valide_markdown import test_markdown, test_code
from IPython.display import display, Markdown, clear_output
bib_validation('cours','MGC2028L')
from Oscillation import test1,test2,test3,test4,test5,test6,test7
notebook_name = "TP_vibration.ipynb"
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
_uid_ = NUMERO_ETUDIANT
np.random.seed(_uid_)
printmd("**Etudiant** {} {} id={}".format(NOM,PRENOM,NUMERO_ETUDIANT))
# parametres
_mcl_ = ['énergie','cinétique','potentielle','période','erreur','amortie','oscillation', 'forcage']
_omega0 = np.random.randint(1,10)
# systeme
_m = np.round(0.1+2*np.random.rand(),2)
_k = _m*_omega0**2
_b = np.round(0.1+0.5*np.random.rand(),2)
# CI
_x0 = np.round(0.5*(2*np.random.rand()-1),2)
_v0 = 0.
# forcage
_omega = _omega0/2.
_A = np.round(_x0*_omega0**2*0.25*(0.1+np.random.rand()),2)
if (np.abs(_A)<0.5): _A = 0.5
#
printmd("**parametres:** masse m={}, raid.k={}, amort.b={} Forcage: omega={},A={} CdtsInit: x0={},v0={}".format(_m,_k,_b,_omega,_A,_x0, _v0))
Etudiant NBGrader Course id=2592889824
parametres: masse m=0.27, raid.k=17.28, amort.b=0.21 Forcage: omega=4.0,A=6.54 CdtsInit: x0=0.44,v0=0.0
Le système oscillant mécanique le plus simple est une masse $ m $ attachée à un ressort, sans frottement. Cependant, ces systèmes sont soumis à des frottements représentés par un amortisseur mécanique
La loi de Newton appliquée au système masse-ressort général (amorti) s'écrit:
où
En divisant par $m$ et en notant $\omega^2 = \frac{k}{m}$, ce système (1) est équivalent à
que l'on peut l'écrire comme un système (2) de deux équations différentielles d'ordre 1,
\begin{equation} \mbox{(2)}\;\;\;\left\{ \begin{aligned} \dot{x} &= v, \nonumber\\ \dot{v} &= - \omega_0^2 x - bv \end{aligned} \right. \end{equation}En notant $Y(t)$ le vecteur d'état \begin{equation} \mathbf{Y} = \begin{bmatrix} x \\ v \end{bmatrix} \end{equation}
la forme vectorielle du système précédent s'écrit alors:
\begin{equation} \dot{\mathbf{Y}} = \mathbf{F}(\mathbf{Y},t) \mbox{ avec } \mathbf{F}(\mathbf{Y},t) = \begin{bmatrix} v \\ - \omega_0^2\,x - b\,v \end{bmatrix}. \end{equation}Pour résoudre le problème, nous allons en écrire une fonction MasseRessortAmorti pour décrire notre modèle.
On définira en particulier les variables omega0 et ben fonction des paramètres du problème. Ces 2 variables seront définis comme des variables globales dans la fonction. Cela permet de les définir en dehors de la fonction et de les utiliser dans la fonction dans le but de permettre une modification facile en dehors de la fonction.
Écrire cette fonction python dans la cellule ci-dessous
def MasseRessortAmorti(y):
'''
calcul le second membre du système masse ressort avec amortissement
Arguments
---------
y : vecteur numpy de dimension 2 [x, v]
Retour
-------
second membre (vecteur numpy de dimension 2)
'''
global omega0, b
...
...
Dans la cellule suivante:
MasseRessortAmorti(y) définie ci-dessusprintmd("**parametres:** masse m={} [kg], raid. k={} [kg/s^2], amort. b={} [s^-1] CdtsInit: x0={} [m],v0={} [m/s]".format(_m,_k,_b,_x0, _v0))
parametres: masse m=0.27 [kg], raid. k=17.28 [kg/s^2], amort. b=0.21 [s^-1] CdtsInit: x0=0.44 [m],v0=0.0 [m/s]
# definition de la fonction et des variables globales
b = 0
omega0 = 0
### BEGIN SOLUTION
m = _m
k = _k
b = _b
omega0 =np.sqrt(k/m)
def MasseRessortAmorti(Y):
'''
calcul le second membre du système masse ressort avec amortissement
Arguments
---------
Y : vecteur d'etat [x, v]
Retour
-------
derivs: derivée du vecteur d'etat
'''
global omega0,b
derivs = np.array([Y[1], -omega0**2*Y[0]-b*Y[1]])
return derivs
### END SOLUTION
# Verification: appel de la fonction pour y=0.1, v=0. et t=0
F0 = None
Y0 = None
### BEGIN SOLUTION
Y0 = [0.1,0.]
F0 = MasseRessortAmorti(Y0)
print(F0,type(F0))
### END SOLUTION
[ 0. -6.4] <class 'numpy.ndarray'>
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(test_code(notebook_name,'cell-verif3','MasseRessortAmorti'))
assert(np.abs(omega0**2*_m/_k -1.0)<1.e-6)
assert(np.allclose(MasseRessortAmorti([1,0]),[0,-omega0**2]))
### END HIDDEN TESTS
Pour calculer la solution numérique du système d'EDO
$$\dot{\mathbf{Y}} = \mathbf{F}(\mathbf{Y}(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 le nombre de temps $N$, ce qui permet de définir un vecteur T contenant les $N$ temps où on calcule numériquement la solution, et qui est donnée par :
$$ T_i = i\Delta t \mbox{ pour } i=0,N-1 $$On calcule ensuite la solution numérique sous forme d'une matrice Y de dimension $(N,2)$ qui pour chaque ligne i contient la valeur numérique de $\mathbf{Y}(T_i)$ au temps $T_i$
la condition initiale fournit la première valeur de $\mathbf{Y}$
$$Y[0,:] = \mathbf{Y}_0$$
on calcule itérativement la valeur de ce vecteur solution $Y[i,:]\approx \mathbf{Y}(t=T[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,:]) \\ \mathbf{Y}[i,:] & = \mathbf{Y}[i-1,:] + \Delta t \,\, \mathbf{F}(\mathbf{Y}_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 :
Cette fonction calcule la valeur numérique Ysol de la solution (qui est une matrice (N,2)). Dans cette fonction :
## 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,:]) * dt*0.5
tm = (T[i]+T[i-1])/2
Y[i,:] = Y[i-1,:] + F(Ym) *dt
return Y
## END SOLUTION
# 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(MasseRessortAmorti,T,Y0)
print(Y,Y.shape,type(Y))
### END SOLUTION
[[ 0.1 0. ] [ 0.068 -0.63328 ] [-0.01642306 -0.84810156]] (3, 2) <class 'numpy.ndarray'>
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(test_function(RK2,test7,MasseRessortAmorti))
### END HIDDEN TESTS
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(Y.size == T.size*2)
assert(test_code(notebook_name,'cell-verif2b',['MasseRessortAmorti','RK2']))
### END HIDDEN TESTS
On définit une période periode à partir de la pulsation propre du système $\omega_0$.
On fait le calcul sur 10 périodes avec 100 pas en temps par période.
tfin N du tableau T et mettre dans T les valeurs des temps $t_i = i\,dt$ où on calcule la solution avec linspace . #
x0 = 0
v0 = 0
tfin = 0
N = 0
T = None
periode = 0
### BEGIN SOLUTION
periode = 2*np.pi/omega0
tfin = 10*periode # simulation time, in number of periods
N = 10*100 + 1
# time array
T = np.linspace(0, tfin, N)
x0 = _x0 # initial position
v0 = _v0 # initial velocity
print("periode=",periode," dt=",T[1]-T[0])
print("CI=",x0,v0)
### END SOLUTION
periode= 0.7853981633974483 dt= 0.007853981633974483 CI= 0.44 0.0
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert((N == 10*100+1) and (T.size == N))
### END HIDDEN TESTS
sol_num1 en appelant la fonction RK2subplot pour tracer les 2 graphes (position et vitesse) dans la même cellule.# iteration en temps
sol_num1 = None
Y0 = None
b = None
### BEGIN SOLUTION
b = _b
Y0 = np.array([x0,v0])
# solution par la méthode de Runge Kutta 2
sol_num1 = RK2(MasseRessortAmorti,T,Y0)
# plot solution
fig = plt.figure(figsize=(10,12))
plt.subplot(2,1,1)
plt.plot(T, sol_num1[:,0], linewidth=2, linestyle='-', label='avec amorti.')
plt.xlabel('Temps [s]')
plt.ylabel('$x$ [m]')
plt.title("Système masse ressort avec amortissement.\n")
plt.subplot(2,1,2)
plt.plot(T, sol_num1[:,1], linewidth=2, linestyle='-', label='avec amorti.')
plt.xlabel('Temps [s]')
plt.ylabel('$\dot{x}$ [m/s]');
### END SOLUTION
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(sol_num1.size == 2*N)
assert(np.allclose(sol_num1[0],[x0,v0]) == True)
assert((np.allclose(sol_num1[0],[_x0,_v0]) == True))
assert(test_code(notebook_name,'cell-verif1c',['RK2','MasseRessortAmorti']))
assert(test_code(notebook_name,'cell-verif1c',['plt.plot','plt.title',
'plt.xlabel','plt.ylabel','plt.subplot']))
### END HIDDEN TESTS
Le système masse-ressort sans amortissement possède une propriété importante d'un point de vue mécanique: c'est un système mécanique conservatif, c.a.d., l'énergie totale du système se conserve au cours du temps. L'énergie totale par unité de masse est la somme de l'énergie cinétique par unité de masse: $Ec = \frac{1}{2} v^2$ et de l'énergie potentielle par unité de masse: $U = \frac{1}{2} \omega_0^2 x^2$ associée à la force de rappel: $ -k x = \frac{d U}{dx}$. On se propose d'étudier cette propriété dans le cas de notre système masse-ressort amorti.
E0 l'énergie par unité de masse du système à l'instant initial.Ec1 = None
Up1 = None
E0 = None
### BEGIN SOLUTION
# calcul de l'energie par unité de masse pour la méthode Runge Kutta 2
Ec1 = 0.5*sol_num1[:,1]**2
Up1 = 0.5*omega0**2*sol_num1[:,0]**2
# energie initiale
E0 = 0.5*omega0**2*x0**2
plt.figure(figsize=(10,8))
plt.plot(T,Ec1+Up1,label="Et",lw=3)
plt.plot(T,Ec1,label="Ec")
plt.plot(T,Up1,label="Up")
plt.xlabel('t [s]')
plt.ylabel("E. par kg")
plt.legend()
plt.title("Energie Ec et Up.");
### END SOLUTION
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert ((Ec1.size == Up1.size) and (Ec1.size == T.size))
assert (np.abs(Ec1[0]+Up1[0]-E0)<1.e-18)
assert(test_code(notebook_name,'cell-verif2d',['plt.plot','plt.title',
'plt.xlabel','plt.ylabel','plt.legend']))
### END HIDDEN TESTS
Pour comparer, on va étudier le même système, mais sans amortissement, en refaisant le même type de calcul, mais en l'adaptant.
sol_num2 en appelant la fonction RK2sol_num2, et pour la solution précédente sol_num1 avec un titre et des labels sur les axes. On utilisera subplot pour tracer les 2 graphes (position et vitesse)Commentaires ci-dessous# iteration en temps
sol_num2 = None
b = None
### BEGIN SOLUTION
b = 0
Y0 = np.array([x0,v0])
# solution par la méthode de Runge Kutta 2
sol_num2 = RK2(MasseRessortAmorti,T,Y0)
# plot solution
fig = plt.figure(figsize=(10,10))
plt.subplot(2,1,1)
plt.plot(T, sol_num1[:,0], linewidth=2, linestyle='-', label='amorti.')
plt.plot(T, sol_num2[:,0], linewidth=2, linestyle='-', label='sans amorti.')
plt.xlabel('Temps [s]')
plt.ylabel('position [m]')
plt.title("Système masse ressort avec amortisseement.\n")
plt.legend();
plt.subplot(2,1,2)
plt.plot(T, sol_num1[:,1], linewidth=2, linestyle='-', label='amorti.')
plt.plot(T, sol_num2[:,1], linewidth=2, linestyle='-', label='sans amorti.')
plt.xlabel('Temps [s]')
plt.ylabel('vitesse [m/s]')
plt.legend();
### END SOLUTION
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(sol_num2.size == 2*N)
assert(np.allclose(sol_num2[0],[x0,v0]) == True)
assert((np.allclose(sol_num2[0],[_x0,_v0]) == True))
assert(test_code(notebook_name,'cell-verif2c',['RK2','MasseRessortAmorti']))
assert(test_code(notebook_name,'cell-verif2c',['plt.plot','plt.title','plt.subplot',
'plt.xlabel','plt.ylabel','plt.legend']))
### END HIDDEN TESTS
Pour ce système sans amortissement
Commentaires(as-t-on conservation de l'énergie dans notre cas ?)Ec2 = None
Up2 = None
### BEGIN SOLUTION
# calcul de l'energie par unité de masse pour la méthode Runge Kutta 2
Ec2 = 0.5*sol_num2[:,1]**2
Up2 = 0.5*omega0**2*sol_num2[:,0]**2
# energie initiale
plt.figure(figsize=(10,8))
plt.plot(T,Ec1+Up1,'--',label="Et1",lw=3)
plt.plot(T,Ec1,label="Ec1")
plt.plot(T,Up1,label="Up1")
plt.plot(T,Ec2+Up2,'--',label="Et2",lw=3)
plt.plot(T,Ec2,label="Ec2")
plt.plot(T,Up2,label="Up2")
plt.xlabel('t [s]')
plt.ylabel("E. par kg")
plt.legend()
plt.title("Energie Ec et Up.");
### END SOLUTION
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert ((Ec2.size == Up2.size) and (Ec2.size == T.size))
assert (np.abs(Ec2[0]+Up2[0]-E0)<1.e-18)
assert(test_code(notebook_name,'cell-verif3d',['plt.plot','plt.title',
'plt.xlabel','plt.ylabel','plt.legend']))
### END HIDDEN TESTS
Ecrire dans la cellule en markdown suivante les commentaires sur les résultats et les courbes obtenues, en particulier sur:
- la forme de la solution numérique
- l'évolution de l'énergie cinétique et potentielle
- la période, l'amplitude (constante ou amortie) de la solution
- la vérification de propriétés de conservation
- l'évolution en temps de la solution et de l'énergie
attention à l'orthographe (sinon vous perdez des points)
Commentaires sur l'analyse des résultats
%%% BEGIN SOLUTION
Pour le système masse ressort sans amortissement, on constate:
Pour le système masse ressort avec amortissement:
Le résultat ci-dessus montre que les oscillations s'atténuent au bout de quelques périodes: les oscillations sont amorties dans le temps. On constate donc que:
%%% END SOLUTION
Le système masse-ressort, comme vous le voyez, peut se comporter de différentes manières. Si le ressort est linéaire et qu'il n'y a pas d'amortissement ou de forçage (comme dans la leçon précédente), le mouvement est périodique. Si nous ajoutons de l'amortissement, le mouvement oscillatoire décroît avec le temps. Avec le forçage, le mouvement peut être un peu plus compliqué et parfois présenter une résonance.
Chacun de ces types de mouvement est représenté par des solutions correspondantes au système différentiel, dictées par les paramètres du modèle et les conditions initiales.
Comment avoir une idée de tous les types de solutions à un système différentiel ? Une méthode puissante pour ce faire est d'utiliser le plan de phase.
Un système de deux équations différentielles du premier ordre:
\begin{eqnarray} \dot{x}(t) &=& f(x, y) \\ \dot{y}(t) &=& g(x, y) \end{eqnarray}avec un vecteur d'état
\begin{equation} \mathbf{Y} = \begin{bmatrix} x \\ y \end{bmatrix}, \end{equation}est appelé un système autonome planaire: planaire, car le vecteur d'état a deux composantes; et autonome (auto-génératrice), car la variable de temps n'apparaît pas explicitement sur le côté droit (qui ne s'appliquerait pas au système masse-ressort entraîné).
Pour les conditions initiales $\mathbf{Y} _0 = (x_0, y_0) $, le système a une solution unique $ \mathbf{Y} (t) = \left (x (t), y (t) \right) $ . Cette solution peut être représentée par une courbe plane sur le plan $ xy $ le plan de phase et est appelée une trajectoire du système.
Pour les différentes solutions numériques, tracer la trajectoire dans l'espace des phases?
# tracer dans l'espace des phases
### BEGIN SOLUTION
fig = plt.figure(figsize=(12,8))
plt.plot(sol_num2[:,0], sol_num2[:,1], '-',linewidth=2,label="sans amort.")
plt.plot(sol_num1[:,0], sol_num1[:,1], '--' ,linewidth=2,label="avec amort.")
plt.plot([x0],[v0],'ok')
plt.xlabel('Position, $x$, [m]')
plt.ylabel('Vitesse, $v$, [m/s]')
plt.title('Espace des phases: système masse ressort\n')
plt.legend()
plt.xlim(-1,1)
plt.ylim(-5,5)
plt.figtext(0.1,0,'$m={:.1f}$, $k={:.1f}$, $b={:.1f}$'.format(m,k,b));
### END SOLUTION
# ne rien écrire dans cette cellule
### BEGIN HIDDEN TESTS
assert(test_code(notebook_name,'cell-code7',['plot(','title(','legend(','sol_num1[','sol_num2[']))
### END HIDDEN TESTS
Ecrire vos commentaires ci-dessous
Commentaires
%%% BEGIN SOLUTION
Le résultat ci-dessus montre que les oscillations s'atténuent au bout de quelques périodes: les oscillations sont amorties dans le temps. On constate donc que:
Le tracé dans l'espace des phases montre que pour un système masse ressort amorti, la solution converge vers l'état d'équilibre (système immobile) avec une trajectoire sous forme de spirale.
La convergence vers cet état d'équilibre est d'autant plus rapide que l'on est proche de cet état d'équilibre.
C'est la caractéristique des systèmes oscillatoires avec amortissement.
%%% END SOLUTION
# version
from platform import python_version,uname,platform
print("Systeme :",uname())
print("OS :",platform())
print("version python:",python_version())
print("version numpy :",np.__version__)