{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3 · Fehler und Konvergenz\n",
    "\n",
    "Eine numerische Rechnung ist erst überzeugend, wenn der Fehler bei Verfeinerung systematisch abnimmt. Wir messen am Endzeitpunkt den diskreten $L^2$-Fehler\n",
    "\n",
    "$$\\lVert u_h-u\\rVert_{L^2} \\approx \\left(\\sum_j |u_h(x_j)-u(x_j)|^2h\\right)^{1/2}.$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "from pathlib import Path\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "project = Path.cwd()\n",
    "if not (project / 'galerkin_heat.py').exists() and (project.parent / 'galerkin_heat.py').exists():\n",
    "    project = project.parent\n",
    "if not (project / 'galerkin_heat.py').exists():\n",
    "    from urllib.request import urlretrieve\n",
    "    source = 'https://raw.githubusercontent.com/karkessler/dhbw-mathe3/main/galerkin/galerkin_heat.py'\n",
    "    urlretrieve(source, project / 'galerkin_heat.py')\n",
    "sys.path.insert(0, str(project))\n",
    "\n",
    "from galerkin_heat import l2_error, solve_heat_equation\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "element_counts = np.array([8, 12, 16, 24, 32, 48])\n",
    "spatial_errors = []\n",
    "for elements in element_counts:\n",
    "    # Sehr kleiner Zeitschritt, damit vor allem der Ortsfehler sichtbar wird.\n",
    "    result = solve_heat_equation(elements=elements, dt=0.0001, final_time=0.05)\n",
    "    spatial_errors.append(l2_error(result))\n",
    "\n",
    "spatial_errors = np.array(spatial_errors)\n",
    "for elements, error in zip(element_counts, spatial_errors):\n",
    "    print(f'{elements:3d} Elemente: L2-Fehler = {error:.4e}')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(7, 4))\n",
    "plt.loglog(1.0 / element_counts, spatial_errors, 'o-', label='gemessener Fehler')\n",
    "reference = spatial_errors[-1] * (element_counts[-1] / element_counts) ** 2\n",
    "plt.loglog(1.0 / element_counts, reference, '--', label=r'Referenz $O(h^2)$')\n",
    "plt.xlabel('Gitterweite h')\n",
    "plt.ylabel('L2-Fehler')\n",
    "plt.title('Räumliche Konvergenz')\n",
    "plt.grid(which='both', alpha=0.3)\n",
    "plt.legend()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Zeitdiskretisierung\n",
    "\n",
    "Crank–Nicolson besitzt für glatte Lösungen die Fehlerordnung zwei. Für diesen Versuch verwenden wir ein feines Ortsgitter und variieren nur den Zeitschritt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "time_steps = np.array([0.02, 0.01, 0.005, 0.0025])\n",
    "time_errors = []\n",
    "for dt in time_steps:\n",
    "    result = solve_heat_equation(elements=100, dt=float(dt), final_time=0.1)\n",
    "    time_errors.append(l2_error(result))\n",
    "\n",
    "plt.figure(figsize=(7, 4))\n",
    "plt.loglog(time_steps, time_errors, 'o-', label='gemessener Fehler')\n",
    "reference = time_errors[-1] * (time_steps / time_steps[-1]) ** 2\n",
    "plt.loglog(time_steps, reference, '--', label=r'Referenz $O(\\Delta t^2)$')\n",
    "plt.xlabel('Zeitschritt Δt')\n",
    "plt.ylabel('L2-Fehler')\n",
    "plt.title('Zeitliche Konvergenz')\n",
    "plt.grid(which='both', alpha=0.3)\n",
    "plt.legend()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Fazit\n",
    "\n",
    "Das Beispiel zeigt die typische Arbeitsweise der numerischen Mathematik: Modell formulieren, Variationsproblem aufstellen, Raum und Zeit diskretisieren und die Näherung systematisch gegen eine bekannte Lösung prüfen."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"},
  "language_info": {"name": "python", "version": "3"}
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
