{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\\newcommand{\\nr}[1]{\\|#1\\|}\n",
    "\\newcommand{\\RR}{\\mathbb{R}}\n",
    "\\newcommand{\\N}{\\mathbb{N}}\n",
    "$$\n",
    "### MEU357 - Méthodes numériques pour les équations différentielles. 2022/23.\n",
    "\n",
    "# TP1 - La méthode des différences finies pour l'équation de Poisson.\n",
    "\n",
    "**Remarques importantes** : Dorénavant vous allez programmer des méthodes numériques pour résoudre de manière approchée des EDP, et la complexité de vos programmes augmentera au fur et à mesure. Il faut veiller à respecter les points suivants:\n",
    "\n",
    "- Vos programmes doivent être ***souples***, ce qui veut dire qu'on devra pouvoir changer les données du problème  sans avoir à tout refaire et même modifier facilement les programmes pour les adapter à un problème plus complexe (avec d'autre type de données, par exemple). Vous devrez donc utiliser des variables pour définir vos données, éventuellement des fonctions externes définies en dehors du programme principal, etc...\n",
    "\n",
    "- Vos programmes doivent être ***lisibles et compréhensibles*** par quelqu'un d'autre que vous et par vous mêmes si vous y revenez quelques jours plus tard. Il faut donc utiliser des noms cohérents pour les variables, des commentaires pour expliquer le programme, des titres et légendes pour vos graphiques, etc...\n",
    "\n",
    "Le principe de la **méthode des différences finies** est d'approcher la solution $u$ d'une équation différentielle en un ensemble discret de points du domaine de $u,$ en remplaçant les dérivés de $u$ en ces points par des *quotients de différences finies* qui les approchent. Par exemple, on a pour une fonction $u$ d'une seule variable que sa dérivé en un point $x$ de son domaine vérifie\n",
    "\n",
    "$$\n",
    "u'(x)=\\lim_{h\\to0}\\frac{u(x+h)-u(x)}{h}=\\lim_{h\\to0}\\frac{u(x)-u(x-h)}{h}=\\lim_{h\\to0}\\frac{u(x+h)-u(x-h)}{2h}=\\dots,\n",
    "$$\n",
    "\n",
    "et on peut donc approcher $u'(x)$ par les quotients\n",
    "\n",
    "$$\n",
    "\\frac{u(x+h)-u(x)}{h},\\ \\frac{u(x)-u(x-h)}{h},\\ \\frac{u(x+h)-u(x-h)}{2h},\\dots,\n",
    "$$\n",
    "\n",
    "avec $h\\,$ *petit*.\n",
    "\n",
    "On verra aussi que si $u$ est deux fois dérivable au point $x,$ alors\n",
    "\n",
    "$$\n",
    "u''(x)=\\lim_{h\\to0}\\frac{u(x+h)-2u(x)+u(x-h)}{h^2}.\n",
    "$$\n",
    "\n",
    "On peut donc aussi approcher $u''(x)$ par le quotient de différences finies\n",
    "\n",
    "$$\n",
    "\\frac{u(x+h)-2u(x)+u(x-h)}{h^2},\n",
    "$$\n",
    "\n",
    "avec $h$ *petit*.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercice 1. L'équation de Poisson en dimension 1 d'espace avec conditions aux limites de Dirichlet homogènes. \n",
    "\n",
    "On s'intéresse à la résolution approchée, par la méthode des différences finies, d'un problème aux limites pour l'équation de Poisson en dimension $1$ d'espace sur $\\Omega=]0,1[$, avec conditions aux limites de Dirichlet homogènes\n",
    "\\begin{equation}\n",
    "(P)\\ \\ \\ \\ \\ \\begin{cases}\n",
    "-u''(x)=f(x),\\ \\,x\\in]0,1[,\\\\\n",
    "u(0)=0,\\ \\,u(1)=0,\n",
    "\\end{cases}\n",
    "\\end{equation}\n",
    "où $f\\in\\mathcal{C}^2(\\RR)$ est donnée.\n",
    "\n",
    "L'idée de la méthode des différences finies pour approcher le problème de Poisson (P) est alors la suivante :\n",
    "- Discrétiser le domaine $\\overline{\\Omega}$ ($=[0,1])$ (*le remplacer par un ensemble discret de points*) ;\n",
    "- Remplacer l'opérateur différentiel $u\\mapsto u''$ par l'opérateur aux différences discret\n",
    "  $$u\\mapsto \\frac{u(x+h)-2u(x)+u(x-h)}{h^2}$$\n",
    "  aux points de la discrétisation.\n",
    "\n",
    "--------------------------------------------------------------------------------------------------------------\n",
    "\n",
    "**Question 1.** \n",
    "  Supposons que $f$ est de classe $C^2$. On peut montrer que la solution $u$ de (P) est alors de classe $C^4$ sur $[0,1]$. Montrer qu'il existe une constante $C>0$, qui ne dépend que de $f$, tel que pour tout $x\\in]0,1[$ et pour tout $h>0$ tel que $x\\pm h\\in]0,1[$ on ait\n",
    "  \n",
    "  $$\n",
    "\\Big|u''(x)-\\frac{u(x+h)-2u(x)+u(x-h)}{h^2}\\Big|\\leq Ch^2.\n",
    "  $$\n",
    "\n",
    "En particulier, la constante $C$ est indépendante de $h$. \n",
    "\n",
    "Ce résultat indique que l'opérateur discret est une approximation *d'ordre 2* de l'opérateur continu.\n",
    "\n",
    "---------------------------------------------------------------------------------------------------------\n",
    "\n",
    "En pratique on procède comme suit pour la discrétisation du problème (P) par la méthode des différences finies :\n",
    "\n",
    "- Discrétisation du domaine $[0,1].$\n",
    "\n",
    "Pour chaque entier $M\\ge1$ donné, on pose $h=\\frac{1}{M+1}.$ $h$ est le *pas de la discrétisation*.\n",
    "\n",
    "Associée à chaque $h$ ainsi défini, on construit une discrétisation uniforme de l'intervalle $[0,1],$ de pas $h,$ donnée par les $M+2$ points\n",
    "\n",
    "$$\n",
    "x_j=jh,\\ j=0,\\dots,M+1.\n",
    "$$\n",
    "\n",
    "- Calcul d'une solution approchée aux points de la discrétisation.\n",
    "\n",
    "Soit $u\\in\\mathcal{C}^2([0,1])$ la solution du problème (P). On cherche à calculer des valeurs approchées notées $u_j$ de la solution $u$ aux points $x_j,$ autrement dit on cherche $u_j\\in\\RR,$ pour $j=0,\\dots,M+1,$ tel que $u_j\\simeq u(x_j).$\n",
    "\n",
    "Comme la fonction $u$ est solution de (P), on a\n",
    "\n",
    "$$\n",
    "-u''(x_j)=f(x_j),\\ \\textrm{pour }j=1,\\dots,M, \n",
    "$$\n",
    "\n",
    "et, puisque $x_0=0$ et $x_{M+1}=1,$\n",
    "\n",
    "$$\n",
    "u(x_0)=u(x_{M+1})=0.\n",
    "$$\n",
    "\n",
    "La solution $u$ de (P) vérifie alors\n",
    "\n",
    "$$\n",
    "-\\frac{u(x_j+h)-2u(x_j)+u(x_j-h)}{h^2}\\simeq f(x_j),\\ \\textrm{pour }j=1,\\dots,M,\n",
    "$$\n",
    "\n",
    "ainsi que $u(x_0)=u(x_{M+1})=0.$ Remarquons que $x_j+h=x_{j+1}$ et $x_j-h=x_{j-1},$ si $j\\in\\{1,\\dots,M\\}.$\n",
    "\n",
    "La méthode des différences finies pour le problème (P) consiste alors à, pour $M>1$ entier positif donné, chercher des réels $u_0,\\dots,u_{M+1}$ vérifiant\n",
    "\n",
    "\\begin{equation}\n",
    "(P_h)\\ \\ \\ \\ \\   \\begin{cases}\n",
    "    -\\frac{1}{h^2}\\big(u_{j+1}-2u_{j}+u_{j-1}\\big)=f(x_j),\\ \\  j=1,\\dots,M,\\\\\n",
    "    u_0=u_{M+1}=0,\n",
    "  \\end{cases}\n",
    "\\end{equation}\n",
    "\n",
    "où $h=\\frac{1}{M+1}$ et $x_j=jh,\\ j=0,\\dots,M+1.$\n",
    "\n",
    "-----------------------------------------------------------------------------------------------------------\n",
    "\n",
    "**Question 2.** On introduit une inconnue discrète $(u_0,\\dots,u_{M+1})$, vecteur de $\\RR^{M+2}$ dont les composantes sont les valeurs approchées $u_j,\\ j=0,\\dots,M+1$. Vérifier que le problème discret (P_h) peut s'écrire de manière équivalente :\n",
    "\n",
    "Pour $M>1,\\ M\\in\\N,$ donné, trouver $(u_0,u_1,\\dots,u_M,u_{M+1})\\in\\RR^{M+2}$ tel que\n",
    "\n",
    "\\begin{equation}\n",
    "(S)\\ \\ \\ \\ \\ \\begin{cases}\n",
    "  u_0=u_{M+1}=0,\\\\\n",
    "  (S_h)\\ \\ \\ A_h{U_h}=F,\n",
    "\\end{cases}\n",
    "\\end{equation}\n",
    "\n",
    "où\n",
    "\n",
    "\n",
    "- $A_h\\in\\mathcal{M}_M(\\RR)$ est la matrice\n",
    "  \\begin{equation*}\n",
    "A_h=-\\frac{1}{h^2}\\left[\\begin{array}{rrrrrr}\n",
    "                          -2&1&0&&\\cdots&0\\\\\n",
    "                          1&-2&1&&\\cdots&0\\\\\n",
    "                          0&1&-2&&\\cdots&0\\\\\n",
    "                          \\vdots&&\\ddots&\\ddots&\\ddots&\\vdots\\\\\n",
    "                          0&\\cdots&&1&-2&1\\\\\n",
    "                          0&\\cdots&&0&1&-2\n",
    "                        \\end{array}\n",
    "                      \\right]\\ \\, ;\n",
    "  \\end{equation*}\n",
    "\n",
    "- ${U_h}$ est le vecteur de $\\RR^M,$ ${U_h}=(u_1,\\dots,u_M)^T$ ;\n",
    "\n",
    "- $F\\in\\RR^M$ est le vecteur $\\big(f(x_1),\\dots,f(x_M)\\big)^T,$\n",
    "\n",
    "avec $x_j=jh,\\ j=1,\\dots,M,\\ h=\\frac{1}{M+1}.$\n",
    "\n",
    "-------------------------------------------------------------------------------------------------------------\n",
    "\n",
    "On a alors remplacé le problème continu (P) par le problème discret (P_h) ou le problème équivalent (S).\n",
    "\n",
    "Pour des questions pratiques on va résoudre le système $(S_h)$ sous la forme équivalente \n",
    "\n",
    "$$\n",
    "A_M U_h=h^2 F,\n",
    "$$\n",
    "\n",
    "où $A_M\\in\\mathcal{M}_M(\\RR)$ est la matrice $h^2 A_h$ (qui ne dépend plus explicitement de $h$ ; rappelons que le pas $h$ et $M$ sont liés par la relation $h=1/(M+1)$). \n",
    "\n",
    "On montrera en cours que le système ci-dessus admet une solution, i.e. que $A_M$ est une matrice inversible."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On considère $f(x) = (2\\pi)^2 \\sin(2\\pi x)$, de sorte que la solution du problème $(P)$ est donnée par $u(x) = \\sin(2\\pi x)$. \n",
    "\n",
    "**Question 3.** \n",
    "Implementer en python une fonction `A(M)` qui retourne la matrice $A_M$, pour un nombre de points M donné. \n",
    "\n",
    "**Question 4.** Soit $M=49$. Obtenir la solution approchée $U_h$ en résolvant le système linéaire $A_M U_h =h^2 F$, en utilisant la fonction de numpy `np.linalg.solve`. Tracer sur la même figure  la solution approchée obtenue et la solution exacte du problème $(P)$.\n",
    "\n",
    "---------------------------------------------------------------------------------------------------------------\n",
    "\n",
    "On cherche à illustrer numériquement la convergence du schéma aux différences finies $(P_h)$. On montrera en cours cette convergence : la solution $(u_0,\\dots,u_{M+1})$ du problème discret $(P_h)$ approche la solution $u$ du problème continu $(P)$ au sens suivant :\n",
    "\n",
    "  $$\n",
    "  \\lim_{h\\to0}\\left(\\max_{j=0,\\dots,M+1}|u(x_j)-u_j|\\right)=0.\n",
    "  $$\n",
    "\n",
    "**Question 5.** Pour $M=2^{k}$, et donc pour $h=\\frac{1}{2^k +1}$, $2\\le k\\le 11$, calculer et représenter dans un même graphique la différence en valeur absolue entre la solution exacte aux points $x_j$ et la solution approchée. Observer que cette différence décroit avec $h$.\n",
    "\n",
    "------------------------------------------------------------------------------------------------------------------\n",
    "\n",
    "Pour $h>0$ fixé, on note\n",
    "\n",
    "$$\n",
    "e_h:=\\max_{j=0,\\dots,M+1}|u(x_j)-u_j|\n",
    "$$\n",
    "\n",
    "l'erreur global du schéma associée à une discrétisation de pas $h$. On montrera en cours que l'on a plus que la convergence\n",
    "\n",
    "$$\n",
    "\\lim_{h\\to0} e_h=0,\n",
    "$$\n",
    "\n",
    "on montrera que cette convergence a lieu *au moins à l'ordre 2*, dans le sens que la propriété suivante se vérifie : il existe une constante $C>0$, indépendante de $h$, tel que\n",
    "\n",
    "$$\n",
    "e_h\\leq C h^2.\n",
    "$$\n",
    "\n",
    "**Question 6.**\n",
    "Calculer, pour chaque valeur de $h$ associée à $M=2^{k}$, $2\\le k\\le 11$, l'erreur globale en norme infinie entre la solution approchée et la solution exacte, définie par  \n",
    "\n",
    "$$\n",
    "e_h=\\max_{1\\le j\\le M}|u_{j}-u(x_{j})|.\n",
    "$$\n",
    "\n",
    "Représenter dans une même figure $e/h$ et $e/h^2$ en fonction de $h$ et dans une autre figure $e$ en fonction de $h,$ ainsi qu'une droite de pente 1 et une droite de pente 2, en utilisant une échelle logarithmique. Commenter les résultats obtenus. Que peut-on déduire sur l'ordre du schéma ?\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 304,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def A(M):\n",
    "    return 2 * np.eye(M) - np.eye(M, k=-1) - np.eye(M, k=1)\n",
    "\n",
    "def solution_approchée(f, M, a=0, b=1, ua=0, ub=0):\n",
    "    X = np.linspace(a, b + 1 if b == 0 else b, M+2)\n",
    "    U = np.zeros(M+2)\n",
    "    U[0], U[-1] = ua, ub\n",
    "    \n",
    "    h = (b-a)/(M+1)\n",
    "\n",
    "    delta = np.zeros(M)\n",
    "    delta[0], delta[-1] = ua/np.power(h, 2), ub/np.power(h, 2)\n",
    "    \n",
    "    U[1:-1] = np.linalg.solve(A(M), h**2 * (f(X)[1:-1] + delta))\n",
    "    return X, U"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 305,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, \"$-u''=f(x)$\")"
      ]
     },
     "execution_count": 305,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 45084 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "M = 50\n",
    "h = 1/(M+1)\n",
    "\n",
    "def f1(x):\n",
    "    return (2 * np.pi)**2 * np.sin(2 * np.pi * x)\n",
    "\n",
    "def u1(x):\n",
    "    return np.sin(2 * np.pi * x)\n",
    "\n",
    "x, U_app = solution_approchée(f1, M)\n",
    "plt.figure()\n",
    "plt.scatter(x, U_app, label=f\"$A_M U_h = h² F$\")\n",
    "plt.plot(x, u1(x), label='Solution exacte', color='red')\n",
    "plt.legend()\n",
    "plt.ylabel('f(x)')\n",
    "plt.xlabel('x')\n",
    "plt.title(f\"$-u''=f(x)$\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 307,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Différence en valeur absolue entre la solution exacte et la solution approchée')"
      ]
     },
     "execution_count": 307,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 87576 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "H, E = [], []\n",
    "for k in range(2, 12):\n",
    "    M = 2**k\n",
    "    h = 1/(M+1)\n",
    "    \n",
    "    x, U_app = solution_approchée(f1, M)\n",
    "    \n",
    "    e = np.abs(u1(x) - U_app)\n",
    "    plt.plot(x, e, label=f'{h}')\n",
    "    E.append(np.max(e))\n",
    "    H.append(h)\n",
    "\n",
    "H, E = np.array(H), np.array(E)    \n",
    "\n",
    "plt.xlabel('$h$')\n",
    "plt.ylabel('$\\max_{j=0,\\dots,M+1}|u(x_j)-u_j|$')\n",
    "plt.legend(fontsize=7)\n",
    "plt.title('Différence en valeur absolue entre la solution exacte et la solution approchée')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 308,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Erreur globale en fonction de $h$')"
      ]
     },
     "execution_count": 308,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 25924 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure()\n",
    "plt.plot(H, E/H, label=f'$E_h / h$')\n",
    "plt.plot(H, E/H**2, label=f'$E_h / h^2$')\n",
    "plt.legend()\n",
    "plt.xlabel('$h$')\n",
    "plt.ylabel('Erreur globale')\n",
    "plt.title('Erreur globale en fonction de $h$')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 309,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Erreur pour la méthode mon_schema en echelle logarithmique : log(E) en fonction de log(h)')"
      ]
     },
     "execution_count": 309,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 59984 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure()\n",
    "plt.plot(np.log(H), np.log(E), '+-', label='erreur')\n",
    "plt.plot(np.log(H), np.log(H), '-', label='droite pente 1')\n",
    "plt.plot(np.log(H), 2*np.log(H), '-', label='droite pente 2')\n",
    "plt.legend()\n",
    "plt.xlabel(f'$log(h)$')\n",
    "plt.ylabel(f'$log(E)$')\n",
    "plt.title('Erreur pour la méthode mon_schema en echelle logarithmique : log(E) en fonction de log(h)')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercice 2. Conditions de Dirichlet non homogènes.\n",
    "\n",
    "On approche à présent le problème de Dirichlet $-u''=f$ dans un intervalle $]a,b[$, avec $a,\\ b\\in\\RR$, avec conditions de Dirichlet non-homogènes : \n",
    "\n",
    "$$\n",
    "\\left\\{\\begin{aligned}\n",
    "&-u''(x) = f(x) \\hbox{ sur } ]a,b[ \\\\\n",
    "&u(a) = u_a, u(b) = u_b,\n",
    "\\end{aligned}\\right.$$\n",
    "\n",
    "où $u_a,\\ u_b\\in\\RR$ sont données. En suivant la même démarche que dans l'exercice 1, étant donnée une discrétisation de $]a,b[$ de pas $h=\\frac{(b-a)}{M+1}$, on cherche une solution approchée $(u_0,\\dots,u_{M+1})$ solution du système linéaire suivant : \n",
    "\n",
    "$$\n",
    "\\left\\{\\begin{aligned}\n",
    "&-\\frac{1}{h^2}(u_{i-1} - 2 u_i + u_{i+1}) = f(x_i) \\hbox{ pour } 1\\leq i\\leq M \\\\\n",
    "&u_{0} = u_a, u_{M+1} = u_b,\n",
    "\\end{aligned}\\right.$$\n",
    "\n",
    "où la discrétisation considérée de l'intervalle $[a,b]$ est définie par les $M+2$ points $x_i=a+ih,\\ i=0,\\dots,M+1$.\n",
    "\n",
    "**Question 1.** Mettre ce système sous la forme $A_h U_h = F+\\Delta$, où $\\Delta \\in\\RR^M$ est un vecteur à déterminer.\n",
    "\n",
    "**Question 2.** \n",
    "Donner la solution exacte du problème \n",
    "$$\n",
    "\\left\\{\\begin{aligned}\n",
    "&-u''(x) = 1 \\hbox{ sur } ]1,2[, \\\\\n",
    "&u(1) = 1, u(2) = 2.\n",
    "\\end{aligned}\\right.$$\n",
    "\n",
    "**Question 3.** En suivant la démarche de l'exercice 1, calculer une solution approchée de ce problème par la méthode des différences finies, et représenter dans la même figure la solution exacte et la solution approchée. Refaire dans une autre figure les calculs de la question 5 de l'exercice 1. Refaire l'exercice pour le problème\n",
    "\n",
    "$$\n",
    "\\left\\{\\begin{aligned}\n",
    "&-u''(x) = -\\frac{2}{(x+1)^3} \\hbox{ sur } ]1,2[ \\\\\n",
    "&u(1) = 1/2, u(2) = 1/3,\n",
    "\\end{aligned}\\right.$$\n",
    "\n",
    "dont la solution exacte est donnée par $u(x)=\\frac{1}{x+1}$. Chercher à comprendre les résultats obtenus."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 316,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, \"$-u''=f(x)$\")"
      ]
     },
     "execution_count": 316,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 39952 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "ua, ub = 1/2, 1/3\n",
    "a, b = 1, 2\n",
    "M = 49\n",
    "\n",
    "def f2(x):\n",
    "    return -2/np.power((x+1), 3)\n",
    "\n",
    "def u2(x):\n",
    "    return 1/(x+1)\n",
    "\n",
    "x, U_app = solution_approchée(f2, M, a, b, ua, ub)\n",
    "plt.figure()\n",
    "plt.scatter(x, U_app, label=f\"$A_M U_h = h² F$\")\n",
    "plt.plot(x, u2(x), label='Solution exacte', color='red')\n",
    "plt.legend()\n",
    "plt.ylabel('f(x)')\n",
    "plt.xlabel('x')\n",
    "plt.title(f\"$-u''=f(x)$\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 317,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Différence en valeur absolue entre la solution exacte et la solution approchée')"
      ]
     },
     "execution_count": 317,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 85704 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure()\n",
    "H, E = [], []\n",
    "for k in range(2, 12):\n",
    "    M = 2**k\n",
    "    h = (b-a)/(M+1)\n",
    "    \n",
    "    x, U_app = solution_approchée(f2, M, a, b, ua, ub)\n",
    "    \n",
    "    e = np.abs(u2(x) - U_app)\n",
    "    plt.plot(x, e, label=f'{h}')\n",
    "    E.append(np.max(e))\n",
    "    H.append(h)\n",
    " \n",
    "plt.xlabel('$h$')\n",
    "plt.ylabel('$\\max_{j=0,\\dots,M+1}|u(x_j)-u_j|$')\n",
    "plt.legend(fontsize=7)\n",
    "plt.title('Différence en valeur absolue entre la solution exacte et la solution approchée')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 318,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Erreur globale en fonction de $h$')"
      ]
     },
     "execution_count": 318,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 29860 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "H, E = np.array(H), np.array(E)   \n",
    "plt.figure()\n",
    "plt.plot(H, E/H, label=f'$E_h / h$')\n",
    "plt.plot(H, E/H**2, label=f'$E_h / h^2$')\n",
    "plt.legend()\n",
    "plt.xlabel('$h$')\n",
    "plt.ylabel('Erreur globale')\n",
    "plt.title('Erreur globale en fonction de $h$')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 319,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Erreur pour la méthode mon_schema en echelle logarithmique : log(E) en fonction de log(h)')"
      ]
     },
     "execution_count": 319,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 58000 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure()\n",
    "plt.plot(np.log(H), np.log(E), '+-', label='erreur')\n",
    "plt.plot(np.log(H), np.log(H), '-', label='droite pente 1')\n",
    "plt.plot(np.log(H), 2*np.log(H), '-', label='droite pente 2')\n",
    "plt.legend()\n",
    "plt.xlabel(f'$log(h)$')\n",
    "plt.ylabel(f'$log(E)$')\n",
    "plt.title('Erreur pour la méthode mon_schema en echelle logarithmique : log(E) en fonction de log(h)')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercice 3. Conditions aux limites de Neumann.\n",
    "\n",
    "On approche dans cet exercice le problème $-u'' + u=f$ dans $]0,1[$, avec conditions aux limites dites de Neumann (homogènes) : \n",
    "\n",
    "$$\n",
    "\\left\\{\\begin{aligned}\n",
    "&-u''(x) + u(x) = f(x) \\hbox{ sur } ]0,1[ \\\\\n",
    "&u'(0) = u'(1) = 0.\n",
    "\\end{aligned}\\right.$$\n",
    "\n",
    "On considère la même discrétisation que dans l'exercice 1 de l'intervalle $[0,1]$ et on effectue les approximations suivantes des dérivées de la solution $u$ aux points 0 et 1 :\n",
    "\n",
    "$$\n",
    "u'(0)\\simeq \\frac{u(h)-u(0)}{h},\\ \\ \\ \\ \\ u'(1)\\simeq \\frac{u(1)-u(1-h)}{h}\n",
    "$$\n",
    "\n",
    "On cherche alors, pour $M>0$ donné et $h=\\frac{1}{M+1}$, des valeurs $u_0,\\dots,u_{M+1}$ solutions du système linéaire \n",
    "\n",
    "$$\n",
    "\\left\\{\\begin{aligned}\n",
    "&\\frac{u_{1} - u_0}{h} = 0,\\ \\ \\ \\ \\  \\frac{u_{M+1} - u_M}{h} = 0 \\\\[4pt]\n",
    "&-\\frac{1}{h^2}(u_{i-1} - 2 u_i + u_{i+1})+u_i = f(x_i) \\hbox{ pour } 1\\leq i\\leq M\n",
    "\\end{aligned}\\right.,$$\n",
    "\n",
    "où $x_j = h j$ pour $0\\leq j\\leq M+1$, approchant les valeurs $u(x_0),\\dots,u(x_{M+1})$ de la solution exacte aux points $x_0,\\dots,x_{M+1}$.\n",
    "\n",
    "Le problème discret peut être écrit à nouveau sous la forme $({A_N}_h + I_M)U = F$ où ${A_N}_h$ est une matrice de taille $M\\times M$, $I_M$ la matrice l'identité de taille $M$, $U = (u_1,\\dots,u_{M}) \\in\\R^M$, et où $F = (f(x_1),\\dots,f(x_M))$.\n",
    "\n",
    "**Question 1.** Donner l'expression de la matrice ${A_N}_h$. On peut écrire le système $({A_N}_h + I_M)U = F$ sous la forme équivalente $h^2({A_N}_h + I_M)U = h^2F$. Implémenter la matrice $A_N:=h^2{A_N}_h$ dans une fonction python.\n",
    "\n",
    "**Question 2.** En prenant $f(x) = ((2\\pi)^2 + 1) \\cos(2\\pi x)$ (et on a donc $u(x) = \\cos(2\\pi x)$), refaire les mêmes figures que dans l'exercice 1. Montrer que la convergence dans ce cas est d'ordre $1$ mais pas d'ordre $2$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 326,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, \"$-u''(x) + u(x)=f(x)$\")"
      ]
     },
     "execution_count": 326,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 42364 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def An(M, h):\n",
    "    mat = 2*np.eye(M) - np.eye(M, k=1) - np.eye(M, k=-1)\n",
    "    mat[0,0] = 1\n",
    "    mat[-1, -1] = 1\n",
    "    return mat + h**2 * np.eye(M)\n",
    "\n",
    "def f3(x):\n",
    "    return (np.power(2 * np.pi, 2) + 1 ) * np.cos(2*np.pi * x)\n",
    "\n",
    "def u3(x):\n",
    "    return np.cos(2*np.pi * x)\n",
    "\n",
    "def solution_neumann(f, M, a, b):\n",
    "    X = np.linspace(a, b, M+2)\n",
    "    U = np.zeros(M+2)\n",
    "    h = 1/(M+1)\n",
    "    \n",
    "    U[1:-1] = np.linalg.solve(An(M, h), h**2 * f3(X[1:-1]))\n",
    "    U[0], U[-1] = U[1], U[-2]\n",
    "    return X, U\n",
    "\n",
    "\n",
    "a, b = 0, 1\n",
    "\n",
    "plt.figure()\n",
    "for M in [49, 99, 499]:\n",
    "    x, U_app = solution_neumann(f3, M, a, b)\n",
    "\n",
    "    plt.scatter(x, U_app, marker='+', s=3, label=\"$h^2({A_N}_h + I_M)U = h^2F$ pour M={M}\")\n",
    "plt.plot(x, u3(x), label='Solution exacte', color='red')\n",
    "plt.legend(fontsize=8)\n",
    "plt.ylabel('f(x)')\n",
    "plt.xlabel('x')\n",
    "plt.title(f\"$-u''(x) + u(x)=f(x)$\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 321,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Différence en valeur absolue entre la solution exacte et la solution approchée')"
      ]
     },
     "execution_count": 321,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 62320 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure()\n",
    "H, E = [], []\n",
    "for k in range(2, 12):\n",
    "    M = 2**k\n",
    "    h = (b-a)/(M+1)\n",
    "    \n",
    "    x, U_app = solution_neumann(f3, M, a, b)\n",
    "    \n",
    "    e = np.abs(u3(x) - U_app)\n",
    "    plt.plot(x, e, label=f'{h}')\n",
    "    E.append(np.max(e))\n",
    "    H.append(h)\n",
    " \n",
    "plt.xlabel('$h$')\n",
    "plt.ylabel('$\\max_{j=0,\\dots,M+1}|u(x_j)-u_j|$')\n",
    "plt.legend(fontsize=7)\n",
    "plt.title('Différence en valeur absolue entre la solution exacte et la solution approchée')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 323,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Erreur pour la méthode mon_schema en echelle logarithmique : log(E) en fonction de log(h)')"
      ]
     },
     "execution_count": 323,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 55792 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure()\n",
    "plt.plot(np.log(H), np.log(E), '+-', label='erreur')\n",
    "plt.plot(np.log(H), np.log(H), '-', label='droite pente 1')\n",
    "plt.plot(np.log(H), 2*np.log(H), '-', label='droite pente 2')\n",
    "plt.legend()\n",
    "plt.xlabel(f'$log(h)$')\n",
    "plt.ylabel(f'$log(E)$')\n",
    "plt.title('Erreur pour la méthode mon_schema en echelle logarithmique : log(E) en fonction de log(h)')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercice 4. Méthode d'ordre 2 pour les conditions de Neumann\n",
    "\n",
    "On considère à nouveau le problème $-u'' + u=f$ avec conditions de Neumann : \n",
    "\n",
    "$$\n",
    "\\left\\{\\begin{aligned}\n",
    "&-u''(x) + u(x) = f(x) \\hbox{ sur } ]0,1[ \\\\\n",
    "&u'(0) = u'(1) = 0\n",
    "\\end{aligned}\\right.$$\n",
    "\n",
    "**Question 1 :** En partant de l'égalité\n",
    "$ u(h) = u(0) + hu'(0) + \\frac{h^2}{2} u''(0) + O(h^3), $\n",
    "\n",
    "réécrite sous la forme\n",
    "$\n",
    "\\begin{align}\n",
    "u'(0) &= \\frac{1}{h} (u(h) - u(0)) - \\frac{h}{2} u''(0) + O(h^2) \\\\\n",
    "&= \\frac{1}{h} (u(h) - u(0)) + \\frac{h}{2} (f(0) - u(0))  + O(h^2) \n",
    "\\end{align}\n",
    "$\n",
    "\n",
    "proposer une discrétisation d'ordre deux pour la condition $u'(0) = 0$. Mettre le schéma sous la forme $AU = F + \\Delta$ où $A$ (taille $M\\times M$) et $\\Delta\\in\\RR^M$ sont à déterminer.\n",
    "\n",
    "**Question 2 :** Vérifier numériquement qu'on obtient effectivement une méthode d'ordre $2$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercice 5. Pour aller plus loin.\n",
    "\n",
    "Vous pourrez tester un problème de Neumann dans un intervalle $]a,b[$ autre que $]0,1[$, avec des conditions aux limites de Neumann non homogènes, c'est-à-dire du type $u'(a)=\\alpha,\\ u'(b)=\\beta$, avec $\\alpha,\\ \\beta\\in\\RR$ pas forcément égaux à 0.\n",
    "\n",
    "*Pour considérer un tel problème, vous pouvez partir d'une fonction $u$ que vous choisirez (cette fonction sera la solution de votre problème), prendre comme second membre $f$ la fonction correspondante à $-u''+u$, prendre un intrevalle $[a,b]$ de votre choix et $\\alpha,\\ \\beta$ égaux respectivement aux valeurs $u'(a),\\ u'(b)$.* "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercice 6. Une application au traitement de signal.\n",
    "\n",
    "On s'intéresse à un problème de *débruitage*. Étant donné un signal 1D, $v:[0,1]\\to\\mathbb{R}$, continu, et un paramètre $\\alpha > 0$, on cherche à construire un signal régularisé en résolvant l'équation différentielle\n",
    "\n",
    "$$\n",
    "(D)\\hspace{3cm} \\left\\{\\begin{aligned}\n",
    "&-u''(x) + \\alpha (u(x) - v(x)) = 0 \\hbox{ sur } ]0,1[ \\\\\n",
    "&u'(0) = u'(1) = 0\n",
    "\\end{aligned}\\right.$$\n",
    "\n",
    "On remarque que si $\\alpha\\to\\infty$, on obtient formellement comme solution de l'équation $(D)$ $u=v$. D'autre part, on peut montrer comme on l'a fait pour le problème de Poisson avec conditions aux limites de Dirichlet, que la solution de $(D)$ est une fonction de classe $C^2$. L'idée alors pour obtenir une *régularisation* du signal $v$ est de résoudre $(D)$ pour une valeur de $\\alpha$ grande.\n",
    "\n",
    "On discrétise $(D)$ de la manière suivante. Étant donnée une discrétisation de $]0,1[$ de pas $h=\\frac{1}{M+1}$,  avec $M>0$ donné, définie par les points $x_i=ih,\\ i=0,\\dots,M+1$, on cherche une solution approchée $(u_0,\\dots,u_{M+1})$ vérifiant :\n",
    "\n",
    "$$\n",
    "(D_h)\\hspace{3cm} \\left\\{\\begin{aligned}\n",
    "&u_{0} = u_1, u_{M+1} = u_M\\\\\n",
    "&-\\frac{1}{h^2\\alpha}(u_{i-1} - 2 u_i + u_{i+1}) + u_i = v_i, \\hbox{ pour } 1\\leq i\\leq M,\n",
    "\\end{aligned}\\right.$$\n",
    "\n",
    "où $v_i = v(x_i)$. \n",
    "\n",
    "**Question 1** Mettre le système $(D_h)$ sous la forme $A_M U = F$ où la matrice $A_M$ et le vecteur $F$ sont à construire. Écrire une fonction python $A(M,alpha)$ construisant la matrice $A_M$.\n",
    "\n",
    "**Question 2** Considérer le signal $v$ défini ci-dessous, auquel on a rajouté un bruit uniforme, à travers la fonction random.random de numpy. Pour $M=99$ et pour les valeurs de $\\alpha=50,100,1000,10000$, résoudre numériquement le système $D_h$. Représenter le signal $v$ ainsi que les solutions approchées obtenues pour les différentes valeurs de $\\alpha$. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "def v(x):\n",
    "    return 4*np.exp(-500*np.square(x-.8)) + np.exp(-50*np.square(x-.2))+.5*np.random.random(x.shape)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
