{"id":883,"date":"2026-07-30T12:23:24","date_gmt":"2026-07-30T12:23:24","guid":{"rendered":"https:\/\/matforge.org\/?p=883","raw":"https:\/\/matforge.org\/?p=883"},"modified":"2026-07-30T12:23:24","modified_gmt":"2026-07-30T12:23:24","slug":"python-debugging-scientific-code-print-statements-profiling","status":"publish","type":"post","link":"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/","title":{"rendered":"Python-Debugging f\u00fcr wissenschaftlichen Code: Vom Druckausweis bis zum Profiling","raw":"Python-Debugging f\u00fcr wissenschaftlichen Code: Vom Druckausweis bis zum Profiling"},"content":{"rendered":"<span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 10<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span><h2>Schl\u00fcssel zum Mitnehmen<\/h2>\n<ul>\n<li>Das Debuggen des wissenschaftlichen Python-Codes folgt einer Eskalationsleiter: <code>print<\/code>, <code>assert<\/code>, <code>pdb<\/code>, Profiling und Tests.<\/li>\n<li>Die meisten wissenschaftlichen Fehler sind numerisch, wie z. B. NAN-Werte, Array-Form-Fehlanpassungen, Pr\u00e4zisionsverlust oder instabile Zeitschritte.<\/li>\n<li><code>print()<\/code> F\u00e4ngt viele Anf\u00e4ngerfehler auf, aber Behauptungen und Debugger erleichtern die Isolierung.<\/li>\n<li>Ein zuverl\u00e4ssiger wissenschaftlicher Debugging-Workflow ist: Versagen Sie es zuverl\u00e4ssig, isolieren Sie das Problem, \u00e4ndern Sie eines, \u00fcberpr\u00fcfen Sie und f\u00fcgen Sie einen Test hinzu.<\/li>\n<li>Profiling kann versteckte Fehler aufdecken, da langsamer Code oft ein Zeichen falscher Array-Logik, versehentlicher Schleifen oder \u00fcberm\u00e4\u00dfiger Zuweisung ist.<\/li>\n<\/ul>\n<p>Das Debuggen von wissenschaftlichem Python-Code ist eine der frustrierendsten F\u00e4higkeiten, die es zu entwickeln gilt. Sie schreiben, was eine einfache W\u00e4rmediffusionssimulation sein sollte, der Code l\u00e4uft fehlerfrei und die Ausgabe ist v\u00f6llig falsch.<\/p>\n<p>Kein Absturz. Keine Spur. Nur lautlos falsche Zahlen.<\/p>\n<p>Das ist die einzigartige Herausforderung des wissenschaftlichen Debuggens. Die gef\u00e4hrlichsten Fehler sind oft diejenigen, die \u00fcberhaupt nicht abst\u00fcrzen. Eine falsche Zahl kann durch Tausende von Iterationen propagieren und ein Ergebnis erzeugen, das physisch plausibel aussieht, bis Sie die Erhaltung, Stabilit\u00e4t oder Konvergenz \u00fcberpr\u00fcfen.<\/p>\n<p>In diesem Handbuch wird erl\u00e4utert, wie Sie wissenschaftlichen Python-Code auf mehreren Ebenen debuggen: Druckanweisungen, Protokollierung, Assertions, interaktives Debuggen, Profiling und Regressionstests.<\/p>\n<p>Egal, ob Sie eine erste FIPY-Simulation durchf\u00fchren oder eine Produktionsforschungs-Pipeline debuggen, dieser Workflow bietet Ihnen eine praktische Struktur.<\/p>\n<h2>Was ist anders beim Debuggen von wissenschaftlichem Code?<\/h2>\n<p>Das wissenschaftliche Debuggen unterscheidet sich vom Debuggen einer Web-App, API oder einer gew\u00f6hnlichen Daten-Pipeline.<\/p>\n<p>Die Zahlen k\u00f6nnen l\u00fcgen. Eine Web-App schl\u00e4gt oft sichtbar fehl: Eine Schaltfl\u00e4che funktioniert nicht, eine API gibt einen Fehler zur\u00fcck oder eine Datenbankabfrage schl\u00e4gt fehl. Wissenschaftlicher Code kann perfekt laufen und trotzdem falsche Ergebnisse liefern. Eine negative Konzentration, ein explodierendes Energiefeld oder eine versteckte NAN k\u00f6nnen nur angezeigt werden, wenn Sie direkt danach suchen.<\/p>\n<p>Korrektheit wird durch die Physik definiert, nicht nur durch die Syntax. Ein Python-Programm, das ausgef\u00fchrt wird, ist syntaktisch g\u00fcltig. Ein wissenschaftliches Programm ist nur dann glaubw\u00fcrdig, wenn es bekannte L\u00f6sungen entspricht, erwartete Mengen bewahrt, Randbedingungen einh\u00e4lt und unter Verfeinerung konvergiert.<\/p>\n<p>Stapelspuren zeigen oft in numerische, scipy oder fipy Interna. Der eigentliche Fehler liegt m\u00f6glicherweise in Ihrem Setup, aber die Fehlermeldung kann tief in einer Bibliothek angezeigt werden. Deshalb braucht das wissenschaftliche Debuggen nicht nur das Lesen von Traceback.<\/p>\n<h2>Der Debugging-Workflow: Ein f\u00fcnfstufiges mentales Modell<\/h2>\n<p>Ein n\u00fctzlicher Debugging-Workflow f\u00fcr den wissenschaftlichen Code folgt f\u00fcnf Schritten.<\/p>\n<h3>Schritt 1: Lassen Sie es zuverl\u00e4ssig scheitern<\/h3>\n<p>Ihre erste Aufgabe besteht darin, einen Testfall zu erstellen, der jedes Mal fehlschl\u00e4gt. Im wissenschaftlichen Kodex bedeutet dies normalerweise, ein minimal reproduzierbares Beispiel zu erstellen, das das falsche Ergebnis liefert.<\/p>\n<p>Sie ben\u00f6tigen nicht die vollst\u00e4ndige Simulation. Sie ben\u00f6tigen den kleinsten Code, der den Fehler demonstriert.<\/p>\n<pre><code class=\"language-python\">import numpy as np\n\n# Full simulation may be hundreds of lines.\n# Start with the smallest failing update step.\n\nphi = np.ones((10, 10))\nphi[:] = 0.5\n\ngradient = np.gradient(phi)[0]\nphi_new = phi - 0.1 * gradient\n\nprint(f\"min(phi_new) = {np.min(phi_new)}\")\nprint(f\"max(phi_new) = {np.max(phi_new)}\")\n<\/code><\/pre>\n<p>Durch die Isolierung des Aktualisierungsschritts erstellen Sie einen reproduzierbaren Fall. Dies ist die Grundlage f\u00fcr jeden sp\u00e4teren Debugging-Schritt.<\/p>\n<h3>Schritt 2: Teilen und erobern<\/h3>\n<p>Wenn Sie einen Fall nicht haben, isolieren Sie ihn weiter. Suchen Sie den f\u00fcr den Fehler verantwortlichen Modul, Funktion, Linie oder Physik.<\/p>\n<p>F\u00fcr den Simulationscode besteht eine n\u00fctzliche Taktik darin, jeden Physikbegriff separat zu testen. Wenn Ihr Modell Diffusion, Reaktion und Advektion kombiniert, testen Sie jedes Einzelne, bevor Sie das gekoppelte System debuggen.<\/p>\n<pre><code class=\"language-python\">solution_diffusion = solve_diffusion_only(initial_conditions, dt)\nassert np.all(solution_diffusion &gt;= 0), \"Diffusion produced negative values\"\n\nsolution_reaction = solve_reaction_only(initial_conditions, dt)\nassert np.all(solution_reaction &gt;= 0), \"Reaction produced negative values\"\n\nsolution_coupled = solve_coupled(initial_conditions, dt)\n<\/code><\/pre>\n<p>Dies verengt den Suchraum. Anstatt die gesamte Simulation zu debuggen, debuggen Sie einen Term oder einen Kopplungsmechanismus.<\/p>\n<h3>Schritt 3: \u00c4ndern Sie jeweils eine Sache<\/h3>\n<p>Das wissenschaftliche Debuggen wird verwirrend, wenn Sie mehrere Variablen gleichzeitig \u00e4ndern. Wenn Sie den Zeitschritt, die Randbedingungen, die Solvertoleranz und die Anfangsbedingung zusammen \u00e4ndern, k\u00f6nnen Sie nicht wissen, welche \u00c4nderung wichtig ist.<\/p>\n<p>\u00c4ndern Sie einen Parameter. laufen vergleichen dokumentieren<\/p>\n<p>Verwenden Sie die Versionskontrolle, um \u00c4nderungen zu verfolgen. Selbst einfache Commits wie \u201e\u00c4nderte DT von 0,01 auf 0,001\u201c k\u00f6nnen Stunden sp\u00e4ter sparen.<\/p>\n<h3>Schritt 4: Verwenden Sie den Debugger<\/h3>\n<p>Wenn Drucke und Zusicherungen nicht ausreichen, verwenden Sie <code>pdb<\/code>, <code>ipdb<\/code> oder einen IDE-Debugger. Mit einem Debugger k\u00f6nnen Sie die Ausf\u00fchrung anhalten, Variablen pr\u00fcfen, Code durchlaufen und \u00fcberpr\u00fcfen, warum sich ein Wert ge\u00e4ndert hat.<\/p>\n<h3>Schritt 5: Zur Testsuite hinzuf\u00fcgen<\/h3>\n<p>Nachdem Sie einen Fehler behoben haben, f\u00fcgen Sie einen Test hinzu, der ihn abgefangen h\u00e4tte. Dies verhindert, dass der gleiche Fehler sp\u00e4ter zur\u00fcckkehrt.<\/p>\n<pre><code class=\"language-python\">def test_no_negative_phases():\n    \"\"\"Verify phase field stays non-negative after diffusion.\"\"\"\n    phi = np.ones((10, 10))\n    phi = diffusion_step(phi, dt=0.01)\n    assert np.min(phi) &gt;= 0, f\"Got min value {np.min(phi)}\"\n<\/code><\/pre>\n<p>Der Workflow ist einfach: isolieren, dividieren, \u00e4ndern, \u00e4ndern, debuggen und testen. Der Rest dieser Anleitung erkl\u00e4rt, welche Tools auf jeder Ebene verwendet werden sollen.<\/p>\n<h2>Stufe 1: Anweisungen drucken und protokollieren<\/h2>\n<p><code>print()<\/code> ist immer noch n\u00fctzlich. Es ist das einfachste Debugging-Tool und f\u00e4ngt viele fr\u00fche Fehler auf. Der Schl\u00fcssel ist, es mit Struktur zu verwenden.<\/p>\n<h3>Das Problem mit schlechten Drucken<\/h3>\n<p>Ein h\u00e4ufiger Anf\u00e4ngerfehler ist das Drucken von Werten ohne Kontext.<\/p>\n<pre><code class=\"language-python\">for i in range(100):\n    print(phi[i])\n<\/code><\/pre>\n<p>Nach ein paar Iterationen haben Sie viele Zahlen ohne klare Bedeutung. Sie wissen nicht, welche Variable, Zeitschritt oder Bedingung jede Zeile erzeugt hat.<\/p>\n<h3>Der richtige Weg: Strukturierte Drucke<\/h3>\n<p>Alles beschriften. Schlie\u00dfen Sie den Zeitschritt, den Variablennamen, das Minimum, das Maximum und jede konservierte Menge ein, die Ihnen wichtig ist.<\/p>\n<pre><code class=\"language-python\">import numpy as np\n\nprint(\n    f\"[t={t:06.4f}] \"\n    f\"min(phi)={np.min(phi):.6f}, \"\n    f\"max(phi)={np.max(phi):.6f}\"\n)\n\nprint(\n    f\"[t={t:06.4f}] \"\n    f\"energy={np.sum(phi * phi):.6f}, \"\n    f\"mass={np.sum(phi):.6f}\"\n)\n<\/code><\/pre>\n<p>Diese Ausgabe ist einfach zu scannen:<\/p>\n<pre><code>[t=0.0010] min(phi)=0.000000, max(phi)=1.000000\n[t=0.0010] energy=0.850000, mass=1.000000\n[t=0.0020] min(phi)=0.000000, max(phi)=1.023456\n[t=0.0020] energy=0.873456, mass=1.012345\n<\/code><\/pre>\n<p>Sie sehen sofort, dass der Maximalwert \u00fcber 1,0 gestiegen ist und die Masse ge\u00e4ndert wird. Das sind n\u00fctzliche Hinweise.<\/p>\n<h3>Protokollierung vs. Druckanweisungen<\/h3>\n<p>Verwenden Sie f\u00fcr ein ernsthaftes Debuggen das Modul <code>logging<\/code>  von Python anstelle von RAW <code>print()<\/code> .<\/p>\n<pre><code class=\"language-python\">import logging\n\nlogging.basicConfig(\n    level=logging.DEBUG,\n    format=\"%(levelname)s: %(message)s\"\n)\n\nlogger = logging.getLogger(__name__)\n\nlogger.debug(\"Running diffusion step at t=%f, dt=%f\", t, dt)\nlogger.warning(\"Energy increased by %.5f\", energy_change)\n<\/code><\/pre>\n<p>Logging hat mehrere Vorteile:<\/p>\n<ul>\n<li>Ebenen wie <code>DEBUG<\/code>, <code>WARNING<\/code> und <code>ERROR<\/code>.<\/li>\n<li>Optionale Zeitstempel und Dateiausgabe.<\/li>\n<li>Einfache Aktivierung oder Deaktivierung ohne L\u00f6schen des Debug-Codes.<\/li>\n<li>Reinigere Diagnose bei langen Simulationsl\u00e4ufen.<\/li>\n<\/ul>\n<p>Eine praktische Regel ist die Verwendung von <code>print()<\/code> f\u00fcr schnelle Erkundungsskripte und <code>logging<\/code> f\u00fcr wiederverwendbare oder produktionstechnische Simulationscodes.<\/p>\n<h2>Stufe 2: Behauptungen und Vernunftpr\u00fcfungen<\/h2>\n<p>Behauptungen sind wie gedruckte Aussagen, die sich weigern, zu schweigen. Sie werfen einen Fehler auf, wenn eine Annahme verletzt wird.<\/p>\n<h3>Das Muster: Annahmen zuerst<\/h3>\n<p>Das n\u00fctzlichste Muster besteht darin, die Eingaben zu \u00fcberpr\u00fcfen, den Solver auszuf\u00fchren und die Ausgaben zu \u00fcberpr\u00fcfen.<\/p>\n<pre><code class=\"language-python\">import numpy as np\n\ndef solve_diffusion(phi, dt, diffusion_coeff):\n    # Preconditions\n    assert np.all(phi &gt;= 0), f\"Negative phase: {np.min(phi)}\"\n    assert np.all(phi &lt;= 1), f\"Phase &gt; 1: {np.max(phi)}\"\n    assert np.isfinite(phi).all(), \"Non-finite input values detected\"\n    \n    # Solver step\n    phi_new = apply_diffusion(phi, dt, diffusion_coeff)\n    \n    # Postconditions\n    assert np.isfinite(phi_new).all(), \"Solver produced NaN or Inf\"\n    assert np.all(phi_new &gt;= 0), f\"Negative output: {np.min(phi_new)}\"\n    \n    return phi_new\n<\/code><\/pre>\n<p>Dadurch wird ein stiller numerischer Fehler in einen eindeutigen Fehler mit Ort und Kontext.<\/p>\n<h3>Die Behauptungsphilosophie<\/h3>\n<p>Bei Behauptungen geht es nicht nur darum, Fehler zu fangen. Sie heften Wanzen an.<\/p>\n<p>Eine Behauptung, die am Anfang eines Solvers fehlschl\u00e4gt, sagt Ihnen, wo die Annahme gebrochen ist. Ein Nan, das Hunderte von Zeilen sp\u00e4ter erscheint, sagt Ihnen viel weniger.<\/p>\n<p>N\u00fctzliche Orte f\u00fcr Behauptungen sind:<\/p>\n<ul>\n<li>Vor und nach dem Solver-Aufruf.<\/li>\n<li>an Modulgrenzen, wo Daten ausgetauscht werden.<\/li>\n<li>Nach Netzoperationen wie Verfeinerung, Restricken oder Vergr\u00f6bern.<\/li>\n<li>Nach dem Anwenden von Randbedingungen.<\/li>\n<\/ul>\n<h3>Eine Warnung vor Behauptungen<\/h3>\n<p>Verwenden Sie die Behauptungen in teuren inneren Schleifen nicht \u00fcberm\u00e4\u00dfig, es sei denn, Sie ben\u00f6tigen sie. Sie k\u00f6nnen Runtime-Overhead hinzuf\u00fcgen und das Debuggen laut machen.<\/p>\n<p>Verwenden Sie Assertions f\u00fcr Invarianten und Vorbedingungen: Bedingungen, die niemals verletzt werden sollten, wenn der Code korrekt ist.<\/p>\n<h2>Level 3: Der Python-Debugger<\/h2>\n<p>Wenn Druckanweisungen nicht ausreichen, verwenden Sie einen interaktiven Debugger. Mit einem Debugger k\u00f6nnen Sie das Programm anhalten, den Status \u00fcberpr\u00fcfen und die Logik durchlaufen.<\/p>\n<h3>Das klassische Werkzeug: PDB<\/h3>\n<p><code>pdb<\/code> ist der integrierte Debugger von Python. Es ist in jeder Python-Installation verf\u00fcgbar.<\/p>\n<p>In IPython k\u00f6nnen Sie nach einem Fehler das Post-Mortem-Debugging verwenden:<\/p>\n<pre><code class=\"language-python\">In [1]: %run simulation.py\n\nIn [2]: %debug\n<\/code><\/pre>\n<p>Dies bringt Sie zum Fehlerpunkt in den Debugger.<\/p>\n<p>Starten Sie f\u00fcr Befehlszeilenskripte das Skript mit dem Debugger:<\/p>\n<pre><code class=\"language-bash\">python -m pdb simulation.py\n<\/code><\/pre>\n<p>N\u00fctzliche Befehle sind:<\/p>\n<ul>\n<li><code>c<\/code>: Weiterf\u00fchrung.<\/li>\n<li><code>n<\/code>: Gehen Sie zur n\u00e4chsten Zeile.<\/li>\n<li><code>s<\/code>: Schritt in einen Funktionsaufruf.<\/li>\n<li><code>b<\/code>: Legen Sie einen Haltepunkt fest.<\/li>\n<li><code>p variable_name<\/code>: Drucken Sie eine Variable.<\/li>\n<li><code>q<\/code>: Beenden Sie den Debugger.<\/li>\n<\/ul>\n<p>Sie k\u00f6nnen auch einen Haltepunkt direkt in Code einf\u00fcgen:<\/p>\n<pre><code class=\"language-python\">def solve_phase_field(phi, dt):\n    import pdb\n    pdb.set_trace()\n    \n    phi_new = update_phase_field(phi, dt)\n    return phi_new\n<\/code><\/pre>\n<h3>IPDB- und IPython-Debugging<\/h3>\n<p><code>ipdb<\/code> ist eine erweiterte Version von <code>pdb<\/code>. Es bietet Tab-Vervollst\u00e4ndigung, bessere Formatierung und ein komfortableres interaktives Erlebnis.<\/p>\n<pre><code class=\"language-bash\">pip install ipdb\n<\/code><\/pre>\n<pre><code class=\"language-python\">import ipdb\nipdb.set_trace()\n<\/code><\/pre>\n<p>F\u00fcr IPython-Benutzer ist <code>%debug<\/code> oft die schnellste Option nach einer Ausnahme.<\/p>\n<h3>IDE-Debugger<\/h3>\n<p>PyCharm, VS Code und Spyder enthalten grafische Debugger. Sie k\u00f6nnen Breakpoints setzen, Variablen pr\u00fcfen, Code durchlaufen und Ausdr\u00fccke ohne Eingabe von Befehlen eingeben.<\/p>\n<p>IDE-Debugger sind hilfreich f\u00fcr die lokale Entwicklung. F\u00fcr Remote-Cluster oder Headless-Server sind <code>pdb<\/code>, <code>ipdb<\/code> und die Protokollierung in der Regel praktischer.<\/p>\n<h3>Wann verwenden Sie welches Werkzeug?<\/h3>\n<table class=\"custom-table\">\n<tbody>\n<tr>\n<th>Szenario<\/th>\n<th>Empfohlenes Werkzeug<\/th>\n<\/tr>\n<tr>\n<td>Schnelles Skript oder fr\u00fche lokale Entwicklung<\/td>\n<td><code>print()<\/code>  plus Behauptungen<\/td>\n<\/tr>\n<tr>\n<td>Fehlerverfolgung in Ipython<\/td>\n<td><code>%debug<\/code><\/td>\n<\/tr>\n<tr>\n<td>Remote-Cluster ohne IDE-Zugriff<\/td>\n<td><code>python -m pdb script.py<\/code><\/td>\n<\/tr>\n<tr>\n<td>Interaktives Debuggen mit besserem Kontext<\/td>\n<td><code>ipdb<\/code> oder IDE-Debugger<\/td>\n<\/tr>\n<tr>\n<td>Diagnose im Produktionsstil<\/td>\n<td><code>logging<\/code> mit strategischen Breakpoints<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Level 4: Profiling, um versteckte Fehler zu finden<\/h2>\n<p>Profiling wird normalerweise als Leistungstool angesehen, hilft aber auch beim Debuggen. Langsamer Code ist oft falscher Code. Profiler k\u00f6nnen zuf\u00e4llige Python-Schleifen, wiederholte Zuordnungen, redundante Berechnungen oder unerwartete Funktionsaufrufe anzeigen.<\/p>\n<h3>cProfile: Finden Sie die Hot-Funktionen<\/h3>\n<pre><code class=\"language-python\">import cProfile\nimport pstats\n\nprofiler = cProfile.Profile()\n\nprofiler.enable()\nsolve_phase_field(phi, dt, n_iterations)\nprofiler.disable()\n\nstats = pstats.Stats(profiler)\nstats.sort_stats(\"cumulative\")\nstats.print_stats(20)\n<\/code><\/pre>\n<p>Dies zeigt, welche Funktionen die meiste Zeit verbrauchen. Wenn eine Hilfsfunktion die Laufzeit unerwartet dominiert, kann sie einen versteckten Fehler oder eine ineffiziente Array-Logik enthalten.<\/p>\n<h3>line_profiler: Finden Sie die Hotlines<\/h3>\n<p>Wenn <code>cProfile<\/code> eine langsame Funktion identifiziert, kann <code>line_profiler<\/code> anzeigen, welche Zeilen innerhalb der Funktion Zeit verbrauchen.<\/p>\n<pre><code class=\"language-bash\">pip install line_profiler\nkernprof -l -v simulation.py\n<\/code><\/pre>\n<p>Dies ist n\u00fctzlich, wenn eine einzelne Zeile innerhalb einer Schleife wiederholt Arrays erzeugt oder versehentlich einen langsamen Vorgang ausf\u00fchrt.<\/p>\n<h3>Speicherprofilierung: versteckte Zuordnungen finden<\/h3>\n<p>Der wissenschaftliche Code verbirgt h\u00e4ufig Speicherfehler wie versehentliche Kopien oder \u00fcberm\u00e4\u00dfige Arrayerstellung in Schleifen. <code>memory_profiler<\/code> kann bei der Verfolgung von Zuordnungen helfen.<\/p>\n<pre><code class=\"language-python\">from memory_profiler import profile\n\n@profile\ndef solve_phase_field(phi, dt):\n    phi_new = apply_diffusion(phi, dt)\n    return phi_new\n<\/code><\/pre>\n<p>Zeilen-f\u00fcr-Linien-Speicherprofile k\u00f6nnen zeigen, wo gro\u00dfe Arrays erstellt und verworfen werden.<\/p>\n<h3>Profiling als Debuggen<\/h3>\n<p>Profiling hilft, wenn ein Solver sowohl falsch als auch langsam ist. Beispielsweise kann ein falscher Schnitt Energie \u00fcber den falschen Bereich berechnen. Ein Profil kann zeigen, dass die Energieberechnung unerwartet teuer ist, was Sie zur\u00fcck zum Array-Vorgang hinweist.<\/p>\n<p>Behauptungen best\u00e4tigen den Fehler. Profiling hilft Ihnen zu finden, wo Sie suchen m\u00fcssen.<\/p>\n<h2>Stufe 5: Testen und Regressionspr\u00e4vention<\/h2>\n<p>Das endg\u00fcltige Debugging-Niveau ist die Pr\u00e4vention. Schreiben Sie nach dem Beheben eines Fehlers einen Test, der den Fehler reproduziert und beweist, dass der Fix funktioniert.<\/p>\n<h3>Das wissenschaftliche Testmuster<\/h3>\n<pre><code class=\"language-python\">import pytest\nimport numpy as np\n\n@pytest.mark.parametrize(\"mesh_size\", [10, 20, 50])\ndef test_energy_decreases(mesh_size):\n    \"\"\"Energy should decrease during simple diffusion.\"\"\"\n    phi = init_phi(mesh_size)\n    energy_history = []\n    \n    for step in range(100):\n        energy_history.append(compute_energy(phi))\n        phi = solve_step(phi, dt=0.01)\n    \n    energy_changes = np.diff(energy_history)\n    assert np.all(energy_changes &lt;= 1e-10)\n<\/code><\/pre>\n<p>Dieser Test \u00fcberpr\u00fcft das physikalische Verhalten, nicht nur die Syntax. Eine korrekte Simulation sollte nicht nur ausgef\u00fchrt werden. Es sollte erwartete Mengen erhalten, Grenzen einhalten und mit der Verfeinerung konvergieren.<\/p>\n<h3>Die Testpyramide f\u00fcr wissenschaftlichen Code<\/h3>\n<ul>\n<li>Unit-Tests sind schnell und pr\u00fcfen einzelne Funktionen wie <code>compute_gradient()<\/code> oder <code>apply_boundary_conditions()<\/code>.<\/li>\n<li>Integrationstests f\u00fchren \u00dcberpr\u00fcfungen auf Solver-Ebene gegen analytische oder hergestellte L\u00f6sungen durch.<\/li>\n<li>Regressionstests vergleichen vollst\u00e4ndige Simulationen mit vertrauensw\u00fcrdigen Baseline-Ausgaben.<\/li>\n<\/ul>\n<p>Das Schl\u00fcsselprinzip ist einfach: Testen Sie die Physik, nicht nur den Codepfad.<\/p>\n<h2>H\u00e4ufige wissenschaftliche Debugging-Fallen<\/h2>\n<h3>Nan und Unendlichkeit<\/h3>\n<p>NAN- und Unendlichkeitswerte sind im wissenschaftlichen Kodex \u00fcblich. Sie treten oft nach Division durch Werte nahe Null, <code>log(0)<\/code> , <code>sqrt<\/code>  negativer Werte oder \u00dcberlauf in Exponentialen auf.<\/p>\n<pre><code class=\"language-python\">phi = np.zeros((100, 100))\nphi[50, 50] = 1.0\n\nresult = phi \/ (phi + 1e-300)\n<\/code><\/pre>\n<p>Nach numerischen Operationen auf endliche Werte pr\u00fcfen:<\/p>\n<pre><code class=\"language-python\">assert np.isfinite(result).all(), (\n    f\"NaN or Inf detected: min={np.nanmin(result)}, max={np.nanmax(result)}\"\n)\n<\/code><\/pre>\n<h3>Array-Form-Fehlanpassungen<\/h3>\n<p>Array-Formfehler sind h\u00e4ufig beim Wechsel zwischen abgeflachten Solver-Arrays und 2D- oder 3D-Feldern.<\/p>\n<pre><code class=\"language-python\">print(f\"phi shape: {phi.shape}\")\nprint(f\"expected shape: {expected_shape}\")\n<\/code><\/pre>\n<p>Zu den h\u00e4ufigsten Ursachen geh\u00f6ren versehentliches Abflachen, Indizierung, die eine Dimension entfernt, und Bibliotheksvariablen, die Daten in einer anderen Form als erwartet speichern.<\/p>\n<h3>Gleitkomma-Pr\u00e4zision<\/h3>\n<p>Vergleichen Sie Gleitkomma-Arrays nicht mit exakter Gleichheit, es sei denn, es wird wirklich eine genaue Gleichheit erwartet. Verwenden Sie toleranzbasierte Vergleiche.<\/p>\n<pre><code class=\"language-python\">np.testing.assert_allclose(phi, phi_new, rtol=1e-6, atol=1e-12)\n<\/code><\/pre>\n<p>Dies ist besonders wichtig f\u00fcr iterative Solver, gemischte Pr\u00e4zision und langzeitige Integrationen.<\/p>\n<h3>Randbedingungsfehler<\/h3>\n<p>Randbedingungsfehler sind h\u00e4ufig, da der Code h\u00e4ufig ausgef\u00fchrt wird, auch wenn die falschen Zellen oder Gesichter eingeschr\u00e4nkt sind.<\/p>\n<pre><code class=\"language-python\"># Wrong: overwrites all cells\nphi[:] = boundary_value\n\n# Better: apply only to selected boundary locations\nphi[boundary_mask] = boundary_value\n<\/code><\/pre>\n<p>Stellen Sie immer sicher, dass Grenzwerte nur dort angewendet werden, wo beabsichtigt und Innenwerte nicht \u00fcberschrieben werden.<\/p>\n<h3>Konvergenz und Stabilit\u00e4t<\/h3>\n<p>Simulationen k\u00f6nnen lautlos auseinander gehen, wenn die Zeitschritte zu gro\u00df oder die Solvertoleranzen zu locker sind. \u00dcberwachen Sie konservierte oder begrenzte Mengen im Laufe der Zeit.<\/p>\n<pre><code class=\"language-python\">if energy &gt; prev_energy * 1.1:\n    print(f\"Energy growing. Reducing dt from {dt} to {dt \/ 2}\")\n    dt \/= 2\n<\/code><\/pre>\n<p>Verfolgen Sie Energie, Masse, Impuls, Grenzen, Residuen und Zeitschrittverhalten. Zeichnen Sie sie w\u00e4hrend des Debuggens.<\/p>\n<h2>Die Debugging-Eskalationsleiter<\/h2>\n<p>Verwenden Sie die niedrigste Debug-Ebene, die gen\u00fcgend Informationen liefert.<\/p>\n<pre><code>Level 1: print()      \u2014 quick inspection\nLevel 2: assert()     \u2014 sanity checks and invariants\nLevel 3: pdb\/ipdb     \u2014 interactive state inspection\nLevel 4: profiling    \u2014 performance bugs and hidden inefficiency\nLevel 5: pytest       \u2014 regression prevention\n<\/code><\/pre>\n<p>Bei Bedarf eskalieren:<\/p>\n<ul>\n<li>Wenn <code>print()<\/code> gen\u00fcgend Kontext gibt, bleiben Sie auf Stufe 1.<\/li>\n<li>Wenn Sie Invarianten \u00fcberpr\u00fcfen m\u00fcssen, verwenden Sie Assertions.<\/li>\n<li>Wenn Sie den Status in einer bestimmten Zeile \u00fcberpr\u00fcfen m\u00fcssen, verwenden Sie <code>pdb<\/code> oder <code>ipdb<\/code>.<\/li>\n<li>Wenn der Code falsch und langsam ist, profilieren Sie ihn.<\/li>\n<li>Wenn Sie verhindern m\u00f6chten, dass der Fehler zur\u00fcckkehrt, schreiben Sie einen Test.<\/li>\n<\/ul>\n<h2>Ein praktisches Debugging-Beispiel: Fixieren einer FIPY-Simulation<\/h2>\n<p>Angenommen, eine FIPY-Diffusionssimulation erzeugt unbegrenzte Konzentrationswerte anstelle einer glatten Diffusion. Schreiben Sie zun\u00e4chst eine minimale, klare Version des Problems.<\/p>\n<pre><code class=\"language-python\">import numpy as np\nfrom fipy import Grid2D, CellVariable, TransientTerm, DiffusionTerm\n\nmesh = Grid2D(nx=50, ny=50, dx=1.0, dy=1.0)\n\nphi = CellVariable(name=\"concentration\", mesh=mesh, value=0.0)\nx, y = mesh.cellCenters\n\n# Initial hot spot in the center\nphi.setValue(\n    1.0,\n    where=((x - 25.0)**2 + (y - 25.0)**2) &lt; 25.0\n)\n\neq = TransientTerm(var=phi) == DiffusionTerm(coeff=1.0, var=phi)\n\ndt = 0.01\n\nprint(f\"Initial: min={phi.value.min()}, max={phi.value.max()}\")\n\nfor step in range(100):\n    eq.solve(var=phi, dt=dt)\n    \n    assert np.isfinite(phi.value).all(), \"NaN or Inf detected\"\n    assert phi.value.min() &gt;= -1e-10, f\"Negative value at step {step}\"\n    \nprint(f\"Final: min={phi.value.min()}, max={phi.value.max()}\")\n<\/code><\/pre>\n<p>Dann systematisch debuggen.<\/p>\n<h3>Schritt 1: Lassen Sie es zuverl\u00e4ssig scheitern<\/h3>\n<p>Protokollieren Sie Minimum, Maximum, Masse und Energie vor und nach dem Lauf.<\/p>\n<pre><code class=\"language-python\">print(f\"min={phi.value.min()}, max={phi.value.max()}\")\nprint(f\"mass={np.sum(phi.value * mesh.cellVolumes)}\")\nprint(f\"energy={np.sum(phi.value**2 * mesh.cellVolumes)}\")\n<\/code><\/pre>\n<h3>Schritt 2: Teilen und erobern<\/h3>\n<p>Testen Sie den Diffusionsbegriff allein. Entfernen Sie Reaktionsbegriffe, Advektionsbegriffe, nichtlineare Quellen und komplexe Randbedingungen, bis der Fehler verschwindet oder isoliert wird.<\/p>\n<h3>Schritt 3: \u00c4ndern Sie eine Sache<\/h3>\n<p>Versuchen Sie einen kleineren Zeitschritt und vergleichen Sie die Ergebnisse.<\/p>\n<pre><code class=\"language-python\">dt = 0.001\n<\/code><\/pre>\n<p>Wenn der Fehler bei einem kleineren Zeitschritt besteht, kann das Problem r\u00e4umlich, strukturell oder in Bezug auf Randbedingungen und nicht auf Zeitstabilit\u00e4t liegen.<\/p>\n<h3>Schritt 4: Verwenden Sie den Debugger<\/h3>\n<p>In einen Debugger in der N\u00e4he des ausfallenden Schritts fallen und Werte, Netzform, Masken und Randbedingungen \u00fcberpr\u00fcfen.<\/p>\n<pre><code class=\"language-python\">import pdb\npdb.set_trace()\n<\/code><\/pre>\n<h3>Schritt 5: Testen Sie einen Test<\/h3>\n<p>Nachdem Sie den Fehler behoben haben, f\u00fcgen Sie einen Regressionstest hinzu.<\/p>\n<pre><code class=\"language-python\">def test_diffusion_mass_stays_finite():\n    phi_initial, phi_final, volumes = run_small_diffusion_case()\n    \n    mass_initial = np.sum(phi_initial * volumes)\n    mass_final = np.sum(phi_final * volumes)\n    \n    np.testing.assert_allclose(mass_initial, mass_final, rtol=1e-6)\n    assert np.isfinite(phi_final).all()\n<\/code><\/pre>\n<h2>Zusammenfassung und n\u00e4chste Schritte<\/h2>\n<p>Das Debuggen des wissenschaftlichen Python-Codes ist eine progressive F\u00e4higkeit. Beginnen Sie mit Druckanweisungen, wechseln Sie zu Assertionen, verwenden Sie Debugger, wenn eine Statusinspektion erforderlich ist, profilieren Sie, wenn der Code langsam oder verd\u00e4chtig ist, und schreiben Sie Tests, um Regressionen zu verhindern.<\/p>\n<p>Das wichtigste Prinzip ist es, den Fehler zu isolieren, bevor er behoben wird. Ein Fehler, den Sie in einem kleinen Testfall reproduzieren k\u00f6nnen, ist viel einfacher zu beheben. Ein Fehler, der nur in einer gro\u00dfen Simulation erscheint, bleibt ein R\u00e4tsel.<\/p>\n<p>F\u00fcr wissenschaftlichen Kodex merken Sie sich folgende Regeln:<\/p>\n<ul>\n<li>\u00dcberpr\u00fcfen Sie nach numerischen Operationen auf NAN und Unendlichkeit.<\/li>\n<li>Drucken von Array-Formen, wenn die Indizierung komplex wird.<\/li>\n<li>\u00dcberwachen Sie konservierte Gr\u00f6\u00dfen wie Masse, Energie und Impuls.<\/li>\n<li>Testen Sie die Randbedingungen getrennt von der inneren Logik.<\/li>\n<li>Verwenden Sie Behauptungen als erste Verteidigungslinie gegen stilles numerisches Versagen.<\/li>\n<\/ul>\n<h3>N\u00e4chste Schritte<\/h3>\n<ul>\n<li>Beginnen Sie mit der Debugging-Eskalationsleiter: Beginnen Sie mit <code>print()<\/code> und eskalieren nur bei Bedarf.<\/li>\n<li>F\u00fcgen Sie Assertions vor und nach dem Solver-Funktionen hinzu.<\/li>\n<li>Einrichten der Protokollierung f\u00fcr die Simulationsdiagnose.<\/li>\n<li>Schreiben Sie mindestens einen Testfall, der einen allgemeinen Fehler reproduziert.<\/li>\n<\/ul>\n<p>Debuggen ist kein Zeichen von Schw\u00e4che. Es ist Teil des Schreibens von Code, der z\u00e4hlt. Die F\u00e4higkeiten, die Sie beim Debuggen des wissenschaftlichen Python-Codes aufbauen, helfen bei allen Arten von numerischen und Forschungssoftwareprojekten.<\/p>\n<h2>Verwandte Anleitungen<\/h2>\n<ul>\n<li><a href=\"https:\/\/matforge.org\/performance-profiling-optimization-python-pde-solvers\/\">Leistungsprofilierung und -optimierung f\u00fcr Python-PDE-Solver: Ein praktischer Leitfaden <\/a> &#8211; Profilerstellungstools, NUMBA und Speicheroptimierung.<\/li>\n<li><a href=\"https:\/\/matforge.org\/unit-testing-scientific-code-pytest-strategies-research-projects\/\"> Einheitentest f\u00fcr wissenschaftlichen Code: PyTest-Strategien <\/a> &#8211; Numerische Pr\u00e4zision und Testmuster.<\/li>\n<li><a href=\"https:\/\/matforge.org\/continuous-integration-research-software-automated-testing-validation\/\"> Kontinuierliche Integration f\u00fcr Forschungssoftware: Automatisiertes Testen und Validieren<\/a> \u2014 CI-Pipelines und automatisierte Tests.<\/li>\n<li><a href=\"https:\/\/matforge.org\/best-practices-for-maintaining-scientific-code\/\"> Best Practices f\u00fcr die Aufrechterhaltung wissenschaftlicher Codes <\/a> &#8211; Projektstruktur, Versionskontrolle und Dokumentation.<\/li>\n<li><a href=\"https:\/\/matforge.org\/using-fipy-for-phase-field-modeling\/\"> Verwenden von FIPY f\u00fcr Phasenfeldmodellierung <\/a> &#8211; FIPY-Implementierungsmuster.<\/li>\n<\/ul>\n<h2>Ben\u00f6tigen Sie Hilfe beim Debuggen Ihres Simulationscodes?<\/h2>\n<p>Das Debuggen komplexer wissenschaftlicher Simulationen kann zeitaufw\u00e4ndig sein, insbesondere wenn Sie sich nicht sicher sind, ob sich der Fehler in den Einstellungen des Codes, des Mesh, der Randbedingungen oder der Solver-Einstellungen befindet.<\/p>\n<p>Unser Team kann bei systematischen Debugging-Workflows, numerischen Testf\u00e4llen, Sanity-Checks f\u00fcr Erhaltungsgesetze und CI-Pipelines mit Regressionstests helfen.<\/p>\n","protected":false,"raw":"<h2>Schl\u00fcssel zum Mitnehmen<\/h2>\n<ul>\n<li>Das Debuggen des wissenschaftlichen Python-Codes folgt einer Eskalationsleiter: <code>print<\/code>, <code>assert<\/code>, <code>pdb<\/code>, Profiling und Tests.<\/li>\n<li>Die meisten wissenschaftlichen Fehler sind numerisch, wie z. B. NAN-Werte, Array-Form-Fehlanpassungen, Pr\u00e4zisionsverlust oder instabile Zeitschritte.<\/li>\n<li><code>print()<\/code> F\u00e4ngt viele Anf\u00e4ngerfehler auf, aber Behauptungen und Debugger erleichtern die Isolierung.<\/li>\n<li>Ein zuverl\u00e4ssiger wissenschaftlicher Debugging-Workflow ist: Versagen Sie es zuverl\u00e4ssig, isolieren Sie das Problem, \u00e4ndern Sie eines, \u00fcberpr\u00fcfen Sie und f\u00fcgen Sie einen Test hinzu.<\/li>\n<li>Profiling kann versteckte Fehler aufdecken, da langsamer Code oft ein Zeichen falscher Array-Logik, versehentlicher Schleifen oder \u00fcberm\u00e4\u00dfiger Zuweisung ist.<\/li>\n<\/ul>\n<p>Das Debuggen von wissenschaftlichem Python-Code ist eine der frustrierendsten F\u00e4higkeiten, die es zu entwickeln gilt. Sie schreiben, was eine einfache W\u00e4rmediffusionssimulation sein sollte, der Code l\u00e4uft fehlerfrei und die Ausgabe ist v\u00f6llig falsch.<\/p>\n<p>Kein Absturz. Keine Spur. Nur lautlos falsche Zahlen.<\/p>\n<p>Das ist die einzigartige Herausforderung des wissenschaftlichen Debuggens. Die gef\u00e4hrlichsten Fehler sind oft diejenigen, die \u00fcberhaupt nicht abst\u00fcrzen. Eine falsche Zahl kann durch Tausende von Iterationen propagieren und ein Ergebnis erzeugen, das physisch plausibel aussieht, bis Sie die Erhaltung, Stabilit\u00e4t oder Konvergenz \u00fcberpr\u00fcfen.<\/p>\n<p>In diesem Handbuch wird erl\u00e4utert, wie Sie wissenschaftlichen Python-Code auf mehreren Ebenen debuggen: Druckanweisungen, Protokollierung, Assertions, interaktives Debuggen, Profiling und Regressionstests.<\/p>\n<p>Egal, ob Sie eine erste FIPY-Simulation durchf\u00fchren oder eine Produktionsforschungs-Pipeline debuggen, dieser Workflow bietet Ihnen eine praktische Struktur.<\/p>\n<h2>Was ist anders beim Debuggen von wissenschaftlichem Code?<\/h2>\n<p>Das wissenschaftliche Debuggen unterscheidet sich vom Debuggen einer Web-App, API oder einer gew\u00f6hnlichen Daten-Pipeline.<\/p>\n<p>Die Zahlen k\u00f6nnen l\u00fcgen. Eine Web-App schl\u00e4gt oft sichtbar fehl: Eine Schaltfl\u00e4che funktioniert nicht, eine API gibt einen Fehler zur\u00fcck oder eine Datenbankabfrage schl\u00e4gt fehl. Wissenschaftlicher Code kann perfekt laufen und trotzdem falsche Ergebnisse liefern. Eine negative Konzentration, ein explodierendes Energiefeld oder eine versteckte NAN k\u00f6nnen nur angezeigt werden, wenn Sie direkt danach suchen.<\/p>\n<p>Korrektheit wird durch die Physik definiert, nicht nur durch die Syntax. Ein Python-Programm, das ausgef\u00fchrt wird, ist syntaktisch g\u00fcltig. Ein wissenschaftliches Programm ist nur dann glaubw\u00fcrdig, wenn es bekannte L\u00f6sungen entspricht, erwartete Mengen bewahrt, Randbedingungen einh\u00e4lt und unter Verfeinerung konvergiert.<\/p>\n<p>Stapelspuren zeigen oft in numerische, scipy oder fipy Interna. Der eigentliche Fehler liegt m\u00f6glicherweise in Ihrem Setup, aber die Fehlermeldung kann tief in einer Bibliothek angezeigt werden. Deshalb braucht das wissenschaftliche Debuggen nicht nur das Lesen von Traceback.<\/p>\n<h2>Der Debugging-Workflow: Ein f\u00fcnfstufiges mentales Modell<\/h2>\n<p>Ein n\u00fctzlicher Debugging-Workflow f\u00fcr den wissenschaftlichen Code folgt f\u00fcnf Schritten.<\/p>\n<h3>Schritt 1: Lassen Sie es zuverl\u00e4ssig scheitern<\/h3>\n<p>Ihre erste Aufgabe besteht darin, einen Testfall zu erstellen, der jedes Mal fehlschl\u00e4gt. Im wissenschaftlichen Kodex bedeutet dies normalerweise, ein minimal reproduzierbares Beispiel zu erstellen, das das falsche Ergebnis liefert.<\/p>\n<p>Sie ben\u00f6tigen nicht die vollst\u00e4ndige Simulation. Sie ben\u00f6tigen den kleinsten Code, der den Fehler demonstriert.<\/p>\n<pre><code class=\"language-python\">import numpy as np\n\n# Full simulation may be hundreds of lines.\n# Start with the smallest failing update step.\n\nphi = np.ones((10, 10))\nphi[:] = 0.5\n\ngradient = np.gradient(phi)[0]\nphi_new = phi - 0.1 * gradient\n\nprint(f\"min(phi_new) = {np.min(phi_new)}\")\nprint(f\"max(phi_new) = {np.max(phi_new)}\")\n<\/code><\/pre>\n<p>Durch die Isolierung des Aktualisierungsschritts erstellen Sie einen reproduzierbaren Fall. Dies ist die Grundlage f\u00fcr jeden sp\u00e4teren Debugging-Schritt.<\/p>\n<h3>Schritt 2: Teilen und erobern<\/h3>\n<p>Wenn Sie einen Fall nicht haben, isolieren Sie ihn weiter. Suchen Sie den f\u00fcr den Fehler verantwortlichen Modul, Funktion, Linie oder Physik.<\/p>\n<p>F\u00fcr den Simulationscode besteht eine n\u00fctzliche Taktik darin, jeden Physikbegriff separat zu testen. Wenn Ihr Modell Diffusion, Reaktion und Advektion kombiniert, testen Sie jedes Einzelne, bevor Sie das gekoppelte System debuggen.<\/p>\n<pre><code class=\"language-python\">solution_diffusion = solve_diffusion_only(initial_conditions, dt)\nassert np.all(solution_diffusion &gt;= 0), \"Diffusion produced negative values\"\n\nsolution_reaction = solve_reaction_only(initial_conditions, dt)\nassert np.all(solution_reaction &gt;= 0), \"Reaction produced negative values\"\n\nsolution_coupled = solve_coupled(initial_conditions, dt)\n<\/code><\/pre>\n<p>Dies verengt den Suchraum. Anstatt die gesamte Simulation zu debuggen, debuggen Sie einen Term oder einen Kopplungsmechanismus.<\/p>\n<h3>Schritt 3: \u00c4ndern Sie jeweils eine Sache<\/h3>\n<p>Das wissenschaftliche Debuggen wird verwirrend, wenn Sie mehrere Variablen gleichzeitig \u00e4ndern. Wenn Sie den Zeitschritt, die Randbedingungen, die Solvertoleranz und die Anfangsbedingung zusammen \u00e4ndern, k\u00f6nnen Sie nicht wissen, welche \u00c4nderung wichtig ist.<\/p>\n<p>\u00c4ndern Sie einen Parameter. laufen vergleichen dokumentieren<\/p>\n<p>Verwenden Sie die Versionskontrolle, um \u00c4nderungen zu verfolgen. Selbst einfache Commits wie \u201e\u00c4nderte DT von 0,01 auf 0,001\u201c k\u00f6nnen Stunden sp\u00e4ter sparen.<\/p>\n<h3>Schritt 4: Verwenden Sie den Debugger<\/h3>\n<p>Wenn Drucke und Zusicherungen nicht ausreichen, verwenden Sie <code>pdb<\/code>, <code>ipdb<\/code> oder einen IDE-Debugger. Mit einem Debugger k\u00f6nnen Sie die Ausf\u00fchrung anhalten, Variablen pr\u00fcfen, Code durchlaufen und \u00fcberpr\u00fcfen, warum sich ein Wert ge\u00e4ndert hat.<\/p>\n<h3>Schritt 5: Zur Testsuite hinzuf\u00fcgen<\/h3>\n<p>Nachdem Sie einen Fehler behoben haben, f\u00fcgen Sie einen Test hinzu, der ihn abgefangen h\u00e4tte. Dies verhindert, dass der gleiche Fehler sp\u00e4ter zur\u00fcckkehrt.<\/p>\n<pre><code class=\"language-python\">def test_no_negative_phases():\n    \"\"\"Verify phase field stays non-negative after diffusion.\"\"\"\n    phi = np.ones((10, 10))\n    phi = diffusion_step(phi, dt=0.01)\n    assert np.min(phi) &gt;= 0, f\"Got min value {np.min(phi)}\"\n<\/code><\/pre>\n<p>Der Workflow ist einfach: isolieren, dividieren, \u00e4ndern, \u00e4ndern, debuggen und testen. Der Rest dieser Anleitung erkl\u00e4rt, welche Tools auf jeder Ebene verwendet werden sollen.<\/p>\n<h2>Stufe 1: Anweisungen drucken und protokollieren<\/h2>\n<p><code>print()<\/code> ist immer noch n\u00fctzlich. Es ist das einfachste Debugging-Tool und f\u00e4ngt viele fr\u00fche Fehler auf. Der Schl\u00fcssel ist, es mit Struktur zu verwenden.<\/p>\n<h3>Das Problem mit schlechten Drucken<\/h3>\n<p>Ein h\u00e4ufiger Anf\u00e4ngerfehler ist das Drucken von Werten ohne Kontext.<\/p>\n<pre><code class=\"language-python\">for i in range(100):\n    print(phi[i])\n<\/code><\/pre>\n<p>Nach ein paar Iterationen haben Sie viele Zahlen ohne klare Bedeutung. Sie wissen nicht, welche Variable, Zeitschritt oder Bedingung jede Zeile erzeugt hat.<\/p>\n<h3>Der richtige Weg: Strukturierte Drucke<\/h3>\n<p>Alles beschriften. Schlie\u00dfen Sie den Zeitschritt, den Variablennamen, das Minimum, das Maximum und jede konservierte Menge ein, die Ihnen wichtig ist.<\/p>\n<pre><code class=\"language-python\">import numpy as np\n\nprint(\n    f\"[t={t:06.4f}] \"\n    f\"min(phi)={np.min(phi):.6f}, \"\n    f\"max(phi)={np.max(phi):.6f}\"\n)\n\nprint(\n    f\"[t={t:06.4f}] \"\n    f\"energy={np.sum(phi * phi):.6f}, \"\n    f\"mass={np.sum(phi):.6f}\"\n)\n<\/code><\/pre>\n<p>Diese Ausgabe ist einfach zu scannen:<\/p>\n<pre><code>[t=0.0010] min(phi)=0.000000, max(phi)=1.000000\n[t=0.0010] energy=0.850000, mass=1.000000\n[t=0.0020] min(phi)=0.000000, max(phi)=1.023456\n[t=0.0020] energy=0.873456, mass=1.012345\n<\/code><\/pre>\n<p>Sie sehen sofort, dass der Maximalwert \u00fcber 1,0 gestiegen ist und die Masse ge\u00e4ndert wird. Das sind n\u00fctzliche Hinweise.<\/p>\n<h3>Protokollierung vs. Druckanweisungen<\/h3>\n<p>Verwenden Sie f\u00fcr ein ernsthaftes Debuggen das Modul <code>logging<\/code>  von Python anstelle von RAW <code>print()<\/code> .<\/p>\n<pre><code class=\"language-python\">import logging\n\nlogging.basicConfig(\n    level=logging.DEBUG,\n    format=\"%(levelname)s: %(message)s\"\n)\n\nlogger = logging.getLogger(__name__)\n\nlogger.debug(\"Running diffusion step at t=%f, dt=%f\", t, dt)\nlogger.warning(\"Energy increased by %.5f\", energy_change)\n<\/code><\/pre>\n<p>Logging hat mehrere Vorteile:<\/p>\n<ul>\n<li>Ebenen wie <code>DEBUG<\/code>, <code>WARNING<\/code> und <code>ERROR<\/code>.<\/li>\n<li>Optionale Zeitstempel und Dateiausgabe.<\/li>\n<li>Einfache Aktivierung oder Deaktivierung ohne L\u00f6schen des Debug-Codes.<\/li>\n<li>Reinigere Diagnose bei langen Simulationsl\u00e4ufen.<\/li>\n<\/ul>\n<p>Eine praktische Regel ist die Verwendung von <code>print()<\/code> f\u00fcr schnelle Erkundungsskripte und <code>logging<\/code> f\u00fcr wiederverwendbare oder produktionstechnische Simulationscodes.<\/p>\n<h2>Stufe 2: Behauptungen und Vernunftpr\u00fcfungen<\/h2>\n<p>Behauptungen sind wie gedruckte Aussagen, die sich weigern, zu schweigen. Sie werfen einen Fehler auf, wenn eine Annahme verletzt wird.<\/p>\n<h3>Das Muster: Annahmen zuerst<\/h3>\n<p>Das n\u00fctzlichste Muster besteht darin, die Eingaben zu \u00fcberpr\u00fcfen, den Solver auszuf\u00fchren und die Ausgaben zu \u00fcberpr\u00fcfen.<\/p>\n<pre><code class=\"language-python\">import numpy as np\n\ndef solve_diffusion(phi, dt, diffusion_coeff):\n    # Preconditions\n    assert np.all(phi &gt;= 0), f\"Negative phase: {np.min(phi)}\"\n    assert np.all(phi &lt;= 1), f\"Phase &gt; 1: {np.max(phi)}\"\n    assert np.isfinite(phi).all(), \"Non-finite input values detected\"\n    \n    # Solver step\n    phi_new = apply_diffusion(phi, dt, diffusion_coeff)\n    \n    # Postconditions\n    assert np.isfinite(phi_new).all(), \"Solver produced NaN or Inf\"\n    assert np.all(phi_new &gt;= 0), f\"Negative output: {np.min(phi_new)}\"\n    \n    return phi_new\n<\/code><\/pre>\n<p>Dadurch wird ein stiller numerischer Fehler in einen eindeutigen Fehler mit Ort und Kontext.<\/p>\n<h3>Die Behauptungsphilosophie<\/h3>\n<p>Bei Behauptungen geht es nicht nur darum, Fehler zu fangen. Sie heften Wanzen an.<\/p>\n<p>Eine Behauptung, die am Anfang eines Solvers fehlschl\u00e4gt, sagt Ihnen, wo die Annahme gebrochen ist. Ein Nan, das Hunderte von Zeilen sp\u00e4ter erscheint, sagt Ihnen viel weniger.<\/p>\n<p>N\u00fctzliche Orte f\u00fcr Behauptungen sind:<\/p>\n<ul>\n<li>Vor und nach dem Solver-Aufruf.<\/li>\n<li>an Modulgrenzen, wo Daten ausgetauscht werden.<\/li>\n<li>Nach Netzoperationen wie Verfeinerung, Restricken oder Vergr\u00f6bern.<\/li>\n<li>Nach dem Anwenden von Randbedingungen.<\/li>\n<\/ul>\n<h3>Eine Warnung vor Behauptungen<\/h3>\n<p>Verwenden Sie die Behauptungen in teuren inneren Schleifen nicht \u00fcberm\u00e4\u00dfig, es sei denn, Sie ben\u00f6tigen sie. Sie k\u00f6nnen Runtime-Overhead hinzuf\u00fcgen und das Debuggen laut machen.<\/p>\n<p>Verwenden Sie Assertions f\u00fcr Invarianten und Vorbedingungen: Bedingungen, die niemals verletzt werden sollten, wenn der Code korrekt ist.<\/p>\n<h2>Level 3: Der Python-Debugger<\/h2>\n<p>Wenn Druckanweisungen nicht ausreichen, verwenden Sie einen interaktiven Debugger. Mit einem Debugger k\u00f6nnen Sie das Programm anhalten, den Status \u00fcberpr\u00fcfen und die Logik durchlaufen.<\/p>\n<h3>Das klassische Werkzeug: PDB<\/h3>\n<p><code>pdb<\/code> ist der integrierte Debugger von Python. Es ist in jeder Python-Installation verf\u00fcgbar.<\/p>\n<p>In IPython k\u00f6nnen Sie nach einem Fehler das Post-Mortem-Debugging verwenden:<\/p>\n<pre><code class=\"language-python\">In [1]: %run simulation.py\n\nIn [2]: %debug\n<\/code><\/pre>\n<p>Dies bringt Sie zum Fehlerpunkt in den Debugger.<\/p>\n<p>Starten Sie f\u00fcr Befehlszeilenskripte das Skript mit dem Debugger:<\/p>\n<pre><code class=\"language-bash\">python -m pdb simulation.py\n<\/code><\/pre>\n<p>N\u00fctzliche Befehle sind:<\/p>\n<ul>\n<li><code>c<\/code>: Weiterf\u00fchrung.<\/li>\n<li><code>n<\/code>: Gehen Sie zur n\u00e4chsten Zeile.<\/li>\n<li><code>s<\/code>: Schritt in einen Funktionsaufruf.<\/li>\n<li><code>b<\/code>: Legen Sie einen Haltepunkt fest.<\/li>\n<li><code>p variable_name<\/code>: Drucken Sie eine Variable.<\/li>\n<li><code>q<\/code>: Beenden Sie den Debugger.<\/li>\n<\/ul>\n<p>Sie k\u00f6nnen auch einen Haltepunkt direkt in Code einf\u00fcgen:<\/p>\n<pre><code class=\"language-python\">def solve_phase_field(phi, dt):\n    import pdb\n    pdb.set_trace()\n    \n    phi_new = update_phase_field(phi, dt)\n    return phi_new\n<\/code><\/pre>\n<h3>IPDB- und IPython-Debugging<\/h3>\n<p><code>ipdb<\/code> ist eine erweiterte Version von <code>pdb<\/code>. Es bietet Tab-Vervollst\u00e4ndigung, bessere Formatierung und ein komfortableres interaktives Erlebnis.<\/p>\n<pre><code class=\"language-bash\">pip install ipdb\n<\/code><\/pre>\n<pre><code class=\"language-python\">import ipdb\nipdb.set_trace()\n<\/code><\/pre>\n<p>F\u00fcr IPython-Benutzer ist <code>%debug<\/code> oft die schnellste Option nach einer Ausnahme.<\/p>\n<h3>IDE-Debugger<\/h3>\n<p>PyCharm, VS Code und Spyder enthalten grafische Debugger. Sie k\u00f6nnen Breakpoints setzen, Variablen pr\u00fcfen, Code durchlaufen und Ausdr\u00fccke ohne Eingabe von Befehlen eingeben.<\/p>\n<p>IDE-Debugger sind hilfreich f\u00fcr die lokale Entwicklung. F\u00fcr Remote-Cluster oder Headless-Server sind <code>pdb<\/code>, <code>ipdb<\/code> und die Protokollierung in der Regel praktischer.<\/p>\n<h3>Wann verwenden Sie welches Werkzeug?<\/h3>\n<table class=\"custom-table\">\n<tbody><tr>\n<th>Szenario<\/th>\n<th>Empfohlenes Werkzeug<\/th>\n<\/tr>\n<tr>\n<td>Schnelles Skript oder fr\u00fche lokale Entwicklung<\/td>\n<td><code>print()<\/code>  plus Behauptungen<\/td>\n<\/tr>\n<tr>\n<td>Fehlerverfolgung in Ipython<\/td>\n<td><code>%debug<\/code><\/td>\n<\/tr>\n<tr>\n<td>Remote-Cluster ohne IDE-Zugriff<\/td>\n<td><code>python -m pdb script.py<\/code><\/td>\n<\/tr>\n<tr>\n<td>Interaktives Debuggen mit besserem Kontext<\/td>\n<td><code>ipdb<\/code> oder IDE-Debugger<\/td>\n<\/tr>\n<tr>\n<td>Diagnose im Produktionsstil<\/td>\n<td><code>logging<\/code> mit strategischen Breakpoints<\/td>\n<\/tr>\n<\/tbody><\/table>\n<h2>Level 4: Profiling, um versteckte Fehler zu finden<\/h2>\n<p>Profiling wird normalerweise als Leistungstool angesehen, hilft aber auch beim Debuggen. Langsamer Code ist oft falscher Code. Profiler k\u00f6nnen zuf\u00e4llige Python-Schleifen, wiederholte Zuordnungen, redundante Berechnungen oder unerwartete Funktionsaufrufe anzeigen.<\/p>\n<h3>cProfile: Finden Sie die Hot-Funktionen<\/h3>\n<pre><code class=\"language-python\">import cProfile\nimport pstats\n\nprofiler = cProfile.Profile()\n\nprofiler.enable()\nsolve_phase_field(phi, dt, n_iterations)\nprofiler.disable()\n\nstats = pstats.Stats(profiler)\nstats.sort_stats(\"cumulative\")\nstats.print_stats(20)\n<\/code><\/pre>\n<p>Dies zeigt, welche Funktionen die meiste Zeit verbrauchen. Wenn eine Hilfsfunktion die Laufzeit unerwartet dominiert, kann sie einen versteckten Fehler oder eine ineffiziente Array-Logik enthalten.<\/p>\n<h3>line_profiler: Finden Sie die Hotlines<\/h3>\n<p>Wenn <code>cProfile<\/code> eine langsame Funktion identifiziert, kann <code>line_profiler<\/code> anzeigen, welche Zeilen innerhalb der Funktion Zeit verbrauchen.<\/p>\n<pre><code class=\"language-bash\">pip install line_profiler\nkernprof -l -v simulation.py\n<\/code><\/pre>\n<p>Dies ist n\u00fctzlich, wenn eine einzelne Zeile innerhalb einer Schleife wiederholt Arrays erzeugt oder versehentlich einen langsamen Vorgang ausf\u00fchrt.<\/p>\n<h3>Speicherprofilierung: versteckte Zuordnungen finden<\/h3>\n<p>Der wissenschaftliche Code verbirgt h\u00e4ufig Speicherfehler wie versehentliche Kopien oder \u00fcberm\u00e4\u00dfige Arrayerstellung in Schleifen. <code>memory_profiler<\/code> kann bei der Verfolgung von Zuordnungen helfen.<\/p>\n<pre><code class=\"language-python\">from memory_profiler import profile\n\n@profile\ndef solve_phase_field(phi, dt):\n    phi_new = apply_diffusion(phi, dt)\n    return phi_new\n<\/code><\/pre>\n<p>Zeilen-f\u00fcr-Linien-Speicherprofile k\u00f6nnen zeigen, wo gro\u00dfe Arrays erstellt und verworfen werden.<\/p>\n<h3>Profiling als Debuggen<\/h3>\n<p>Profiling hilft, wenn ein Solver sowohl falsch als auch langsam ist. Beispielsweise kann ein falscher Schnitt Energie \u00fcber den falschen Bereich berechnen. Ein Profil kann zeigen, dass die Energieberechnung unerwartet teuer ist, was Sie zur\u00fcck zum Array-Vorgang hinweist.<\/p>\n<p>Behauptungen best\u00e4tigen den Fehler. Profiling hilft Ihnen zu finden, wo Sie suchen m\u00fcssen.<\/p>\n<h2>Stufe 5: Testen und Regressionspr\u00e4vention<\/h2>\n<p>Das endg\u00fcltige Debugging-Niveau ist die Pr\u00e4vention. Schreiben Sie nach dem Beheben eines Fehlers einen Test, der den Fehler reproduziert und beweist, dass der Fix funktioniert.<\/p>\n<h3>Das wissenschaftliche Testmuster<\/h3>\n<pre><code class=\"language-python\">import pytest\nimport numpy as np\n\n@pytest.mark.parametrize(\"mesh_size\", [10, 20, 50])\ndef test_energy_decreases(mesh_size):\n    \"\"\"Energy should decrease during simple diffusion.\"\"\"\n    phi = init_phi(mesh_size)\n    energy_history = []\n    \n    for step in range(100):\n        energy_history.append(compute_energy(phi))\n        phi = solve_step(phi, dt=0.01)\n    \n    energy_changes = np.diff(energy_history)\n    assert np.all(energy_changes &lt;= 1e-10)\n<\/code><\/pre>\n<p>Dieser Test \u00fcberpr\u00fcft das physikalische Verhalten, nicht nur die Syntax. Eine korrekte Simulation sollte nicht nur ausgef\u00fchrt werden. Es sollte erwartete Mengen erhalten, Grenzen einhalten und mit der Verfeinerung konvergieren.<\/p>\n<h3>Die Testpyramide f\u00fcr wissenschaftlichen Code<\/h3>\n<ul>\n<li>Unit-Tests sind schnell und pr\u00fcfen einzelne Funktionen wie <code>compute_gradient()<\/code> oder <code>apply_boundary_conditions()<\/code>.<\/li>\n<li>Integrationstests f\u00fchren \u00dcberpr\u00fcfungen auf Solver-Ebene gegen analytische oder hergestellte L\u00f6sungen durch.<\/li>\n<li>Regressionstests vergleichen vollst\u00e4ndige Simulationen mit vertrauensw\u00fcrdigen Baseline-Ausgaben.<\/li>\n<\/ul>\n<p>Das Schl\u00fcsselprinzip ist einfach: Testen Sie die Physik, nicht nur den Codepfad.<\/p>\n<h2>H\u00e4ufige wissenschaftliche Debugging-Fallen<\/h2>\n<h3>Nan und Unendlichkeit<\/h3>\n<p>NAN- und Unendlichkeitswerte sind im wissenschaftlichen Kodex \u00fcblich. Sie treten oft nach Division durch Werte nahe Null, <code>log(0)<\/code> , <code>sqrt<\/code>  negativer Werte oder \u00dcberlauf in Exponentialen auf.<\/p>\n<pre><code class=\"language-python\">phi = np.zeros((100, 100))\nphi[50, 50] = 1.0\n\nresult = phi \/ (phi + 1e-300)\n<\/code><\/pre>\n<p>Nach numerischen Operationen auf endliche Werte pr\u00fcfen:<\/p>\n<pre><code class=\"language-python\">assert np.isfinite(result).all(), (\n    f\"NaN or Inf detected: min={np.nanmin(result)}, max={np.nanmax(result)}\"\n)\n<\/code><\/pre>\n<h3>Array-Form-Fehlanpassungen<\/h3>\n<p>Array-Formfehler sind h\u00e4ufig beim Wechsel zwischen abgeflachten Solver-Arrays und 2D- oder 3D-Feldern.<\/p>\n<pre><code class=\"language-python\">print(f\"phi shape: {phi.shape}\")\nprint(f\"expected shape: {expected_shape}\")\n<\/code><\/pre>\n<p>Zu den h\u00e4ufigsten Ursachen geh\u00f6ren versehentliches Abflachen, Indizierung, die eine Dimension entfernt, und Bibliotheksvariablen, die Daten in einer anderen Form als erwartet speichern.<\/p>\n<h3>Gleitkomma-Pr\u00e4zision<\/h3>\n<p>Vergleichen Sie Gleitkomma-Arrays nicht mit exakter Gleichheit, es sei denn, es wird wirklich eine genaue Gleichheit erwartet. Verwenden Sie toleranzbasierte Vergleiche.<\/p>\n<pre><code class=\"language-python\">np.testing.assert_allclose(phi, phi_new, rtol=1e-6, atol=1e-12)\n<\/code><\/pre>\n<p>Dies ist besonders wichtig f\u00fcr iterative Solver, gemischte Pr\u00e4zision und langzeitige Integrationen.<\/p>\n<h3>Randbedingungsfehler<\/h3>\n<p>Randbedingungsfehler sind h\u00e4ufig, da der Code h\u00e4ufig ausgef\u00fchrt wird, auch wenn die falschen Zellen oder Gesichter eingeschr\u00e4nkt sind.<\/p>\n<pre><code class=\"language-python\"># Wrong: overwrites all cells\nphi[:] = boundary_value\n\n# Better: apply only to selected boundary locations\nphi[boundary_mask] = boundary_value\n<\/code><\/pre>\n<p>Stellen Sie immer sicher, dass Grenzwerte nur dort angewendet werden, wo beabsichtigt und Innenwerte nicht \u00fcberschrieben werden.<\/p>\n<h3>Konvergenz und Stabilit\u00e4t<\/h3>\n<p>Simulationen k\u00f6nnen lautlos auseinander gehen, wenn die Zeitschritte zu gro\u00df oder die Solvertoleranzen zu locker sind. \u00dcberwachen Sie konservierte oder begrenzte Mengen im Laufe der Zeit.<\/p>\n<pre><code class=\"language-python\">if energy &gt; prev_energy * 1.1:\n    print(f\"Energy growing. Reducing dt from {dt} to {dt \/ 2}\")\n    dt \/= 2\n<\/code><\/pre>\n<p>Verfolgen Sie Energie, Masse, Impuls, Grenzen, Residuen und Zeitschrittverhalten. Zeichnen Sie sie w\u00e4hrend des Debuggens.<\/p>\n<h2>Die Debugging-Eskalationsleiter<\/h2>\n<p>Verwenden Sie die niedrigste Debug-Ebene, die gen\u00fcgend Informationen liefert.<\/p>\n<pre><code>Level 1: print()      \u2014 quick inspection\nLevel 2: assert()     \u2014 sanity checks and invariants\nLevel 3: pdb\/ipdb     \u2014 interactive state inspection\nLevel 4: profiling    \u2014 performance bugs and hidden inefficiency\nLevel 5: pytest       \u2014 regression prevention\n<\/code><\/pre>\n<p>Bei Bedarf eskalieren:<\/p>\n<ul>\n<li>Wenn <code>print()<\/code> gen\u00fcgend Kontext gibt, bleiben Sie auf Stufe 1.<\/li>\n<li>Wenn Sie Invarianten \u00fcberpr\u00fcfen m\u00fcssen, verwenden Sie Assertions.<\/li>\n<li>Wenn Sie den Status in einer bestimmten Zeile \u00fcberpr\u00fcfen m\u00fcssen, verwenden Sie <code>pdb<\/code> oder <code>ipdb<\/code>.<\/li>\n<li>Wenn der Code falsch und langsam ist, profilieren Sie ihn.<\/li>\n<li>Wenn Sie verhindern m\u00f6chten, dass der Fehler zur\u00fcckkehrt, schreiben Sie einen Test.<\/li>\n<\/ul>\n<h2>Ein praktisches Debugging-Beispiel: Fixieren einer FIPY-Simulation<\/h2>\n<p>Angenommen, eine FIPY-Diffusionssimulation erzeugt unbegrenzte Konzentrationswerte anstelle einer glatten Diffusion. Schreiben Sie zun\u00e4chst eine minimale, klare Version des Problems.<\/p>\n<pre><code class=\"language-python\">import numpy as np\nfrom fipy import Grid2D, CellVariable, TransientTerm, DiffusionTerm\n\nmesh = Grid2D(nx=50, ny=50, dx=1.0, dy=1.0)\n\nphi = CellVariable(name=\"concentration\", mesh=mesh, value=0.0)\nx, y = mesh.cellCenters\n\n# Initial hot spot in the center\nphi.setValue(\n    1.0,\n    where=((x - 25.0)**2 + (y - 25.0)**2) &lt; 25.0\n)\n\neq = TransientTerm(var=phi) == DiffusionTerm(coeff=1.0, var=phi)\n\ndt = 0.01\n\nprint(f\"Initial: min={phi.value.min()}, max={phi.value.max()}\")\n\nfor step in range(100):\n    eq.solve(var=phi, dt=dt)\n    \n    assert np.isfinite(phi.value).all(), \"NaN or Inf detected\"\n    assert phi.value.min() &gt;= -1e-10, f\"Negative value at step {step}\"\n    \nprint(f\"Final: min={phi.value.min()}, max={phi.value.max()}\")\n<\/code><\/pre>\n<p>Dann systematisch debuggen.<\/p>\n<h3>Schritt 1: Lassen Sie es zuverl\u00e4ssig scheitern<\/h3>\n<p>Protokollieren Sie Minimum, Maximum, Masse und Energie vor und nach dem Lauf.<\/p>\n<pre><code class=\"language-python\">print(f\"min={phi.value.min()}, max={phi.value.max()}\")\nprint(f\"mass={np.sum(phi.value * mesh.cellVolumes)}\")\nprint(f\"energy={np.sum(phi.value**2 * mesh.cellVolumes)}\")\n<\/code><\/pre>\n<h3>Schritt 2: Teilen und erobern<\/h3>\n<p>Testen Sie den Diffusionsbegriff allein. Entfernen Sie Reaktionsbegriffe, Advektionsbegriffe, nichtlineare Quellen und komplexe Randbedingungen, bis der Fehler verschwindet oder isoliert wird.<\/p>\n<h3>Schritt 3: \u00c4ndern Sie eine Sache<\/h3>\n<p>Versuchen Sie einen kleineren Zeitschritt und vergleichen Sie die Ergebnisse.<\/p>\n<pre><code class=\"language-python\">dt = 0.001\n<\/code><\/pre>\n<p>Wenn der Fehler bei einem kleineren Zeitschritt besteht, kann das Problem r\u00e4umlich, strukturell oder in Bezug auf Randbedingungen und nicht auf Zeitstabilit\u00e4t liegen.<\/p>\n<h3>Schritt 4: Verwenden Sie den Debugger<\/h3>\n<p>In einen Debugger in der N\u00e4he des ausfallenden Schritts fallen und Werte, Netzform, Masken und Randbedingungen \u00fcberpr\u00fcfen.<\/p>\n<pre><code class=\"language-python\">import pdb\npdb.set_trace()\n<\/code><\/pre>\n<h3>Schritt 5: Testen Sie einen Test<\/h3>\n<p>Nachdem Sie den Fehler behoben haben, f\u00fcgen Sie einen Regressionstest hinzu.<\/p>\n<pre><code class=\"language-python\">def test_diffusion_mass_stays_finite():\n    phi_initial, phi_final, volumes = run_small_diffusion_case()\n    \n    mass_initial = np.sum(phi_initial * volumes)\n    mass_final = np.sum(phi_final * volumes)\n    \n    np.testing.assert_allclose(mass_initial, mass_final, rtol=1e-6)\n    assert np.isfinite(phi_final).all()\n<\/code><\/pre>\n<h2>Zusammenfassung und n\u00e4chste Schritte<\/h2>\n<p>Das Debuggen des wissenschaftlichen Python-Codes ist eine progressive F\u00e4higkeit. Beginnen Sie mit Druckanweisungen, wechseln Sie zu Assertionen, verwenden Sie Debugger, wenn eine Statusinspektion erforderlich ist, profilieren Sie, wenn der Code langsam oder verd\u00e4chtig ist, und schreiben Sie Tests, um Regressionen zu verhindern.<\/p>\n<p>Das wichtigste Prinzip ist es, den Fehler zu isolieren, bevor er behoben wird. Ein Fehler, den Sie in einem kleinen Testfall reproduzieren k\u00f6nnen, ist viel einfacher zu beheben. Ein Fehler, der nur in einer gro\u00dfen Simulation erscheint, bleibt ein R\u00e4tsel.<\/p>\n<p>F\u00fcr wissenschaftlichen Kodex merken Sie sich folgende Regeln:<\/p>\n<ul>\n<li>\u00dcberpr\u00fcfen Sie nach numerischen Operationen auf NAN und Unendlichkeit.<\/li>\n<li>Drucken von Array-Formen, wenn die Indizierung komplex wird.<\/li>\n<li>\u00dcberwachen Sie konservierte Gr\u00f6\u00dfen wie Masse, Energie und Impuls.<\/li>\n<li>Testen Sie die Randbedingungen getrennt von der inneren Logik.<\/li>\n<li>Verwenden Sie Behauptungen als erste Verteidigungslinie gegen stilles numerisches Versagen.<\/li>\n<\/ul>\n<h3>N\u00e4chste Schritte<\/h3>\n<ul>\n<li>Beginnen Sie mit der Debugging-Eskalationsleiter: Beginnen Sie mit <code>print()<\/code> und eskalieren nur bei Bedarf.<\/li>\n<li>F\u00fcgen Sie Assertions vor und nach dem Solver-Funktionen hinzu.<\/li>\n<li>Einrichten der Protokollierung f\u00fcr die Simulationsdiagnose.<\/li>\n<li>Schreiben Sie mindestens einen Testfall, der einen allgemeinen Fehler reproduziert.<\/li>\n<\/ul>\n<p>Debuggen ist kein Zeichen von Schw\u00e4che. Es ist Teil des Schreibens von Code, der z\u00e4hlt. Die F\u00e4higkeiten, die Sie beim Debuggen des wissenschaftlichen Python-Codes aufbauen, helfen bei allen Arten von numerischen und Forschungssoftwareprojekten.<\/p>\n<h2>Verwandte Anleitungen<\/h2>\n<ul>\n<li><a href=\"https:\/\/matforge.org\/performance-profiling-optimization-python-pde-solvers\/\">Leistungsprofilierung und -optimierung f\u00fcr Python-PDE-Solver: Ein praktischer Leitfaden <\/a> - Profilerstellungstools, NUMBA und Speicheroptimierung.<\/li>\n<li><a href=\"https:\/\/matforge.org\/unit-testing-scientific-code-pytest-strategies-research-projects\/\"> Einheitentest f\u00fcr wissenschaftlichen Code: PyTest-Strategien <\/a> - Numerische Pr\u00e4zision und Testmuster.<\/li>\n<li><a href=\"https:\/\/matforge.org\/continuous-integration-research-software-automated-testing-validation\/\"> Kontinuierliche Integration f\u00fcr Forschungssoftware: Automatisiertes Testen und Validieren<\/a> \u2014 CI-Pipelines und automatisierte Tests.<\/li>\n<li><a href=\"https:\/\/matforge.org\/best-practices-for-maintaining-scientific-code\/\"> Best Practices f\u00fcr die Aufrechterhaltung wissenschaftlicher Codes <\/a> - Projektstruktur, Versionskontrolle und Dokumentation.<\/li>\n<li><a href=\"https:\/\/matforge.org\/using-fipy-for-phase-field-modeling\/\"> Verwenden von FIPY f\u00fcr Phasenfeldmodellierung <\/a> - FIPY-Implementierungsmuster.<\/li>\n<\/ul>\n<h2>Ben\u00f6tigen Sie Hilfe beim Debuggen Ihres Simulationscodes?<\/h2>\n<p>Das Debuggen komplexer wissenschaftlicher Simulationen kann zeitaufw\u00e4ndig sein, insbesondere wenn Sie sich nicht sicher sind, ob sich der Fehler in den Einstellungen des Codes, des Mesh, der Randbedingungen oder der Solver-Einstellungen befindet.<\/p>\n<p>Unser Team kann bei systematischen Debugging-Workflows, numerischen Testf\u00e4llen, Sanity-Checks f\u00fcr Erhaltungsgesetze und CI-Pipelines mit Regressionstests helfen.<\/p>\n"},"excerpt":{"rendered":"<p><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 10<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>Schl\u00fcssel zum Mitnehmen Das Debuggen des wissenschaftlichen Python-Codes folgt einer Eskalationsleiter: print, assert, pdb, Profiling und Tests. Die meisten wissenschaftlichen Fehler sind numerisch, wie z. B. NAN-Werte, Array-Form-Fehlanpassungen, Pr\u00e4zisionsverlust oder instabile Zeitschritte. print() F\u00e4ngt viele Anf\u00e4ngerfehler auf, aber Behauptungen und Debugger erleichtern die Isolierung. Ein zuverl\u00e4ssiger wissenschaftlicher Debugging-Workflow ist: Versagen Sie es zuverl\u00e4ssig, isolieren Sie [&hellip;]<\/p>\n","protected":false,"raw":""},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_locale":"de_DE","_original_post":"https:\/\/matforge.org\/?p=336","iawp_total_views":0,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-883","post","type-post","status-publish","format-standard","hentry","category-simulation-modeling-projects","de-DE"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Debuggen von wissenschaftlichem Python-Code<\/title>\n<meta name=\"description\" content=\"Erfahren Sie, wie Sie wissenschaftlichen Python-Code mit Drucken, Protokollierung, Assertions, PDB, Profiling, PyTest und numerischen Sanity Checks debuggen.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Debuggen von wissenschaftlichem Python-Code\" \/>\n<meta property=\"og:description\" content=\"Erfahren Sie, wie Sie wissenschaftlichen Python-Code mit Drucken, Protokollierung, Assertions, PDB, Profiling, PyTest und numerischen Sanity Checks debuggen.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/\" \/>\n<meta property=\"og:site_name\" content=\"matforge.org\" \/>\n<meta property=\"article:published_time\" content=\"2026-07-30T12:23:24+00:00\" \/>\n<meta name=\"author\" content=\"Priya Nair\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Priya Nair\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"16\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-debugging-scientific-code-print-statements-profiling\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-debugging-scientific-code-print-statements-profiling\\\/\"},\"author\":{\"name\":\"Priya Nair\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/2effd7bc155a5e6357f31dac970c5795\"},\"headline\":\"Python-Debugging f\u00fcr wissenschaftlichen Code: Vom Druckausweis bis zum Profiling\",\"datePublished\":\"2026-07-30T12:23:24+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-debugging-scientific-code-print-statements-profiling\\\/\"},\"wordCount\":2451,\"commentCount\":0,\"articleSection\":[\"Simulation & amp; Modellierungsprojekte\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/matforge.org\\\/de\\\/python-debugging-scientific-code-print-statements-profiling\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-debugging-scientific-code-print-statements-profiling\\\/\",\"url\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-debugging-scientific-code-print-statements-profiling\\\/\",\"name\":\"Debuggen von wissenschaftlichem Python-Code\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#website\"},\"datePublished\":\"2026-07-30T12:23:24+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/2effd7bc155a5e6357f31dac970c5795\"},\"description\":\"Erfahren Sie, wie Sie wissenschaftlichen Python-Code mit Drucken, Protokollierung, Assertions, PDB, Profiling, PyTest und numerischen Sanity Checks debuggen.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-debugging-scientific-code-print-statements-profiling\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/matforge.org\\\/de\\\/python-debugging-scientific-code-print-statements-profiling\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-debugging-scientific-code-print-statements-profiling\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/matforge.org\\\/de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Python-Debugging f\u00fcr wissenschaftlichen Code: Vom Druckausweis bis zum Profiling\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#website\",\"url\":\"https:\\\/\\\/matforge.org\\\/\",\"name\":\"matforge.org\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/matforge.org\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/2effd7bc155a5e6357f31dac970c5795\",\"name\":\"Priya Nair\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g\",\"caption\":\"Priya Nair\"},\"sameAs\":[\"http:\\\/\\\/matforge.org\"],\"url\":\"https:\\\/\\\/matforge.org\\\/author\\\/priya-nair\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Debuggen von wissenschaftlichem Python-Code","description":"Erfahren Sie, wie Sie wissenschaftlichen Python-Code mit Drucken, Protokollierung, Assertions, PDB, Profiling, PyTest und numerischen Sanity Checks debuggen.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/","og_locale":"de_DE","og_type":"article","og_title":"Debuggen von wissenschaftlichem Python-Code","og_description":"Erfahren Sie, wie Sie wissenschaftlichen Python-Code mit Drucken, Protokollierung, Assertions, PDB, Profiling, PyTest und numerischen Sanity Checks debuggen.","og_url":"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/","og_site_name":"matforge.org","article_published_time":"2026-07-30T12:23:24+00:00","author":"Priya Nair","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Priya Nair","Gesch\u00e4tzte Lesezeit":"16\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/#article","isPartOf":{"@id":"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/"},"author":{"name":"Priya Nair","@id":"https:\/\/matforge.org\/#\/schema\/person\/2effd7bc155a5e6357f31dac970c5795"},"headline":"Python-Debugging f\u00fcr wissenschaftlichen Code: Vom Druckausweis bis zum Profiling","datePublished":"2026-07-30T12:23:24+00:00","mainEntityOfPage":{"@id":"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/"},"wordCount":2451,"commentCount":0,"articleSection":["Simulation & amp; Modellierungsprojekte"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/","url":"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/","name":"Debuggen von wissenschaftlichem Python-Code","isPartOf":{"@id":"https:\/\/matforge.org\/#website"},"datePublished":"2026-07-30T12:23:24+00:00","author":{"@id":"https:\/\/matforge.org\/#\/schema\/person\/2effd7bc155a5e6357f31dac970c5795"},"description":"Erfahren Sie, wie Sie wissenschaftlichen Python-Code mit Drucken, Protokollierung, Assertions, PDB, Profiling, PyTest und numerischen Sanity Checks debuggen.","breadcrumb":{"@id":"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/matforge.org\/de\/python-debugging-scientific-code-print-statements-profiling\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/matforge.org\/de\/"},{"@type":"ListItem","position":2,"name":"Python-Debugging f\u00fcr wissenschaftlichen Code: Vom Druckausweis bis zum Profiling"}]},{"@type":"WebSite","@id":"https:\/\/matforge.org\/#website","url":"https:\/\/matforge.org\/","name":"matforge.org","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/matforge.org\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Person","@id":"https:\/\/matforge.org\/#\/schema\/person\/2effd7bc155a5e6357f31dac970c5795","name":"Priya Nair","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g","caption":"Priya Nair"},"sameAs":["http:\/\/matforge.org"],"url":"https:\/\/matforge.org\/author\/priya-nair\/"}]}},"_links":{"self":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/883","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/comments?post=883"}],"version-history":[{"count":1,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/883\/revisions"}],"predecessor-version":[{"id":1025,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/883\/revisions\/1025"}],"wp:attachment":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/media?parent=883"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/categories?post=883"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/tags?post=883"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}