Schlüssel 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äzisionsverlust oder instabile Zeitschritte.
print()Fängt viele Anfängerfehler auf, aber Behauptungen und Debugger erleichtern die Isolierung.- Ein zuverlässiger wissenschaftlicher Debugging-Workflow ist: Versagen Sie es zuverlässig, isolieren Sie das Problem, ändern Sie eines, überprüfen Sie und fügen Sie einen Test hinzu.
- Profiling kann versteckte Fehler aufdecken, da langsamer Code oft ein Zeichen falscher Array-Logik, versehentlicher Schleifen oder übermäßiger Zuweisung ist.
Das Debuggen von wissenschaftlichem Python-Code ist eine der frustrierendsten Fähigkeiten, die es zu entwickeln gilt. Sie schreiben, was eine einfache Wärmediffusionssimulation sein sollte, der Code läuft fehlerfrei und die Ausgabe ist völlig falsch.
Kein Absturz. Keine Spur. Nur lautlos falsche Zahlen.
Das ist die einzigartige Herausforderung des wissenschaftlichen Debuggens. Die gefährlichsten Fehler sind oft diejenigen, die überhaupt nicht abstürzen. Eine falsche Zahl kann durch Tausende von Iterationen propagieren und ein Ergebnis erzeugen, das physisch plausibel aussieht, bis Sie die Erhaltung, Stabilität oder Konvergenz überprüfen.
In diesem Handbuch wird erläutert, wie Sie wissenschaftlichen Python-Code auf mehreren Ebenen debuggen: Druckanweisungen, Protokollierung, Assertions, interaktives Debuggen, Profiling und Regressionstests.
Egal, ob Sie eine erste FIPY-Simulation durchführen oder eine Produktionsforschungs-Pipeline debuggen, dieser Workflow bietet Ihnen eine praktische Struktur.
Was ist anders beim Debuggen von wissenschaftlichem Code?
Das wissenschaftliche Debuggen unterscheidet sich vom Debuggen einer Web-App, API oder einer gewöhnlichen Daten-Pipeline.
Die Zahlen können lügen. Eine Web-App schlägt oft sichtbar fehl: Eine Schaltfläche funktioniert nicht, eine API gibt einen Fehler zurück oder eine Datenbankabfrage schlägt fehl. Wissenschaftlicher Code kann perfekt laufen und trotzdem falsche Ergebnisse liefern. Eine negative Konzentration, ein explodierendes Energiefeld oder eine versteckte NAN können nur angezeigt werden, wenn Sie direkt danach suchen.
Korrektheit wird durch die Physik definiert, nicht nur durch die Syntax. Ein Python-Programm, das ausgeführt wird, ist syntaktisch gültig. Ein wissenschaftliches Programm ist nur dann glaubwürdig, wenn es bekannte Lösungen entspricht, erwartete Mengen bewahrt, Randbedingungen einhält und unter Verfeinerung konvergiert.
Stapelspuren zeigen oft in numerische, scipy oder fipy Interna. Der eigentliche Fehler liegt möglicherweise in Ihrem Setup, aber die Fehlermeldung kann tief in einer Bibliothek angezeigt werden. Deshalb braucht das wissenschaftliche Debuggen nicht nur das Lesen von Traceback.
Der Debugging-Workflow: Ein fünfstufiges mentales Modell
Ein nützlicher Debugging-Workflow für den wissenschaftlichen Code folgt fünf Schritten.
Schritt 1: Lassen Sie es zuverlässig scheitern
Ihre erste Aufgabe besteht darin, einen Testfall zu erstellen, der jedes Mal fehlschlägt. Im wissenschaftlichen Kodex bedeutet dies normalerweise, ein minimal reproduzierbares Beispiel zu erstellen, das das falsche Ergebnis liefert.
Sie benötigen nicht die vollständige Simulation. Sie benötigen den kleinsten Code, der den Fehler demonstriert.
import numpy as np
# Full simulation may be hundreds of lines.
# Start with the smallest failing update step.
phi = np.ones((10, 10))
phi[:] = 0.5
gradient = np.gradient(phi)[0]
phi_new = phi - 0.1 * gradient
print(f"min(phi_new) = {np.min(phi_new)}")
print(f"max(phi_new) = {np.max(phi_new)}")
Durch die Isolierung des Aktualisierungsschritts erstellen Sie einen reproduzierbaren Fall. Dies ist die Grundlage für jeden späteren Debugging-Schritt.
Schritt 2: Teilen und erobern
Wenn Sie einen Fall nicht haben, isolieren Sie ihn weiter. Suchen Sie den für den Fehler verantwortlichen Modul, Funktion, Linie oder Physik.
Für den Simulationscode besteht eine nützliche 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.
solution_diffusion = solve_diffusion_only(initial_conditions, dt)
assert np.all(solution_diffusion >= 0), "Diffusion produced negative values"
solution_reaction = solve_reaction_only(initial_conditions, dt)
assert np.all(solution_reaction >= 0), "Reaction produced negative values"
solution_coupled = solve_coupled(initial_conditions, dt)
Dies verengt den Suchraum. Anstatt die gesamte Simulation zu debuggen, debuggen Sie einen Term oder einen Kopplungsmechanismus.
Schritt 3: Ändern Sie jeweils eine Sache
Das wissenschaftliche Debuggen wird verwirrend, wenn Sie mehrere Variablen gleichzeitig ändern. Wenn Sie den Zeitschritt, die Randbedingungen, die Solvertoleranz und die Anfangsbedingung zusammen ändern, können Sie nicht wissen, welche Änderung wichtig ist.
Ändern Sie einen Parameter. laufen vergleichen dokumentieren
Verwenden Sie die Versionskontrolle, um Änderungen zu verfolgen. Selbst einfache Commits wie „Änderte DT von 0,01 auf 0,001“ können Stunden später sparen.
Schritt 4: Verwenden Sie den Debugger
Wenn Drucke und Zusicherungen nicht ausreichen, verwenden Sie pdb, ipdb oder einen IDE-Debugger. Mit einem Debugger können Sie die Ausführung anhalten, Variablen prüfen, Code durchlaufen und überprüfen, warum sich ein Wert geändert hat.
Schritt 5: Zur Testsuite hinzufügen
Nachdem Sie einen Fehler behoben haben, fügen Sie einen Test hinzu, der ihn abgefangen hätte. Dies verhindert, dass der gleiche Fehler später zurückkehrt.
def test_no_negative_phases():
"""Verify phase field stays non-negative after diffusion."""
phi = np.ones((10, 10))
phi = diffusion_step(phi, dt=0.01)
assert np.min(phi) >= 0, f"Got min value {np.min(phi)}"
Der Workflow ist einfach: isolieren, dividieren, ändern, ändern, debuggen und testen. Der Rest dieser Anleitung erklärt, welche Tools auf jeder Ebene verwendet werden sollen.
Stufe 1: Anweisungen drucken und protokollieren
print() ist immer noch nützlich. Es ist das einfachste Debugging-Tool und fängt viele frühe Fehler auf. Der Schlüssel ist, es mit Struktur zu verwenden.
Das Problem mit schlechten Drucken
Ein häufiger Anfängerfehler ist das Drucken von Werten ohne Kontext.
for i in range(100):
print(phi[i])
Nach ein paar Iterationen haben Sie viele Zahlen ohne klare Bedeutung. Sie wissen nicht, welche Variable, Zeitschritt oder Bedingung jede Zeile erzeugt hat.
Der richtige Weg: Strukturierte Drucke
Alles beschriften. Schließen Sie den Zeitschritt, den Variablennamen, das Minimum, das Maximum und jede konservierte Menge ein, die Ihnen wichtig ist.
import numpy as np
print(
f"[t={t:06.4f}] "
f"min(phi)={np.min(phi):.6f}, "
f"max(phi)={np.max(phi):.6f}"
)
print(
f"[t={t:06.4f}] "
f"energy={np.sum(phi * phi):.6f}, "
f"mass={np.sum(phi):.6f}"
)
Diese Ausgabe ist einfach zu scannen:
[t=0.0010] min(phi)=0.000000, max(phi)=1.000000
[t=0.0010] energy=0.850000, mass=1.000000
[t=0.0020] min(phi)=0.000000, max(phi)=1.023456
[t=0.0020] energy=0.873456, mass=1.012345
Sie sehen sofort, dass der Maximalwert über 1,0 gestiegen ist und die Masse geändert wird. Das sind nützliche Hinweise.
Protokollierung vs. Druckanweisungen
Verwenden Sie für ein ernsthaftes Debuggen das Modul logging von Python anstelle von RAW print() .
import logging
logging.basicConfig(
level=logging.DEBUG,
format="%(levelname)s: %(message)s"
)
logger = logging.getLogger(__name__)
logger.debug("Running diffusion step at t=%f, dt=%f", t, dt)
logger.warning("Energy increased by %.5f", energy_change)
Logging hat mehrere Vorteile:
- Ebenen wie
DEBUG,WARNINGundERROR. - Optionale Zeitstempel und Dateiausgabe.
- Einfache Aktivierung oder Deaktivierung ohne Löschen des Debug-Codes.
- Reinigere Diagnose bei langen Simulationsläufen.
Eine praktische Regel ist die Verwendung von print() für schnelle Erkundungsskripte und logging für wiederverwendbare oder produktionstechnische Simulationscodes.
Stufe 2: Behauptungen und Vernunftprüfungen
Behauptungen sind wie gedruckte Aussagen, die sich weigern, zu schweigen. Sie werfen einen Fehler auf, wenn eine Annahme verletzt wird.
Das Muster: Annahmen zuerst
Das nützlichste Muster besteht darin, die Eingaben zu überprüfen, den Solver auszuführen und die Ausgaben zu überprüfen.
import numpy as np
def solve_diffusion(phi, dt, diffusion_coeff):
# Preconditions
assert np.all(phi >= 0), f"Negative phase: {np.min(phi)}"
assert np.all(phi <= 1), f"Phase > 1: {np.max(phi)}"
assert np.isfinite(phi).all(), "Non-finite input values detected"
# Solver step
phi_new = apply_diffusion(phi, dt, diffusion_coeff)
# Postconditions
assert np.isfinite(phi_new).all(), "Solver produced NaN or Inf"
assert np.all(phi_new >= 0), f"Negative output: {np.min(phi_new)}"
return phi_new
Dadurch wird ein stiller numerischer Fehler in einen eindeutigen Fehler mit Ort und Kontext.
Die Behauptungsphilosophie
Bei Behauptungen geht es nicht nur darum, Fehler zu fangen. Sie heften Wanzen an.
Eine Behauptung, die am Anfang eines Solvers fehlschlägt, sagt Ihnen, wo die Annahme gebrochen ist. Ein Nan, das Hunderte von Zeilen später erscheint, sagt Ihnen viel weniger.
Nützliche Orte für Behauptungen sind:
- Vor und nach dem Solver-Aufruf.
- an Modulgrenzen, wo Daten ausgetauscht werden.
- Nach Netzoperationen wie Verfeinerung, Restricken oder Vergröbern.
- Nach dem Anwenden von Randbedingungen.
Eine Warnung vor Behauptungen
Verwenden Sie die Behauptungen in teuren inneren Schleifen nicht übermäßig, es sei denn, Sie benötigen sie. Sie können Runtime-Overhead hinzufügen und das Debuggen laut machen.
Verwenden Sie Assertions für Invarianten und Vorbedingungen: Bedingungen, die niemals verletzt werden sollten, wenn der Code korrekt ist.
Level 3: Der Python-Debugger
Wenn Druckanweisungen nicht ausreichen, verwenden Sie einen interaktiven Debugger. Mit einem Debugger können Sie das Programm anhalten, den Status überprüfen und die Logik durchlaufen.
Das klassische Werkzeug: PDB
pdb ist der integrierte Debugger von Python. Es ist in jeder Python-Installation verfügbar.
In IPython können Sie nach einem Fehler das Post-Mortem-Debugging verwenden:
In [1]: %run simulation.py
In [2]: %debug
Dies bringt Sie zum Fehlerpunkt in den Debugger.
Starten Sie für Befehlszeilenskripte das Skript mit dem Debugger:
python -m pdb simulation.py
Nützliche Befehle sind:
c: Weiterführung.n: Gehen Sie zur nächsten Zeile.s: Schritt in einen Funktionsaufruf.b: Legen Sie einen Haltepunkt fest.p variable_name: Drucken Sie eine Variable.q: Beenden Sie den Debugger.
Sie können auch einen Haltepunkt direkt in Code einfügen:
def solve_phase_field(phi, dt):
import pdb
pdb.set_trace()
phi_new = update_phase_field(phi, dt)
return phi_new
IPDB- und IPython-Debugging
ipdb ist eine erweiterte Version von pdb. Es bietet Tab-Vervollständigung, bessere Formatierung und ein komfortableres interaktives Erlebnis.
pip install ipdb
import ipdb
ipdb.set_trace()
Für IPython-Benutzer ist %debug oft die schnellste Option nach einer Ausnahme.
IDE-Debugger
PyCharm, VS Code und Spyder enthalten grafische Debugger. Sie können Breakpoints setzen, Variablen prüfen, Code durchlaufen und Ausdrücke ohne Eingabe von Befehlen eingeben.
IDE-Debugger sind hilfreich für die lokale Entwicklung. Für Remote-Cluster oder Headless-Server sind pdb, ipdb und die Protokollierung in der Regel praktischer.
Wann verwenden Sie welches Werkzeug?
| Szenario | Empfohlenes Werkzeug |
|---|---|
| Schnelles Skript oder frühe lokale Entwicklung | print() plus Behauptungen |
| Fehlerverfolgung in Ipython | %debug |
| Remote-Cluster ohne IDE-Zugriff | python -m pdb script.py |
| Interaktives Debuggen mit besserem Kontext | ipdb oder IDE-Debugger |
| Diagnose im Produktionsstil | logging mit strategischen Breakpoints |
Level 4: Profiling, um versteckte Fehler zu finden
Profiling wird normalerweise als Leistungstool angesehen, hilft aber auch beim Debuggen. Langsamer Code ist oft falscher Code. Profiler können zufällige Python-Schleifen, wiederholte Zuordnungen, redundante Berechnungen oder unerwartete Funktionsaufrufe anzeigen.
cProfile: Finden Sie die Hot-Funktionen
import cProfile
import pstats
profiler = cProfile.Profile()
profiler.enable()
solve_phase_field(phi, dt, n_iterations)
profiler.disable()
stats = pstats.Stats(profiler)
stats.sort_stats("cumulative")
stats.print_stats(20)
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.
line_profiler: Finden Sie die Hotlines
Wenn cProfile eine langsame Funktion identifiziert, kann line_profiler anzeigen, welche Zeilen innerhalb der Funktion Zeit verbrauchen.
pip install line_profiler
kernprof -l -v simulation.py
Dies ist nützlich, wenn eine einzelne Zeile innerhalb einer Schleife wiederholt Arrays erzeugt oder versehentlich einen langsamen Vorgang ausführt.
Speicherprofilierung: versteckte Zuordnungen finden
Der wissenschaftliche Code verbirgt häufig Speicherfehler wie versehentliche Kopien oder übermäßige Arrayerstellung in Schleifen. memory_profiler kann bei der Verfolgung von Zuordnungen helfen.
from memory_profiler import profile
@profile
def solve_phase_field(phi, dt):
phi_new = apply_diffusion(phi, dt)
return phi_new
Zeilen-für-Linien-Speicherprofile können zeigen, wo große Arrays erstellt und verworfen werden.
Profiling als Debuggen
Profiling hilft, wenn ein Solver sowohl falsch als auch langsam ist. Beispielsweise kann ein falscher Schnitt Energie über den falschen Bereich berechnen. Ein Profil kann zeigen, dass die Energieberechnung unerwartet teuer ist, was Sie zurück zum Array-Vorgang hinweist.
Behauptungen bestätigen den Fehler. Profiling hilft Ihnen zu finden, wo Sie suchen müssen.
Stufe 5: Testen und Regressionsprävention
Das endgültige Debugging-Niveau ist die Prävention. Schreiben Sie nach dem Beheben eines Fehlers einen Test, der den Fehler reproduziert und beweist, dass der Fix funktioniert.
Das wissenschaftliche Testmuster
import pytest
import numpy as np
@pytest.mark.parametrize("mesh_size", [10, 20, 50])
def test_energy_decreases(mesh_size):
"""Energy should decrease during simple diffusion."""
phi = init_phi(mesh_size)
energy_history = []
for step in range(100):
energy_history.append(compute_energy(phi))
phi = solve_step(phi, dt=0.01)
energy_changes = np.diff(energy_history)
assert np.all(energy_changes <= 1e-10)
Dieser Test überprüft das physikalische Verhalten, nicht nur die Syntax. Eine korrekte Simulation sollte nicht nur ausgeführt werden. Es sollte erwartete Mengen erhalten, Grenzen einhalten und mit der Verfeinerung konvergieren.
Die Testpyramide für wissenschaftlichen Code
- Unit-Tests sind schnell und prüfen einzelne Funktionen wie
compute_gradient()oderapply_boundary_conditions(). - Integrationstests führen Überprüfungen auf Solver-Ebene gegen analytische oder hergestellte Lösungen durch.
- Regressionstests vergleichen vollständige Simulationen mit vertrauenswürdigen Baseline-Ausgaben.
Das Schlüsselprinzip ist einfach: Testen Sie die Physik, nicht nur den Codepfad.
Häufige wissenschaftliche Debugging-Fallen
Nan und Unendlichkeit
NAN- und Unendlichkeitswerte sind im wissenschaftlichen Kodex üblich. Sie treten oft nach Division durch Werte nahe Null, log(0) , sqrt negativer Werte oder Überlauf in Exponentialen auf.
phi = np.zeros((100, 100))
phi[50, 50] = 1.0
result = phi / (phi + 1e-300)
Nach numerischen Operationen auf endliche Werte prüfen:
assert np.isfinite(result).all(), (
f"NaN or Inf detected: min={np.nanmin(result)}, max={np.nanmax(result)}"
)
Array-Form-Fehlanpassungen
Array-Formfehler sind häufig beim Wechsel zwischen abgeflachten Solver-Arrays und 2D- oder 3D-Feldern.
print(f"phi shape: {phi.shape}")
print(f"expected shape: {expected_shape}")
Zu den häufigsten Ursachen gehören versehentliches Abflachen, Indizierung, die eine Dimension entfernt, und Bibliotheksvariablen, die Daten in einer anderen Form als erwartet speichern.
Gleitkomma-Präzision
Vergleichen Sie Gleitkomma-Arrays nicht mit exakter Gleichheit, es sei denn, es wird wirklich eine genaue Gleichheit erwartet. Verwenden Sie toleranzbasierte Vergleiche.
np.testing.assert_allclose(phi, phi_new, rtol=1e-6, atol=1e-12)
Dies ist besonders wichtig für iterative Solver, gemischte Präzision und langzeitige Integrationen.
Randbedingungsfehler
Randbedingungsfehler sind häufig, da der Code häufig ausgeführt wird, auch wenn die falschen Zellen oder Gesichter eingeschränkt sind.
# Wrong: overwrites all cells
phi[:] = boundary_value
# Better: apply only to selected boundary locations
phi[boundary_mask] = boundary_value
Stellen Sie immer sicher, dass Grenzwerte nur dort angewendet werden, wo beabsichtigt und Innenwerte nicht überschrieben werden.
Konvergenz und Stabilität
Simulationen können lautlos auseinander gehen, wenn die Zeitschritte zu groß oder die Solvertoleranzen zu locker sind. Überwachen Sie konservierte oder begrenzte Mengen im Laufe der Zeit.
if energy > prev_energy * 1.1:
print(f"Energy growing. Reducing dt from {dt} to {dt / 2}")
dt /= 2
Verfolgen Sie Energie, Masse, Impuls, Grenzen, Residuen und Zeitschrittverhalten. Zeichnen Sie sie während des Debuggens.
Die Debugging-Eskalationsleiter
Verwenden Sie die niedrigste Debug-Ebene, die genügend Informationen liefert.
Level 1: print() — quick inspection
Level 2: assert() — sanity checks and invariants
Level 3: pdb/ipdb — interactive state inspection
Level 4: profiling — performance bugs and hidden inefficiency
Level 5: pytest — regression prevention
Bei Bedarf eskalieren:
- Wenn
print()genügend Kontext gibt, bleiben Sie auf Stufe 1. - Wenn Sie Invarianten überprüfen müssen, verwenden Sie Assertions.
- Wenn Sie den Status in einer bestimmten Zeile überprüfen müssen, verwenden Sie
pdboderipdb. - Wenn der Code falsch und langsam ist, profilieren Sie ihn.
- Wenn Sie verhindern möchten, dass der Fehler zurückkehrt, schreiben Sie einen Test.
Ein praktisches Debugging-Beispiel: Fixieren einer FIPY-Simulation
Angenommen, eine FIPY-Diffusionssimulation erzeugt unbegrenzte Konzentrationswerte anstelle einer glatten Diffusion. Schreiben Sie zunächst eine minimale, klare Version des Problems.
import numpy as np
from fipy import Grid2D, CellVariable, TransientTerm, DiffusionTerm
mesh = Grid2D(nx=50, ny=50, dx=1.0, dy=1.0)
phi = CellVariable(name="concentration", mesh=mesh, value=0.0)
x, y = mesh.cellCenters
# Initial hot spot in the center
phi.setValue(
1.0,
where=((x - 25.0)**2 + (y - 25.0)**2) < 25.0
)
eq = TransientTerm(var=phi) == DiffusionTerm(coeff=1.0, var=phi)
dt = 0.01
print(f"Initial: min={phi.value.min()}, max={phi.value.max()}")
for step in range(100):
eq.solve(var=phi, dt=dt)
assert np.isfinite(phi.value).all(), "NaN or Inf detected"
assert phi.value.min() >= -1e-10, f"Negative value at step {step}"
print(f"Final: min={phi.value.min()}, max={phi.value.max()}")
Dann systematisch debuggen.
Schritt 1: Lassen Sie es zuverlässig scheitern
Protokollieren Sie Minimum, Maximum, Masse und Energie vor und nach dem Lauf.
print(f"min={phi.value.min()}, max={phi.value.max()}")
print(f"mass={np.sum(phi.value * mesh.cellVolumes)}")
print(f"energy={np.sum(phi.value**2 * mesh.cellVolumes)}")
Schritt 2: Teilen und erobern
Testen Sie den Diffusionsbegriff allein. Entfernen Sie Reaktionsbegriffe, Advektionsbegriffe, nichtlineare Quellen und komplexe Randbedingungen, bis der Fehler verschwindet oder isoliert wird.
Schritt 3: Ändern Sie eine Sache
Versuchen Sie einen kleineren Zeitschritt und vergleichen Sie die Ergebnisse.
dt = 0.001
Wenn der Fehler bei einem kleineren Zeitschritt besteht, kann das Problem räumlich, strukturell oder in Bezug auf Randbedingungen und nicht auf Zeitstabilität liegen.
Schritt 4: Verwenden Sie den Debugger
In einen Debugger in der Nähe des ausfallenden Schritts fallen und Werte, Netzform, Masken und Randbedingungen überprüfen.
import pdb
pdb.set_trace()
Schritt 5: Testen Sie einen Test
Nachdem Sie den Fehler behoben haben, fügen Sie einen Regressionstest hinzu.
def test_diffusion_mass_stays_finite():
phi_initial, phi_final, volumes = run_small_diffusion_case()
mass_initial = np.sum(phi_initial * volumes)
mass_final = np.sum(phi_final * volumes)
np.testing.assert_allclose(mass_initial, mass_final, rtol=1e-6)
assert np.isfinite(phi_final).all()
Zusammenfassung und nächste Schritte
Das Debuggen des wissenschaftlichen Python-Codes ist eine progressive Fähigkeit. Beginnen Sie mit Druckanweisungen, wechseln Sie zu Assertionen, verwenden Sie Debugger, wenn eine Statusinspektion erforderlich ist, profilieren Sie, wenn der Code langsam oder verdächtig ist, und schreiben Sie Tests, um Regressionen zu verhindern.
Das wichtigste Prinzip ist es, den Fehler zu isolieren, bevor er behoben wird. Ein Fehler, den Sie in einem kleinen Testfall reproduzieren können, ist viel einfacher zu beheben. Ein Fehler, der nur in einer großen Simulation erscheint, bleibt ein Rätsel.
Für wissenschaftlichen Kodex merken Sie sich folgende Regeln:
- Überprüfen Sie nach numerischen Operationen auf NAN und Unendlichkeit.
- Drucken von Array-Formen, wenn die Indizierung komplex wird.
- Überwachen Sie konservierte Größen wie Masse, Energie und Impuls.
- Testen Sie die Randbedingungen getrennt von der inneren Logik.
- Verwenden Sie Behauptungen als erste Verteidigungslinie gegen stilles numerisches Versagen.
Nächste Schritte
- Beginnen Sie mit der Debugging-Eskalationsleiter: Beginnen Sie mit
print()und eskalieren nur bei Bedarf. - Fügen Sie Assertions vor und nach dem Solver-Funktionen hinzu.
- Einrichten der Protokollierung für die Simulationsdiagnose.
- Schreiben Sie mindestens einen Testfall, der einen allgemeinen Fehler reproduziert.
Debuggen ist kein Zeichen von Schwäche. Es ist Teil des Schreibens von Code, der zählt. Die Fähigkeiten, die Sie beim Debuggen des wissenschaftlichen Python-Codes aufbauen, helfen bei allen Arten von numerischen und Forschungssoftwareprojekten.
Verwandte Anleitungen
- Leistungsprofilierung und -optimierung für Python-PDE-Solver: Ein praktischer Leitfaden – Profilerstellungstools, NUMBA und Speicheroptimierung.
- Einheitentest für wissenschaftlichen Code: PyTest-Strategien – Numerische Präzision und Testmuster.
- Kontinuierliche Integration für Forschungssoftware: Automatisiertes Testen und Validieren — CI-Pipelines und automatisierte Tests.
- Best Practices für die Aufrechterhaltung wissenschaftlicher Codes – Projektstruktur, Versionskontrolle und Dokumentation.
- Verwenden von FIPY für Phasenfeldmodellierung – FIPY-Implementierungsmuster.
Benötigen Sie Hilfe beim Debuggen Ihres Simulationscodes?
Das Debuggen komplexer wissenschaftlicher Simulationen kann zeitaufwändig 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.
Unser Team kann bei systematischen Debugging-Workflows, numerischen Testfällen, Sanity-Checks für Erhaltungsgesetze und CI-Pipelines mit Regressionstests helfen.