Équation de Fisher
C'est un modèle d'évolution de population (évolution d'un gène dominant), un cas particulier des équation KPP (Kolmogorov-Petrovsky-Piskounov) :
Propriétés : existence de front d'ondes stationnaires
C'est un modèle d'évolution de population (évolution d'un gène dominant), un cas particulier des équation KPP (Kolmogorov-Petrovsky-Piskounov) :
Propriétés : existence de front d'ondes stationnaires
Une onde stationnaire se déplaçant à la vitesse \(c\) est une solution particulière de la forme \(u(t,x)=U(x-ct)\). En posant \(z=x-ct\), \(U\) vérifie une équation différentielle ordinaire
On s'intéresse aux ondes stationnaires qui opèrent une transition entre les deux états d'équilibre \(U=1\) et \(U=0\) et vérifiant \(0\leq U\leq 1\).
On démontre que pour tout \(c\geq 2\) il existe une unique onde stationnaire se déplaçant à la vitesse \(c\). Pour \(c<2\) une telle onde n'existe pas.
Construction numérique : en raccordant les solutions des linéarisées autour des deux points d'équilibres et bien sûr odeint (à faire)
Kolmogorov, Petrovskii et Piscounov (1937) ont démontré que pour toute donnée unitiale \(u_0\) vérifiant
alors la solution \(u(x,t)\) converge quant \(t\) tend vers l'infini vers une onde stationnaire se déplaçant à la vitesse \(c=2\).
Avec Python, Numpy et Matplotlib
On utilise les formules
On discrétise alors \([0,T]\times[-a,a]\) et non pas \(\mathbb{R}^+\times\mathbb{R}\) :
On pose \(U_{j,n}\) avec \(0\leq j\leq M+1\) et \(0\leq n\leq N+1\) sensé être une approximation de \(u(t_j,x_n)\). Écrire l'EDP en \((t_j,x_n)\) avec les formules donnent plusieurs schémas (il y a plusieurs choix possibles entre la différence progressive, rétrograde ou une moyenne des deux) : explicite et implicite
Pour éviter d'avoir une équation non linéaire à résoudre dans le schéma implicite, le second membre est évalué en \(t_{j-1},x_n\).
Il faut pour boucler le système (et même l'EDP sur le domaine borné) ajouter des conditions sur le bord. Pour faire simple nous prendrons Dirichlet non homogène \(u(t,-a)=1\) et \(u(t,a)=0\), en accord avec le théorème.
La matrice \(A\) utilisée dans la suite appelée matrice de Dirichlet :
Si \(U^{(j)}\) désigne le vecteur de taille \(N\) contenant \(U_{j,n}\) pour \(1\leq n\leq N\) on peut écrire les deux schémas sous forme matricielle
avec \(F^{j}_n= U_{j,n}(1-U_{j,n})\) si \(2\leq n\neq N\) et \(F^j_1= \frac{1}{h^2}+ U_{1,n}(1-U_{1,n})\)
Comme nous avons un problème d'évolution, la donnée initiale \(u_0\) initialise \(U^{(0)}\) avec \(U_{0,n}=u_0(x_n)\) : le premier schéma est dit explicite et le second implicite (à chaque étape on doit résoudre un système linéaire).
Comme pour les EDO on définit les notions de consistance, de stabilité et de convergence.
Le schéma explicite souffre d'un gros défaut : il est stable si et seulement si \(k\leq 2\) (\(k\) a vocation à devenir petit) et
Si le second membre de l'EDP est une donnée \(f\) (qui ne dépend pas de \(u\)) on a la condition CFL usuelle \(k/h^2\leq 1/2\). On demande donc un pas de temps très petit devant le pas d'espace.
Le schéma implicite est inconditionnellement stable (sous réserve ici d'avoir \(k<2\), ce qui est raisonnable).
Le code avec les créations de matrices particulières de Numpy
def schem_expl(y0,T,a,M,N):
dx, dt = 2*a/(N+1), T/(M+1)
A=2*np.diag(np.ones(N))-np.diag(np.ones(N-1),-1)-np.diag(np.ones(N-1),1)
G=np.eye(N)-dt/(dx**2)*A
Y=np.zeros((M+2,N+2))
tpt=np.linspace(0,T,M+2)
xpt=np.linspace(-a,a,N+2)
Y[0,:]=y0(xpt)
Y[:,0]=1
F=np.zeros(N)
for i in range(M+1):
F=Y[i,1:-1]*(1-Y[i,1:-1])
F[0]=F[0]+1/(dx**2)
Y[i+1,1:-1]=np.dot(G,Y[i,1:-1])+dt*F
return tpt, xpt, Y
schem_expl(y1,10,10,10000,500) (donne l'erreur overflow ...) avec donnée initiale discontinue 1 sur \(]-\infty,-4[\) et 0 ailleurs.
schem_expl(y1,10,10,10000,200) avec un tracé sur une sélection de temps. Vers la fin, la progression de l'onde et la condition au limite imposée en \(a\) ne sont plus valides du point de vue du modèle.
Il suffit d'ajouter
from numpy.linalg import solve
def schem_impl(y0,T,a,M,N):
dx, dt = 2*a/(N+1), T/(M+1)
A=2*np.diag(np.ones(N))-np.diag(np.ones(N-1),-1)-np.diag(np.ones(N-1),1)
G=np.eye(N)+dt/(dx**2)*A
Y=np.zeros((M+2,N+2))
tpt=np.linspace(0,T,M+2)
xpt=np.linspace(-a,a,N+2)
Y[0,:]=y0(xpt)
Y[:,0]=1
F=np.zeros(N)
for i in range(M+1):
F=Y[i,1:-1]*(1-Y[i,1:-1])
F[0]=F[0]+1/(dx**2)
Y[i+1,1:-1]=solve(G, Y[i,1:-1]+dt*F) # résolution du système linéaire
return tpt, xpt, Y
schem_impl(y1,8,10,100,800) donne
À l'aide du schéma implicite, pour visualiser la forme de l'onde :
def y1(x):
return np.ones(np.size(x))*[x<-7]
tpt, xpt, Y = schem_impl(y1,8,10,1000,800)
plt.axes(xlim=(-10,10),ylim=(0,1))
for i in range(0,tpt.size):
if i%80==0:
n=np.max(np.nonzero(Y[i,:]>=0.5))
xd=xpt[n]
plt.plot(xpt-xd,Y[i,:])
plt.show()
tpt, xpt, Y = schem_impl(y1,8,10,1000,800)
tpt, xpt, Y = schem_impl(y1,30,40,10000,800) sur la fenêtre \([-10,10]\) et tracé de l'approximation en \(T=20\) :