# ------------------------------------------------------------------------- # # PYTHON for DUMMIES 23-24 # Problème 6 # # Script de test # Vincent Legat # # ------------------------------------------------------------------------- # from numpy import * # ------------------------------------------------------------------------- # # -1- Equations SIR # offertes généreusement par l'équipe didactique ! # # dSdt(t) = - beta*S(i)*I(t) # dIdt(t) = beta*S(i)*I(t) - gamma*I(t) # dRdt(t) = + gamma*I(t) # # def f(u,beta,gamma): dSdt = - beta * u[0] * u[1] dIdt = beta * u[0] * u[1] - gamma * u[1] dRdt = gamma * u[1] return array([dSdt,dIdt,dRdt]) # ------------------------------------------------------------------------- # # -2- Schema de d'Euler explicite d'ordre 1 # def epidemicEuler(Xstart,Xend,Ustart,n,beta,gamma): X = linspace(Xstart,Xend,n+1) # # A MODIFIER ..... [begin] # U = zeros((n+1,3)) U[:,0] = 10000 U[:,1] = 2800 U[:,2] = 7100 # # A MODIFIER ..... [end] # return X,U # ------------------------------------------------------------------------- # # -2- Schema de Taylor classique d'ordre 4 # def epidemicTaylor(Xstart,Xend,Ustart,n,beta,gamma): X = linspace(Xstart,Xend,n+1) # # A MODIFIER ..... [begin] # U = zeros((n+1,3)) U[:,0] = 10000 U[:,1] = 2800 U[:,2] = 7100 # # A MODIFIER ..... [end] # return X,U # ------------------------------------------------------------------------- # # -3- Schema de Runge-Kutta d'ordre 4 # def epidemicRungeKutta(Xstart,Xend,Ustart,n,beta,gamma): X = linspace(Xstart,Xend,n+1) # # A MODIFIER ..... [begin] # U = zeros((n+1,3)) U[:,0] = 10000 U[:,1] = 2800 U[:,2] = 7100 # # A MODIFIER ..... [end] # return X,U # ------------------------------------------------------------------------- # ============================= a so simple class ! ======================= class EpidemicExplMethods(object): "Class of explicit integrators for SIR equations" # # Un constructeur qui est exécutée automatiquement lorsqu'on instancie un # nouvel objet de la classe : elle s'appelle obligatoirement __init__() # def __init__(self,name,order,integrator): self.name = name self.order = order self.f = integrator integrators = [EpidemicExplMethods("Explicit Euler",1,epidemicEuler), EpidemicExplMethods("Taylor",4,epidemicTaylor), EpidemicExplMethods("Runge-Kutta",4,epidemicRungeKutta)] # ========================================================================= def main(): # ------------------------------------------------------------------------------------ # # Script de test # # # ------------------------------------------------------------------------------------ # # -1- Paramètres de la simulation # Nombre initial de susceptibles = 10000 # Nombre initial de contaminés = 2 # Ustart = [10000,2,0] beta = 0.00001315 gamma = 0.01798476 # # -2- Analyse de la convergence des trois méthodes # Xstart = 0; Xend = 50; Uref = 541.867329232525 print(" ============ Exact confirmed cases as reference : Uref(%d) = %14.7e " % (Xend,Uref)) for integrator in integrators: print("\n ================") error = zeros(4) for j in range(4): n = 50*pow(2,j); h = (Xend - Xstart)/n X,U = integrator.f(Xstart,Xend,Ustart,n,beta,gamma) error[j] = abs(U[-1,1]-Uref) print(" ==== %4s (order=%d) n=%6d h=%6.3f : U(%d) = %14.7e : eh(Xend) = %8.2e " % (integrator.name.rjust(15),integrator.order,n,h,Xend,U[-1,1],error[j])) order = mean(log(error[:-1]/error[1:])/log(2)) print(" ================ Estimated order of %s : %.4f " % (integrator.name,order)) # # -3- Comparons notre simulation avec des données réelles ! # Xstart = 0; Xend = 150; m = 10; n = Xend * m # # Pour comprendre pourquoi se confiner est utile :-) # Se confiner permet de réduire la valeur de beta # # Tester les deux lignes qui précèdent ! # Ensuite, on peut imaginer de faire varier beta dans le temps pour être plus réaliste ! # # Xend = 300; beta = beta/2 # Xend = 1200; beta = beta/4 # X,U = epidemicRungeKutta(Xstart,Xend,Ustart,n,beta,gamma) # # -3.1- Données du Japon à partir du 22 janvier 2020 # obtenues ici : [HDX](https://data.humdata.org/dataset/novel-coronavirus-2019-ncov-cases) # dataConfirmed = array([2,1,2,2,4,4,7,7,11,15,20,20,20,22,22,45,25,25,26,26,26,28,28,29,43,59,66, 74,84,94,105,122,147,159,170,189,214,228,241,256,274,293,331,360,420,461, 502,511,581,639,639,701,773,839,825,878,889,924,963]) dataRecovered = array([0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,9,9,9,9,12,12,12,13,18,18,22,22, 22,22,22,22,22,22,32,32,32,43,43,43,46,76,76,76,101,118,118,118,118,118, 144,144,144,150,191]) days = arange(dataConfirmed.shape[0]) # # -3.2- Une jolie figure : et hop :-) # Le choix du nombre de 10000 pour estimer le mélange homogène équivalent # est assez arbitraire et rend donc la prédiction assez aléatoire ! # # Toutefois : jouer sur les facteurs beta et gamma # permet d'avoir une compréhension intuitive de la dynamique d'une épidemie # Se confiner permet de réduire beta : refaites la simulation en diminuant beta # d'un facteur deux : qu'observez-vous ? # # AVERTISSEMENT : ceci est un modèle jouet pour illustrer le cours LEPL1104 # Vous n'êtes pas encore des épidémiologistes avertis même pas dilettantes.... # Mais, cela devrait vous permettre de comprendre intuitivement la dynamique # d'une épidémie ! # from matplotlib import pyplot as plt # # Un petit truc pour éviter d'avoir la barre de commandes et un fond blanc # lorsqu'on fait une capture d'écran # import matplotlib matplotlib.rcParams['toolbar'] = 'None' plt.rcParams['figure.facecolor'] = 'lavender' plt.rcParams['axes.facecolor'] = 'lavender' fig = plt.figure("SIR Equations") X=X[::m]; U=U[::m,:]; plt.plot(X,U[:,0],'-b',X,U[:,1],'-r',X,U[:,2],'-g') plt.plot(days[::5],dataConfirmed[::5],'or') plt.plot(days[::5],dataRecovered[::5],'og') plt.text(0,8800,"Susceptibles",color='blue',fontsize=12) plt.text(38,3500,"Infectious",color='red',fontsize=12) plt.text(120,7800,"Recovered",color='green',fontsize=12) plt.show() if __name__ == '__main__': main()