{"id":1269,"date":"2026-08-21T14:31:33","date_gmt":"2026-08-21T14:31:33","guid":{"rendered":"https:\/\/matforge.org\/?p=1269","raw":"https:\/\/matforge.org\/?p=1269"},"modified":"2026-08-21T14:31:33","modified_gmt":"2026-08-21T14:31:33","slug":"python-debugging-scientific-code-print-statements-profiling","status":"publish","type":"post","link":"https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/","title":{"rendered":"D\u00e9bogage Python pour le code scientifique : des relev\u00e9s d&rsquo;impression au profilage","raw":"D\u00e9bogage Python pour le code scientifique : des relev\u00e9s d'impression au profilage"},"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\"> 12<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span><h2>Points \u00e0 retenir cl\u00e9s<\/h2>\n<ul>\n<li>Le d\u00e9bogage du code Python scientifique suit une \u00e9chelle d&rsquo;escalade&nbsp;: <code>print<\/code>, <code>assert<\/code>, <code>pdb<\/code>, profilage et tests.<\/li>\n<li>La plupart des bogues scientifiques sont num\u00e9riques, tels que les valeurs de NAN, les incompatibilit\u00e9s de forme de tableau, la perte de pr\u00e9cision ou les pas de temps instables.<\/li>\n<li><code>print()<\/code> attrape de nombreux bogues pour d\u00e9butants, mais les assertions et les d\u00e9bogueurs facilitent l&rsquo;isolement des \u00e9checs.<\/li>\n<li>Un workflow de d\u00e9bogage scientifique fiable est&nbsp;: faites-le \u00e9chouer de mani\u00e8re fiable, isolez le probl\u00e8me, modifiez une chose, v\u00e9rifiez et ajoutez un test.<\/li>\n<li>Le profilage peut r\u00e9v\u00e9ler des bogues cach\u00e9s car le code lent est souvent le signe d&rsquo;une logique de tableau, de boucles accidentelles ou d&rsquo;une allocation excessive.<\/li>\n<\/ul>\n<p>Le d\u00e9bogage du code Python scientifique est l&rsquo;une des comp\u00e9tences les plus frustrantes \u00e0 d\u00e9velopper. Vous \u00e9crivez ce qui devrait \u00eatre une simple simulation de diffusion de la chaleur, le code s&rsquo;ex\u00e9cute sans erreur et la sortie est compl\u00e8tement erron\u00e9e.<\/p>\n<p>Pas de crash. Pas de trace. Des chiffres juste en silence.<\/p>\n<p>C&rsquo;est le d\u00e9fi unique du d\u00e9bogage scientifique. Les insectes les plus dangereux sont souvent ceux qui ne plantent pas du tout. Un nombre incorrect peut se propager \u00e0 travers des milliers d&rsquo;it\u00e9rations et produire un r\u00e9sultat qui semble physiquement plausible jusqu&rsquo;\u00e0 ce que vous v\u00e9rifiiez la conservation, la stabilit\u00e9 ou la convergence.<\/p>\n<p>Ce guide explique comment d\u00e9boguer le code Python scientifique \u00e0 plusieurs niveaux : d\u00e9clarations d&rsquo;impression, journalisation, assertions, d\u00e9bogage interactif, profilage et r\u00e9gression.<\/p>\n<p>Que vous ex\u00e9cutiez une premi\u00e8re simulation FIPY ou d\u00e9boguez un pipeline de recherche de production, ce flux de travail vous donne une structure pratique \u00e0 suivre.<\/p>\n<h2>Qu&rsquo;y a-t-il de diff\u00e9rent dans le d\u00e9bogage du code scientifique&nbsp;?<\/h2>\n<p>Le d\u00e9bogage scientifique est diff\u00e9rent du d\u00e9bogage d&rsquo;une application Web, d&rsquo;une API ou d&rsquo;un pipeline de donn\u00e9es ordinaire.<\/p>\n<p>Les chiffres peuvent mentir. Une application Web \u00e9choue souvent de mani\u00e8re visible&nbsp;: un bouton ne fonctionne pas, une API renvoie une erreur ou une requ\u00eate de base de donn\u00e9es \u00e9choue. Le code scientifique peut fonctionner parfaitement et produire des r\u00e9sultats erron\u00e9s. Une concentration n\u00e9gative, un champ d&rsquo;\u00e9nergie explosif ou une NAN cach\u00e9e ne peut appara\u00eetre que si vous le v\u00e9rifiez directement.<\/p>\n<p>L&rsquo;exactitude est d\u00e9finie par la physique, pas seulement par la syntaxe. Un programme Python qui s&rsquo;ex\u00e9cute est syntaxiquement valide. Un programme scientifique n&rsquo;est cr\u00e9dible que s&rsquo;il correspond \u00e0 des solutions connues, conserve des quantit\u00e9s attendues, respecte les conditions aux limites et converge vers le raffinement.<\/p>\n<p>Les traces d&#8217;empilage pointent souvent vers des internes NumPy, Scipy ou Fipy. Le vrai bogue peut \u00eatre dans votre configuration, mais le message d&rsquo;erreur peut appara\u00eetre au plus profond d&rsquo;une biblioth\u00e8que. C&rsquo;est pourquoi le d\u00e9bogage scientifique a besoin d&rsquo;un processus structur\u00e9, pas seulement de la lecture des traces.<\/p>\n<h2>Le flux de travail de d\u00e9bogage : un mod\u00e8le mental en cinq \u00e9tapes<\/h2>\n<p>Un flux de travail de d\u00e9bogage utile pour le code scientifique suit cinq \u00e9tapes.<\/p>\n<h3>\u00c9tape&nbsp;1&nbsp;: faites-le \u00e9chouer de mani\u00e8re fiable<\/h3>\n<p>Votre premier travail consiste \u00e0 cr\u00e9er un cas de test qui \u00e9choue \u00e0 chaque fois. Dans le code scientifique, cela signifie g\u00e9n\u00e9ralement construire un exemple reproductible minimal qui produit le mauvais r\u00e9sultat.<\/p>\n<p>Vous n&rsquo;avez pas besoin de la simulation compl\u00e8te. Vous avez besoin du plus petit morceau de code qui d\u00e9montre le bogue.<\/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>En isolant l&rsquo;\u00e9tape de mise \u00e0 jour, vous cr\u00e9ez un cas reproductible. C&rsquo;est la base de chaque \u00e9tape de d\u00e9bogage ult\u00e9rieure.<\/p>\n<h3>\u00c9tape&nbsp;2&nbsp;: Diviser et conqu\u00e9rir<\/h3>\n<p>Une fois que vous avez un cas d\u00e9faillant, isolez-le davantage. Trouvez le module, la fonction, la ligne ou le terme physique responsable de la d\u00e9faillance.<\/p>\n<p>Pour le code de simulation, une tactique utile consiste \u00e0 tester s\u00e9par\u00e9ment chaque terme de physique. Si votre mod\u00e8le combine la diffusion, la r\u00e9action et l&rsquo;advection, testez chacun seul avant de d\u00e9boguer le syst\u00e8me coupl\u00e9.<\/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>Cela r\u00e9duit l&rsquo;espace de recherche. Au lieu de d\u00e9boguer l&rsquo;int\u00e9gralit\u00e9 de la simulation, vous d\u00e9boguez un terme ou un m\u00e9canisme de couplage.<\/p>\n<h3>\u00c9tape&nbsp;3&nbsp;: Changez une chose \u00e0 la fois<\/h3>\n<p>Le d\u00e9bogage scientifique devient d\u00e9routant lorsque vous modifiez plusieurs variables \u00e0 la fois. Si vous modifiez ensemble le pas de temps, les conditions aux limites, la tol\u00e9rance du solveur et la condition initiale, vous ne pouvez pas savoir quel changement importait.<\/p>\n<p>Modifier un param\u00e8tre. Courez. Comparez. Document.<\/p>\n<p>Utilisez le contr\u00f4le de version pour suivre les modifications. M\u00eame de simples commits tels que \u00ab\u00a0DT chang\u00e9 de 0,01 \u00e0 0,001\u00a0\u00bb peuvent \u00e9conomiser des heures plus tard.<\/p>\n<h3>\u00c9tape&nbsp;4&nbsp;: Utilisez le d\u00e9bogueur<\/h3>\n<p>Lorsque les impressions et les assertions ne suffisent pas, utilisez <code>pdb<\/code>, <code>ipdb<\/code> ou un d\u00e9bogueur IDE. Un d\u00e9bogueur vous permet de suspendre l&rsquo;ex\u00e9cution, d&rsquo;inspecter les variables, de parcourir le code et de v\u00e9rifier pourquoi une valeur a chang\u00e9.<\/p>\n<h3>\u00c9tape&nbsp;5&nbsp;: Ajouter \u00e0 la suite de tests<\/h3>\n<p>Apr\u00e8s avoir corrig\u00e9 un bogue, ajoutez un test qui l&rsquo;aurait attrap\u00e9. Cela emp\u00eache le m\u00eame bogue de revenir plus tard.<\/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>Le flux de travail est simple : isoler, diviser, changer une chose, d\u00e9boguer et tester. Le reste de ce guide explique les outils \u00e0 utiliser \u00e0 chaque niveau.<\/p>\n<h2>Niveau&nbsp;1&nbsp;: instructions d&rsquo;impression et journalisation<\/h2>\n<p><code>print()<\/code> est toujours utile. Il s&rsquo;agit de l&rsquo;outil de d\u00e9bogage le plus simple et attrape de nombreux premiers bogues. La cl\u00e9 est de l&rsquo;utiliser avec la structure.<\/p>\n<h3>Le probl\u00e8me avec les mauvaises impressions<\/h3>\n<p>Une erreur courante de d\u00e9butant est l&rsquo;impression de valeurs sans contexte.<\/p>\n<pre><code class=\"language-python\">for i in range(100):\n    print(phi[i])\n<\/code><\/pre>\n<p>Apr\u00e8s quelques it\u00e9rations, vous avez de nombreux chiffres sans aucune signification claire. Vous ne savez pas quelle variable, pas de temps ou condition produit chaque ligne.<\/p>\n<h3>La bonne mani\u00e8re : des impressions structur\u00e9es<\/h3>\n<p>\u00c9tiquetez tout. Incluez le pas de temps, le nom de la variable, le minimum, le maximum et toute quantit\u00e9 conserv\u00e9e qui vous int\u00e9resse.<\/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>Cette sortie est facile \u00e0 num\u00e9riser :<\/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>Vous pouvez imm\u00e9diatement voir que la valeur maximale a augment\u00e9 au-dessus de 1,0 et que la masse a chang\u00e9. Ce sont des indices utiles.<\/p>\n<h3>Enregistrement vs instructions d&rsquo;impression<\/h3>\n<p>Pour le d\u00e9bogage s\u00e9rieux, utilisez le module <code>logging<\/code> de Python au lieu de 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>La journalisation pr\u00e9sente plusieurs avantages :<\/p>\n<ul>\n<li>niveaux tels que <code>DEBUG<\/code>, <code>WARNING<\/code> et <code>ERROR<\/code>.<\/li>\n<li>Horodatages et sortie de fichiers en option.<\/li>\n<li>Activation ou d\u00e9sactivation facile sans suppression du code de d\u00e9bogage.<\/li>\n<li>Des diagnostics plus nets lors de longues s\u00e9ries de simulation.<\/li>\n<\/ul>\n<p>Une r\u00e8gle pratique consiste \u00e0 utiliser <code>print()<\/code> pour les scripts exploratoires rapides et <code>logging<\/code> pour le code de simulation r\u00e9utilisable ou de type production.<\/p>\n<h2>Niveau 2 : Assertions et v\u00e9rifications de sant\u00e9 mentale<\/h2>\n<p>Les assertions sont comme des d\u00e9clarations d&rsquo;impression qui refusent de garder le silence. Ils soul\u00e8vent une erreur lorsqu&rsquo;une hypoth\u00e8se est viol\u00e9e.<\/p>\n<h3>Le mod\u00e8le&nbsp;: les hypoth\u00e8ses d&rsquo;abord<\/h3>\n<p>Le mod\u00e8le le plus utile consiste \u00e0 v\u00e9rifier les entr\u00e9es, \u00e0 ex\u00e9cuter le solveur et \u00e0 v\u00e9rifier les sorties.<\/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>Cela transforme une d\u00e9faillance num\u00e9rique silencieuse en une erreur claire avec l&#8217;emplacement et le contexte.<\/p>\n<h3>La philosophie d&rsquo;assertion<\/h3>\n<p>Les assertions ne concernent pas seulement les bugs. Ils \u00e9pinglent les bugs.<\/p>\n<p>Une assertion qui \u00e9choue au d\u00e9but d&rsquo;un solveur vous indique o\u00f9 l&rsquo;hypoth\u00e8se s&rsquo;est cass\u00e9e. Une NAN qui appara\u00eet des centaines de lignes plus tard vous en dit beaucoup moins.<\/p>\n<p>Les lieux utiles pour les assertions comprennent :<\/p>\n<ul>\n<li>Avant et apr\u00e8s les appels du solveur.<\/li>\n<li>aux limites des modules o\u00f9 les donn\u00e9es sont \u00e9chang\u00e9es.<\/li>\n<li>Apr\u00e8s des op\u00e9rations de maillage telles que le raffinement, le remaillage ou le grossissement.<\/li>\n<li>Apr\u00e8s avoir appliqu\u00e9 les conditions aux limites.<\/li>\n<\/ul>\n<h3>Un avertissement sur les assertions<\/h3>\n<p>N&rsquo;utilisez pas trop les assertions dans les boucles int\u00e9rieures co\u00fbteuses, sauf si vous en avez besoin. Ils peuvent ajouter des frais g\u00e9n\u00e9raux d&rsquo;ex\u00e9cution et rendre le d\u00e9bogage bruyant.<\/p>\n<p>Utilisez des assertions pour les invariants et les conditions pr\u00e9alables&nbsp;: conditions qui ne doivent jamais \u00eatre viol\u00e9es si le code est correct.<\/p>\n<h2>Niveau 3 : Le d\u00e9bogueur Python<\/h2>\n<p>Lorsque les instructions d&rsquo;impression ne suffisent pas, utilisez un d\u00e9bogueur interactif. Un d\u00e9bogueur vous permet de mettre en pause le programme, d&rsquo;inspecter l&rsquo;\u00e9tat et de parcourir la logique.<\/p>\n<h3>L&rsquo;outil classique : PDB<\/h3>\n<p><code>pdb<\/code> est le d\u00e9bogueur int\u00e9gr\u00e9 de Python. Il est disponible dans chaque installation Python.<\/p>\n<p>Dans IPython, vous pouvez utiliser le d\u00e9bogage post-mortem apr\u00e8s une erreur&nbsp;:<\/p>\n<pre><code class=\"language-python\">In [1]: %run simulation.py\n\nIn [2]: %debug\n<\/code><\/pre>\n<p>Cela vous plonge dans le d\u00e9bogueur au point de d\u00e9faillance.<\/p>\n<p>Pour les scripts de ligne de commande, lancez le script avec le d\u00e9bogueur&nbsp;:<\/p>\n<pre><code class=\"language-bash\">python -m pdb simulation.py\n<\/code><\/pre>\n<p>Les commandes utiles incluent&nbsp;:<\/p>\n<ul>\n<li><code>c<\/code>&nbsp;: Poursuivre l&rsquo;ex\u00e9cution.<\/li>\n<li><code>n<\/code>&nbsp;: Passez \u00e0 la ligne suivante.<\/li>\n<li><code>s<\/code>&nbsp;: entrez dans un appel de fonction.<\/li>\n<li><code>b<\/code>&nbsp;: d\u00e9finissez un point d&rsquo;arr\u00eat.<\/li>\n<li><code>p variable_name<\/code>&nbsp;: Imprimez une variable.<\/li>\n<li><code>q<\/code>&nbsp;: Quittez le d\u00e9bogueur.<\/li>\n<\/ul>\n<p>Vous pouvez \u00e9galement ins\u00e9rer un point d&rsquo;arr\u00eat directement dans le code&nbsp;:<\/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>D\u00e9bogage IPDB et IPython<\/h3>\n<p><code>ipdb<\/code> est une version am\u00e9lior\u00e9e de <code>pdb<\/code>. Il offre une compl\u00e9tion d&rsquo;onglets, un meilleur formatage et une exp\u00e9rience interactive plus confortable.<\/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>Pour les utilisateurs d&rsquo;iPython, <code>%debug<\/code> est souvent l&rsquo;option la plus rapide apr\u00e8s une exception.<\/p>\n<h3>D\u00e9bogueurs IDE<\/h3>\n<p>PyCharm, VS Code et Spyder incluent des d\u00e9bogueurs graphiques. Ils vous permettent de d\u00e9finir des points d&rsquo;arr\u00eat, d&rsquo;inspecter les variables, de parcourir le code et de regarder les expressions sans taper de commandes.<\/p>\n<p>Les d\u00e9bogueurs IDE sont utiles pour le d\u00e9veloppement local. Pour les clusters distants ou les serveurs sans t\u00eate, <code>pdb<\/code>, <code>ipdb<\/code> et la journalisation sont g\u00e9n\u00e9ralement plus pratiques.<\/p>\n<h3>Quand utiliser quel outil<\/h3>\n<table class=\"custom-table\">\n<tbody>\n<tr>\n<th>Sc\u00e9nario<\/th>\n<th>Outil recommand\u00e9<\/th>\n<\/tr>\n<tr>\n<td>Script rapide ou d\u00e9veloppement local pr\u00e9coce<\/td>\n<td><code>print()<\/code> Plus les assertions<\/td>\n<\/tr>\n<tr>\n<td>Retrait d&rsquo;erreur dans IPython<\/td>\n<td><code>%debug<\/code><\/td>\n<\/tr>\n<tr>\n<td>Cluster distant sans acc\u00e8s IDE<\/td>\n<td><code>python -m pdb script.py<\/code><\/td>\n<\/tr>\n<tr>\n<td>D\u00e9bogage interactif avec un meilleur contexte<\/td>\n<td><code>ipdb<\/code> ou d\u00e9bogueur IDE<\/td>\n<\/tr>\n<tr>\n<td>Diagnostics de type production<\/td>\n<td><code>logging<\/code> avec des points d&rsquo;arr\u00eat strat\u00e9giques<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Niveau 4 : Profilage pour trouver des bogues cach\u00e9s<\/h2>\n<p>Le profilage est g\u00e9n\u00e9ralement consid\u00e9r\u00e9 comme un outil de performance, mais il aide \u00e9galement \u00e0 d\u00e9boguer. Le code lent est souvent un code erron\u00e9. Les profileurs peuvent r\u00e9v\u00e9ler des boucles Python accidentelles, des allocations r\u00e9p\u00e9t\u00e9es, des calculs redondants ou des appels de fonctions inattendus.<\/p>\n<h3>cProfile : recherchez les fonctions \u00e0 chaud<\/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>Cela montre les fonctions qui consomment le plus de temps total. Si une fonction d&rsquo;aide domine de mani\u00e8re inattendue l&rsquo;ex\u00e9cution, elle peut contenir un bogue cach\u00e9 ou une logique de tableau inefficace.<\/p>\n<h3>line_profiler&nbsp;: recherchez les lignes chaudes<\/h3>\n<p>Lorsque <code>cProfile<\/code> identifie une fonction lente, <code>line_profiler<\/code> peut montrer quelles lignes \u00e0 l&rsquo;int\u00e9rieur de la fonction consomment du temps.<\/p>\n<pre><code class=\"language-bash\">pip install line_profiler\nkernprof -l -v simulation.py\n<\/code><\/pre>\n<p>Ceci est utile lorsqu&rsquo;une seule ligne \u00e0 l&rsquo;int\u00e9rieur d&rsquo;une boucle cr\u00e9e des tableaux \u00e0 plusieurs reprises ou effectue une op\u00e9ration lente par erreur.<\/p>\n<h3>Profilage de la m\u00e9moire&nbsp;: rechercher des allocations cach\u00e9es<\/h3>\n<p>Le code scientifique masque souvent les bogues de m\u00e9moire tels que les copies accidentelles ou la cr\u00e9ation de tableaux excessifs \u00e0 l&rsquo;int\u00e9rieur des boucles. <code>memory_profiler<\/code> peut aider \u00e0 suivre les allocations.<\/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>Les profils de m\u00e9moire ligne par ligne peuvent r\u00e9v\u00e9ler o\u00f9 de grands tableaux sont cr\u00e9\u00e9s et supprim\u00e9s.<\/p>\n<h3>Profilage comme d\u00e9bogage<\/h3>\n<p>Le profilage aide lorsqu&rsquo;un solveur est \u00e0 la fois faux et lent. Par exemple, une mauvaise tranche peut calculer de l&rsquo;\u00e9nergie sur la mauvaise r\u00e9gion. Un profil peut montrer que le calcul de l&rsquo;\u00e9nergie est \u00e9tonnamment co\u00fbteux, ce qui vous oriente vers le fonctionnement du tableau.<\/p>\n<p>Les assertions confirment le bogue. Le profilage vous aide \u00e0 trouver o\u00f9 chercher.<\/p>\n<h2>Niveau 5 : tests et pr\u00e9vention de la r\u00e9gression<\/h2>\n<p>Le niveau final de d\u00e9bogage est la pr\u00e9vention. Apr\u00e8s avoir corrig\u00e9 un bogue, \u00e9crivez un test qui reproduit l&rsquo;\u00e9chec et prouve que le correctif fonctionne.<\/p>\n<h3>Le mod\u00e8le de test scientifique<\/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>Ce test v\u00e9rifie le comportement physique, pas seulement la syntaxe. Une simulation correcte ne doit pas simplement \u00eatre ex\u00e9cut\u00e9e. Il doit conserver les quantit\u00e9s attendues, respecter les limites et converger avec le raffinement.<\/p>\n<h3>La pyramide de test pour le code scientifique<\/h3>\n<ul>\n<li>Les tests unitaires sont rapides et v\u00e9rifient les fonctions individuelles telles que <code>compute_gradient()<\/code> ou <code>apply_boundary_conditions()<\/code>.<\/li>\n<li>Les tests d&rsquo;int\u00e9gration effectuent des v\u00e9rifications au niveau du solveur par rapport aux solutions analytiques ou fabriqu\u00e9es.<\/li>\n<li>Les tests de r\u00e9gression comparent les simulations compl\u00e8tes avec les sorties de r\u00e9f\u00e9rence de confiance.<\/li>\n<\/ul>\n<p>Le principe cl\u00e9 est simple : tester la physique, pas seulement le chemin du code.<\/p>\n<h2>Les pi\u00e8ges courants du d\u00e9bogage scientifique<\/h2>\n<h3>Nan et l&rsquo;infini<\/h3>\n<p>Les valeurs NAN et Infinity sont courantes dans le code scientifique. Ils apparaissent souvent apr\u00e8s une division par des valeurs proches de z\u00e9ro, <code>log(0)<\/code>, <code>sqrt<\/code> de valeurs n\u00e9gatives ou d\u00e9bordement d&rsquo;exponentielles.<\/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>V\u00e9rifiez les valeurs finies apr\u00e8s des op\u00e9rations num\u00e9riques :<\/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>Incompatibilit\u00e9s de forme de tableau<\/h3>\n<p>Les erreurs de forme de tableau sont courantes lors du passage entre des tableaux de solveurs aplatis et des champs 2D ou 3D.<\/p>\n<pre><code class=\"language-python\">print(f\"phi shape: {phi.shape}\")\nprint(f\"expected shape: {expected_shape}\")\n<\/code><\/pre>\n<p>Les causes courantes comprennent la mise \u00e0 plat accidentelle, l&rsquo;indexation qui supprime une dimension et les variables de biblioth\u00e8que qui stockent des donn\u00e9es dans une forme diff\u00e9rente de celle attendue.<\/p>\n<h3>Pr\u00e9cision \u00e0 virgule flottante<\/h3>\n<p>Ne comparez pas les tableaux \u00e0 virgule flottante avec une \u00e9galit\u00e9 exacte \u00e0 moins que l&rsquo;\u00e9galit\u00e9 exacte ne soit vraiment attendue. Utilisez des comparaisons bas\u00e9es sur les tol\u00e9rances.<\/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>Ceci est particuli\u00e8rement important pour les solveurs it\u00e9ratifs, la pr\u00e9cision mixte et les int\u00e9grations \u00e0 long terme.<\/p>\n<h3>Bugs de condition des limites<\/h3>\n<p>Les bogues des conditions aux limites sont courants car le code s&rsquo;ex\u00e9cute souvent m\u00eame lorsque les mauvaises cellules ou faces sont contraintes.<\/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>V\u00e9rifiez toujours que les valeurs limites ne sont appliqu\u00e9es que lorsque cela est pr\u00e9vu et que les valeurs int\u00e9rieures ne sont pas \u00e9cras\u00e9es.<\/p>\n<h3>Convergence et stabilit\u00e9<\/h3>\n<p>Les simulations peuvent diverger silencieusement lorsque les pas de temps sont trop importants ou que les tol\u00e9rances des solveurs sont trop l\u00e2ches. Surveiller les quantit\u00e9s conserv\u00e9es ou d\u00e9limit\u00e9es au fil du temps.<\/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>Suivre l&rsquo;\u00e9nergie, la masse, la quantit\u00e9 de mouvement, les limites, les r\u00e9sidus et le comportement de pas de temps. Tracez-les pendant le d\u00e9bogage.<\/p>\n<h2>L&rsquo;\u00e9chelle de d\u00e9bogage d&rsquo;escalade<\/h2>\n<p>Utilisez le niveau de d\u00e9bogage le plus bas qui donne suffisamment d&rsquo;informations.<\/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>Monter en cas de besoin&nbsp;:<\/p>\n<ul>\n<li>Si <code>print()<\/code> donne suffisamment de contexte, restez au niveau 1.<\/li>\n<li>Si vous devez v\u00e9rifier les invariants, utilisez des assertions.<\/li>\n<li>Si vous devez inspecter l&rsquo;\u00e9tat \u00e0 une ligne sp\u00e9cifique, utilisez <code>pdb<\/code> ou <code>ipdb<\/code>.<\/li>\n<li>Si le code est erron\u00e9 et lent, profilez-le.<\/li>\n<li>Si vous souhaitez emp\u00eacher le retour du bogue, r\u00e9digez un test.<\/li>\n<\/ul>\n<h2>Un exemple de d\u00e9bogage pratique&nbsp;: correction d&rsquo;une simulation Fipy<\/h2>\n<p>Supposons qu&rsquo;une simulation de diffusion FIPY produit des valeurs de concentration non limitatives au lieu d&rsquo;une diffusion lisse. Commencez par \u00e9crire une version minimale et claire du probl\u00e8me.<\/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>Puis d\u00e9boguer syst\u00e9matiquement.<\/p>\n<h3>\u00c9tape&nbsp;1&nbsp;: faites-le \u00e9chouer de mani\u00e8re fiable<\/h3>\n<p>Enregistrer le minimum, le maximum, la masse et l&rsquo;\u00e9nergie avant et apr\u00e8s la course.<\/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>\u00c9tape&nbsp;2&nbsp;: Diviser et conqu\u00e9rir<\/h3>\n<p>Testez le terme de diffusion seul. Supprimez les termes de r\u00e9action, les termes d&rsquo;advection, les sources non lin\u00e9aires et les conditions aux limites complexes jusqu&rsquo;\u00e0 ce que la d\u00e9faillance disparaisse ou soit isol\u00e9e.<\/p>\n<h3>\u00c9tape&nbsp;3&nbsp;: Changez une chose<\/h3>\n<p>Essayez un pas de temps plus petit, puis comparez les r\u00e9sultats.<\/p>\n<pre><code class=\"language-python\">dt = 0.001\n<\/code><\/pre>\n<p>Si le bogue persiste avec un pas de temps plus petit, le probl\u00e8me peut \u00eatre spatial, structurel ou li\u00e9 aux conditions aux limites plut\u00f4t qu&rsquo;\u00e0 la stabilit\u00e9 du temps.<\/p>\n<h3>\u00c9tape&nbsp;4&nbsp;: Utilisez le d\u00e9bogueur<\/h3>\n<p>D\u00e9posez dans un d\u00e9bogueur pr\u00e8s de l&rsquo;\u00e9tape d\u00e9faillante et inspectez les valeurs, la forme de maillage, les masques et les conditions aux limites.<\/p>\n<pre><code class=\"language-python\">import pdb\npdb.set_trace()\n<\/code><\/pre>\n<h3>\u00c9tape&nbsp;5&nbsp;: Ajoutez un test<\/h3>\n<p>Apr\u00e8s avoir corrig\u00e9 le bogue, ajoutez un test de r\u00e9gression.<\/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>R\u00e9sum\u00e9 et \u00e9tapes suivantes<\/h2>\n<p>Le d\u00e9bogage du code Python scientifique est une comp\u00e9tence progressive. Commencez par les instructions d&rsquo;impression, passez aux assertions, utilisez les d\u00e9bogueurs lorsque l&rsquo;inspection d&rsquo;\u00e9tat est n\u00e9cessaire, profilez-le lorsque le code est lent ou suspect, et \u00e9crivez des tests pour \u00e9viter les r\u00e9gressions.<\/p>\n<p>Le principe le plus important est d&rsquo;isoler le bogue avant de le corriger. Un bogue que vous pouvez reproduire dans un petit cas de test est beaucoup plus facile \u00e0 r\u00e9soudre. Un bug qui n&rsquo;appara\u00eet qu&rsquo;\u00e0 l&rsquo;int\u00e9rieur d&rsquo;une grande simulation reste un myst\u00e8re.<\/p>\n<p>Pour le code scientifique, n&rsquo;oubliez pas ces r\u00e8gles :<\/p>\n<ul>\n<li>V\u00e9rifier la NAN et l&rsquo;infini apr\u00e8s des op\u00e9rations num\u00e9riques.<\/li>\n<li>Imprimez des formes de tableaux lorsque l&rsquo;indexation devient complexe.<\/li>\n<li>Surveillez les quantit\u00e9s conserv\u00e9es telles que la masse, l&rsquo;\u00e9nergie et la quantit\u00e9 de mouvement.<\/li>\n<li>Testez les conditions aux limites s\u00e9par\u00e9es de la logique int\u00e9rieure.<\/li>\n<li>Utilisez des assertions comme premi\u00e8re ligne de d\u00e9fense contre une d\u00e9faillance num\u00e9rique silencieuse.<\/li>\n<\/ul>\n<h3>Prochaines \u00e9tapes<\/h3>\n<ul>\n<li>Commencez \u00e0 utiliser l&rsquo;\u00e9chelle de d\u00e9bogage d&rsquo;escalade&nbsp;: commencez par <code>print()<\/code> et montez uniquement en cas de besoin.<\/li>\n<li>Ajoutez des assertions avant et apr\u00e8s les fonctions du solveur.<\/li>\n<li>Configurez la journalisation pour les diagnostics de simulation.<\/li>\n<li>\u00c9crivez au moins un cas de test qui reproduit un bug commun.<\/li>\n<\/ul>\n<p>Le d\u00e9bogage n&rsquo;est pas un signe de faiblesse. Cela fait partie de la r\u00e9daction de code qui compte. Les comp\u00e9tences que vous d\u00e9veloppez lors du d\u00e9bogage du code Python scientifique vous aideront dans tous les types de projets de logiciels num\u00e9riques et de recherche.<\/p>\n<h2>Guides connexes<\/h2>\n<ul>\n<li><a href=\"https:\/\/matforge.org\/performance-profiling-optimization-python-pde-solvers\/\">Profilage et optimisation des performances pour les solveurs PDE Python&nbsp;: un guide pratique<\/a> \u2014 outils de profilage, Numba et optimisation de la m\u00e9moire.<\/li>\n<li><a href=\"https:\/\/matforge.org\/unit-testing-scientific-code-pytest-strategies-research-projects\/\">Tests unitaires pour le code scientifique&nbsp;: strat\u00e9gies PyTest<\/a> \u2014 Pr\u00e9cision num\u00e9rique et mod\u00e8les de test.<\/li>\n<li><a href=\"https:\/\/matforge.org\/continuous-integration-research-software-automated-testing-validation\/\">Int\u00e9gration continue pour les logiciels de recherche&nbsp;: tests et validation automatis\u00e9s<\/a> \u2014 Pipelines CI et tests automatis\u00e9s.<\/li>\n<li><a href=\"https:\/\/matforge.org\/best-practices-for-maintaining-scientific-code\/\">Meilleures pratiques pour maintenir le code scientifique<\/a> \u2014 Structure du projet, contr\u00f4le des versions et documentation.<\/li>\n<li><a href=\"https:\/\/matforge.org\/using-fipy-for-phase-field-modeling\/\">Utilisation de Fipy pour la mod\u00e9lisation de champ de phase<\/a> \u2014 mod\u00e8les de mise en \u0153uvre FIPY.<\/li>\n<\/ul>\n<h2>Besoin d&rsquo;aide pour d\u00e9boguer votre code de simulation&nbsp;?<\/h2>\n<p>Le d\u00e9bogage de simulations scientifiques complexes peut prendre du temps, surtout lorsque vous ne savez pas si le bogue est dans les param\u00e8tres de code, de maillage, de limites ou de solveur.<\/p>\n<p>Notre \u00e9quipe peut vous aider avec les workflows de d\u00e9bogage syst\u00e9matiques, les cas de test num\u00e9riques, les contr\u00f4les de sant\u00e9 mentale pour les lois de conservation et les pipelines CI avec des tests de r\u00e9gression.<\/p>\n","protected":false,"raw":"<h2>Points \u00e0 retenir cl\u00e9s<\/h2>\n<ul>\n<li>Le d\u00e9bogage du code Python scientifique suit une \u00e9chelle d'escalade&nbsp;: <code>print<\/code>, <code>assert<\/code>, <code>pdb<\/code>, profilage et tests.<\/li>\n<li>La plupart des bogues scientifiques sont num\u00e9riques, tels que les valeurs de NAN, les incompatibilit\u00e9s de forme de tableau, la perte de pr\u00e9cision ou les pas de temps instables.<\/li>\n<li><code>print()<\/code> attrape de nombreux bogues pour d\u00e9butants, mais les assertions et les d\u00e9bogueurs facilitent l'isolement des \u00e9checs.<\/li>\n<li>Un workflow de d\u00e9bogage scientifique fiable est&nbsp;: faites-le \u00e9chouer de mani\u00e8re fiable, isolez le probl\u00e8me, modifiez une chose, v\u00e9rifiez et ajoutez un test.<\/li>\n<li>Le profilage peut r\u00e9v\u00e9ler des bogues cach\u00e9s car le code lent est souvent le signe d'une logique de tableau, de boucles accidentelles ou d'une allocation excessive.<\/li>\n<\/ul>\n<p>Le d\u00e9bogage du code Python scientifique est l'une des comp\u00e9tences les plus frustrantes \u00e0 d\u00e9velopper. Vous \u00e9crivez ce qui devrait \u00eatre une simple simulation de diffusion de la chaleur, le code s'ex\u00e9cute sans erreur et la sortie est compl\u00e8tement erron\u00e9e.<\/p>\n<p>Pas de crash. Pas de trace. Des chiffres juste en silence.<\/p>\n<p>C'est le d\u00e9fi unique du d\u00e9bogage scientifique. Les insectes les plus dangereux sont souvent ceux qui ne plantent pas du tout. Un nombre incorrect peut se propager \u00e0 travers des milliers d'it\u00e9rations et produire un r\u00e9sultat qui semble physiquement plausible jusqu'\u00e0 ce que vous v\u00e9rifiiez la conservation, la stabilit\u00e9 ou la convergence.<\/p>\n<p>Ce guide explique comment d\u00e9boguer le code Python scientifique \u00e0 plusieurs niveaux : d\u00e9clarations d'impression, journalisation, assertions, d\u00e9bogage interactif, profilage et r\u00e9gression.<\/p>\n<p>Que vous ex\u00e9cutiez une premi\u00e8re simulation FIPY ou d\u00e9boguez un pipeline de recherche de production, ce flux de travail vous donne une structure pratique \u00e0 suivre.<\/p>\n<h2>Qu'y a-t-il de diff\u00e9rent dans le d\u00e9bogage du code scientifique&nbsp;?<\/h2>\n<p>Le d\u00e9bogage scientifique est diff\u00e9rent du d\u00e9bogage d'une application Web, d'une API ou d'un pipeline de donn\u00e9es ordinaire.<\/p>\n<p>Les chiffres peuvent mentir. Une application Web \u00e9choue souvent de mani\u00e8re visible&nbsp;: un bouton ne fonctionne pas, une API renvoie une erreur ou une requ\u00eate de base de donn\u00e9es \u00e9choue. Le code scientifique peut fonctionner parfaitement et produire des r\u00e9sultats erron\u00e9s. Une concentration n\u00e9gative, un champ d'\u00e9nergie explosif ou une NAN cach\u00e9e ne peut appara\u00eetre que si vous le v\u00e9rifiez directement.<\/p>\n<p>L'exactitude est d\u00e9finie par la physique, pas seulement par la syntaxe. Un programme Python qui s'ex\u00e9cute est syntaxiquement valide. Un programme scientifique n'est cr\u00e9dible que s'il correspond \u00e0 des solutions connues, conserve des quantit\u00e9s attendues, respecte les conditions aux limites et converge vers le raffinement.<\/p>\n<p>Les traces d'empilage pointent souvent vers des internes NumPy, Scipy ou Fipy. Le vrai bogue peut \u00eatre dans votre configuration, mais le message d'erreur peut appara\u00eetre au plus profond d'une biblioth\u00e8que. C'est pourquoi le d\u00e9bogage scientifique a besoin d'un processus structur\u00e9, pas seulement de la lecture des traces.<\/p>\n<h2>Le flux de travail de d\u00e9bogage : un mod\u00e8le mental en cinq \u00e9tapes<\/h2>\n<p>Un flux de travail de d\u00e9bogage utile pour le code scientifique suit cinq \u00e9tapes.<\/p>\n<h3>\u00c9tape&nbsp;1&nbsp;: faites-le \u00e9chouer de mani\u00e8re fiable<\/h3>\n<p>Votre premier travail consiste \u00e0 cr\u00e9er un cas de test qui \u00e9choue \u00e0 chaque fois. Dans le code scientifique, cela signifie g\u00e9n\u00e9ralement construire un exemple reproductible minimal qui produit le mauvais r\u00e9sultat.<\/p>\n<p>Vous n'avez pas besoin de la simulation compl\u00e8te. Vous avez besoin du plus petit morceau de code qui d\u00e9montre le bogue.<\/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>En isolant l'\u00e9tape de mise \u00e0 jour, vous cr\u00e9ez un cas reproductible. C'est la base de chaque \u00e9tape de d\u00e9bogage ult\u00e9rieure.<\/p>\n<h3>\u00c9tape&nbsp;2&nbsp;: Diviser et conqu\u00e9rir<\/h3>\n<p>Une fois que vous avez un cas d\u00e9faillant, isolez-le davantage. Trouvez le module, la fonction, la ligne ou le terme physique responsable de la d\u00e9faillance.<\/p>\n<p>Pour le code de simulation, une tactique utile consiste \u00e0 tester s\u00e9par\u00e9ment chaque terme de physique. Si votre mod\u00e8le combine la diffusion, la r\u00e9action et l'advection, testez chacun seul avant de d\u00e9boguer le syst\u00e8me coupl\u00e9.<\/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>Cela r\u00e9duit l'espace de recherche. Au lieu de d\u00e9boguer l'int\u00e9gralit\u00e9 de la simulation, vous d\u00e9boguez un terme ou un m\u00e9canisme de couplage.<\/p>\n<h3>\u00c9tape&nbsp;3&nbsp;: Changez une chose \u00e0 la fois<\/h3>\n<p>Le d\u00e9bogage scientifique devient d\u00e9routant lorsque vous modifiez plusieurs variables \u00e0 la fois. Si vous modifiez ensemble le pas de temps, les conditions aux limites, la tol\u00e9rance du solveur et la condition initiale, vous ne pouvez pas savoir quel changement importait.<\/p>\n<p>Modifier un param\u00e8tre. Courez. Comparez. Document.<\/p>\n<p>Utilisez le contr\u00f4le de version pour suivre les modifications. M\u00eame de simples commits tels que \"DT chang\u00e9 de 0,01 \u00e0 0,001\" peuvent \u00e9conomiser des heures plus tard.<\/p>\n<h3>\u00c9tape&nbsp;4&nbsp;: Utilisez le d\u00e9bogueur<\/h3>\n<p>Lorsque les impressions et les assertions ne suffisent pas, utilisez <code>pdb<\/code>, <code>ipdb<\/code> ou un d\u00e9bogueur IDE. Un d\u00e9bogueur vous permet de suspendre l'ex\u00e9cution, d'inspecter les variables, de parcourir le code et de v\u00e9rifier pourquoi une valeur a chang\u00e9.<\/p>\n<h3>\u00c9tape&nbsp;5&nbsp;: Ajouter \u00e0 la suite de tests<\/h3>\n<p>Apr\u00e8s avoir corrig\u00e9 un bogue, ajoutez un test qui l'aurait attrap\u00e9. Cela emp\u00eache le m\u00eame bogue de revenir plus tard.<\/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>Le flux de travail est simple : isoler, diviser, changer une chose, d\u00e9boguer et tester. Le reste de ce guide explique les outils \u00e0 utiliser \u00e0 chaque niveau.<\/p>\n<h2>Niveau&nbsp;1&nbsp;: instructions d'impression et journalisation<\/h2>\n<p><code>print()<\/code> est toujours utile. Il s'agit de l'outil de d\u00e9bogage le plus simple et attrape de nombreux premiers bogues. La cl\u00e9 est de l'utiliser avec la structure.<\/p>\n<h3>Le probl\u00e8me avec les mauvaises impressions<\/h3>\n<p>Une erreur courante de d\u00e9butant est l'impression de valeurs sans contexte.<\/p>\n<pre><code class=\"language-python\">for i in range(100):\n    print(phi[i])\n<\/code><\/pre>\n<p>Apr\u00e8s quelques it\u00e9rations, vous avez de nombreux chiffres sans aucune signification claire. Vous ne savez pas quelle variable, pas de temps ou condition produit chaque ligne.<\/p>\n<h3>La bonne mani\u00e8re : des impressions structur\u00e9es<\/h3>\n<p>\u00c9tiquetez tout. Incluez le pas de temps, le nom de la variable, le minimum, le maximum et toute quantit\u00e9 conserv\u00e9e qui vous int\u00e9resse.<\/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>Cette sortie est facile \u00e0 num\u00e9riser :<\/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>Vous pouvez imm\u00e9diatement voir que la valeur maximale a augment\u00e9 au-dessus de 1,0 et que la masse a chang\u00e9. Ce sont des indices utiles.<\/p>\n<h3>Enregistrement vs instructions d'impression<\/h3>\n<p>Pour le d\u00e9bogage s\u00e9rieux, utilisez le module <code>logging<\/code> de Python au lieu de 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>La journalisation pr\u00e9sente plusieurs avantages :<\/p>\n<ul>\n<li>niveaux tels que <code>DEBUG<\/code>, <code>WARNING<\/code> et <code>ERROR<\/code>.<\/li>\n<li>Horodatages et sortie de fichiers en option.<\/li>\n<li>Activation ou d\u00e9sactivation facile sans suppression du code de d\u00e9bogage.<\/li>\n<li>Des diagnostics plus nets lors de longues s\u00e9ries de simulation.<\/li>\n<\/ul>\n<p>Une r\u00e8gle pratique consiste \u00e0 utiliser <code>print()<\/code> pour les scripts exploratoires rapides et <code>logging<\/code> pour le code de simulation r\u00e9utilisable ou de type production.<\/p>\n<h2>Niveau 2 : Assertions et v\u00e9rifications de sant\u00e9 mentale<\/h2>\n<p>Les assertions sont comme des d\u00e9clarations d'impression qui refusent de garder le silence. Ils soul\u00e8vent une erreur lorsqu'une hypoth\u00e8se est viol\u00e9e.<\/p>\n<h3>Le mod\u00e8le&nbsp;: les hypoth\u00e8ses d'abord<\/h3>\n<p>Le mod\u00e8le le plus utile consiste \u00e0 v\u00e9rifier les entr\u00e9es, \u00e0 ex\u00e9cuter le solveur et \u00e0 v\u00e9rifier les sorties.<\/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>Cela transforme une d\u00e9faillance num\u00e9rique silencieuse en une erreur claire avec l'emplacement et le contexte.<\/p>\n<h3>La philosophie d'assertion<\/h3>\n<p>Les assertions ne concernent pas seulement les bugs. Ils \u00e9pinglent les bugs.<\/p>\n<p>Une assertion qui \u00e9choue au d\u00e9but d'un solveur vous indique o\u00f9 l'hypoth\u00e8se s'est cass\u00e9e. Une NAN qui appara\u00eet des centaines de lignes plus tard vous en dit beaucoup moins.<\/p>\n<p>Les lieux utiles pour les assertions comprennent :<\/p>\n<ul>\n<li>Avant et apr\u00e8s les appels du solveur.<\/li>\n<li>aux limites des modules o\u00f9 les donn\u00e9es sont \u00e9chang\u00e9es.<\/li>\n<li>Apr\u00e8s des op\u00e9rations de maillage telles que le raffinement, le remaillage ou le grossissement.<\/li>\n<li>Apr\u00e8s avoir appliqu\u00e9 les conditions aux limites.<\/li>\n<\/ul>\n<h3>Un avertissement sur les assertions<\/h3>\n<p>N'utilisez pas trop les assertions dans les boucles int\u00e9rieures co\u00fbteuses, sauf si vous en avez besoin. Ils peuvent ajouter des frais g\u00e9n\u00e9raux d'ex\u00e9cution et rendre le d\u00e9bogage bruyant.<\/p>\n<p>Utilisez des assertions pour les invariants et les conditions pr\u00e9alables&nbsp;: conditions qui ne doivent jamais \u00eatre viol\u00e9es si le code est correct.<\/p>\n<h2>Niveau 3 : Le d\u00e9bogueur Python<\/h2>\n<p>Lorsque les instructions d'impression ne suffisent pas, utilisez un d\u00e9bogueur interactif. Un d\u00e9bogueur vous permet de mettre en pause le programme, d'inspecter l'\u00e9tat et de parcourir la logique.<\/p>\n<h3>L'outil classique : PDB<\/h3>\n<p><code>pdb<\/code> est le d\u00e9bogueur int\u00e9gr\u00e9 de Python. Il est disponible dans chaque installation Python.<\/p>\n<p>Dans IPython, vous pouvez utiliser le d\u00e9bogage post-mortem apr\u00e8s une erreur&nbsp;:<\/p>\n<pre><code class=\"language-python\">In [1]: %run simulation.py\n\nIn [2]: %debug\n<\/code><\/pre>\n<p>Cela vous plonge dans le d\u00e9bogueur au point de d\u00e9faillance.<\/p>\n<p>Pour les scripts de ligne de commande, lancez le script avec le d\u00e9bogueur&nbsp;:<\/p>\n<pre><code class=\"language-bash\">python -m pdb simulation.py\n<\/code><\/pre>\n<p>Les commandes utiles incluent&nbsp;:<\/p>\n<ul>\n<li><code>c<\/code>&nbsp;: Poursuivre l'ex\u00e9cution.<\/li>\n<li><code>n<\/code>&nbsp;: Passez \u00e0 la ligne suivante.<\/li>\n<li><code>s<\/code>&nbsp;: entrez dans un appel de fonction.<\/li>\n<li><code>b<\/code>&nbsp;: d\u00e9finissez un point d'arr\u00eat.<\/li>\n<li><code>p variable_name<\/code>&nbsp;: Imprimez une variable.<\/li>\n<li><code>q<\/code>&nbsp;: Quittez le d\u00e9bogueur.<\/li>\n<\/ul>\n<p>Vous pouvez \u00e9galement ins\u00e9rer un point d'arr\u00eat directement dans le code&nbsp;:<\/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>D\u00e9bogage IPDB et IPython<\/h3>\n<p><code>ipdb<\/code> est une version am\u00e9lior\u00e9e de <code>pdb<\/code>. Il offre une compl\u00e9tion d'onglets, un meilleur formatage et une exp\u00e9rience interactive plus confortable.<\/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>Pour les utilisateurs d'iPython, <code>%debug<\/code> est souvent l'option la plus rapide apr\u00e8s une exception.<\/p>\n<h3>D\u00e9bogueurs IDE<\/h3>\n<p>PyCharm, VS Code et Spyder incluent des d\u00e9bogueurs graphiques. Ils vous permettent de d\u00e9finir des points d'arr\u00eat, d'inspecter les variables, de parcourir le code et de regarder les expressions sans taper de commandes.<\/p>\n<p>Les d\u00e9bogueurs IDE sont utiles pour le d\u00e9veloppement local. Pour les clusters distants ou les serveurs sans t\u00eate, <code>pdb<\/code>, <code>ipdb<\/code> et la journalisation sont g\u00e9n\u00e9ralement plus pratiques.<\/p>\n<h3>Quand utiliser quel outil<\/h3>\n<table class=\"custom-table\">\n<tbody><tr>\n<th>Sc\u00e9nario<\/th>\n<th>Outil recommand\u00e9<\/th>\n<\/tr>\n<tr>\n<td>Script rapide ou d\u00e9veloppement local pr\u00e9coce<\/td>\n<td><code>print()<\/code> Plus les assertions<\/td>\n<\/tr>\n<tr>\n<td>Retrait d'erreur dans IPython<\/td>\n<td><code>%debug<\/code><\/td>\n<\/tr>\n<tr>\n<td>Cluster distant sans acc\u00e8s IDE<\/td>\n<td><code>python -m pdb script.py<\/code><\/td>\n<\/tr>\n<tr>\n<td>D\u00e9bogage interactif avec un meilleur contexte<\/td>\n<td><code>ipdb<\/code> ou d\u00e9bogueur IDE<\/td>\n<\/tr>\n<tr>\n<td>Diagnostics de type production<\/td>\n<td><code>logging<\/code> avec des points d'arr\u00eat strat\u00e9giques<\/td>\n<\/tr>\n<\/tbody><\/table>\n<h2>Niveau 4 : Profilage pour trouver des bogues cach\u00e9s<\/h2>\n<p>Le profilage est g\u00e9n\u00e9ralement consid\u00e9r\u00e9 comme un outil de performance, mais il aide \u00e9galement \u00e0 d\u00e9boguer. Le code lent est souvent un code erron\u00e9. Les profileurs peuvent r\u00e9v\u00e9ler des boucles Python accidentelles, des allocations r\u00e9p\u00e9t\u00e9es, des calculs redondants ou des appels de fonctions inattendus.<\/p>\n<h3>cProfile : recherchez les fonctions \u00e0 chaud<\/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>Cela montre les fonctions qui consomment le plus de temps total. Si une fonction d'aide domine de mani\u00e8re inattendue l'ex\u00e9cution, elle peut contenir un bogue cach\u00e9 ou une logique de tableau inefficace.<\/p>\n<h3>line_profiler&nbsp;: recherchez les lignes chaudes<\/h3>\n<p>Lorsque <code>cProfile<\/code> identifie une fonction lente, <code>line_profiler<\/code> peut montrer quelles lignes \u00e0 l'int\u00e9rieur de la fonction consomment du temps.<\/p>\n<pre><code class=\"language-bash\">pip install line_profiler\nkernprof -l -v simulation.py\n<\/code><\/pre>\n<p>Ceci est utile lorsqu'une seule ligne \u00e0 l'int\u00e9rieur d'une boucle cr\u00e9e des tableaux \u00e0 plusieurs reprises ou effectue une op\u00e9ration lente par erreur.<\/p>\n<h3>Profilage de la m\u00e9moire&nbsp;: rechercher des allocations cach\u00e9es<\/h3>\n<p>Le code scientifique masque souvent les bogues de m\u00e9moire tels que les copies accidentelles ou la cr\u00e9ation de tableaux excessifs \u00e0 l'int\u00e9rieur des boucles. <code>memory_profiler<\/code> peut aider \u00e0 suivre les allocations.<\/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>Les profils de m\u00e9moire ligne par ligne peuvent r\u00e9v\u00e9ler o\u00f9 de grands tableaux sont cr\u00e9\u00e9s et supprim\u00e9s.<\/p>\n<h3>Profilage comme d\u00e9bogage<\/h3>\n<p>Le profilage aide lorsqu'un solveur est \u00e0 la fois faux et lent. Par exemple, une mauvaise tranche peut calculer de l'\u00e9nergie sur la mauvaise r\u00e9gion. Un profil peut montrer que le calcul de l'\u00e9nergie est \u00e9tonnamment co\u00fbteux, ce qui vous oriente vers le fonctionnement du tableau.<\/p>\n<p>Les assertions confirment le bogue. Le profilage vous aide \u00e0 trouver o\u00f9 chercher.<\/p>\n<h2>Niveau 5 : tests et pr\u00e9vention de la r\u00e9gression<\/h2>\n<p>Le niveau final de d\u00e9bogage est la pr\u00e9vention. Apr\u00e8s avoir corrig\u00e9 un bogue, \u00e9crivez un test qui reproduit l'\u00e9chec et prouve que le correctif fonctionne.<\/p>\n<h3>Le mod\u00e8le de test scientifique<\/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>Ce test v\u00e9rifie le comportement physique, pas seulement la syntaxe. Une simulation correcte ne doit pas simplement \u00eatre ex\u00e9cut\u00e9e. Il doit conserver les quantit\u00e9s attendues, respecter les limites et converger avec le raffinement.<\/p>\n<h3>La pyramide de test pour le code scientifique<\/h3>\n<ul>\n<li>Les tests unitaires sont rapides et v\u00e9rifient les fonctions individuelles telles que <code>compute_gradient()<\/code> ou <code>apply_boundary_conditions()<\/code>.<\/li>\n<li>Les tests d'int\u00e9gration effectuent des v\u00e9rifications au niveau du solveur par rapport aux solutions analytiques ou fabriqu\u00e9es.<\/li>\n<li>Les tests de r\u00e9gression comparent les simulations compl\u00e8tes avec les sorties de r\u00e9f\u00e9rence de confiance.<\/li>\n<\/ul>\n<p>Le principe cl\u00e9 est simple : tester la physique, pas seulement le chemin du code.<\/p>\n<h2>Les pi\u00e8ges courants du d\u00e9bogage scientifique<\/h2>\n<h3>Nan et l'infini<\/h3>\n<p>Les valeurs NAN et Infinity sont courantes dans le code scientifique. Ils apparaissent souvent apr\u00e8s une division par des valeurs proches de z\u00e9ro, <code>log(0)<\/code>, <code>sqrt<\/code> de valeurs n\u00e9gatives ou d\u00e9bordement d'exponentielles.<\/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>V\u00e9rifiez les valeurs finies apr\u00e8s des op\u00e9rations num\u00e9riques :<\/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>Incompatibilit\u00e9s de forme de tableau<\/h3>\n<p>Les erreurs de forme de tableau sont courantes lors du passage entre des tableaux de solveurs aplatis et des champs 2D ou 3D.<\/p>\n<pre><code class=\"language-python\">print(f\"phi shape: {phi.shape}\")\nprint(f\"expected shape: {expected_shape}\")\n<\/code><\/pre>\n<p>Les causes courantes comprennent la mise \u00e0 plat accidentelle, l'indexation qui supprime une dimension et les variables de biblioth\u00e8que qui stockent des donn\u00e9es dans une forme diff\u00e9rente de celle attendue.<\/p>\n<h3>Pr\u00e9cision \u00e0 virgule flottante<\/h3>\n<p>Ne comparez pas les tableaux \u00e0 virgule flottante avec une \u00e9galit\u00e9 exacte \u00e0 moins que l'\u00e9galit\u00e9 exacte ne soit vraiment attendue. Utilisez des comparaisons bas\u00e9es sur les tol\u00e9rances.<\/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>Ceci est particuli\u00e8rement important pour les solveurs it\u00e9ratifs, la pr\u00e9cision mixte et les int\u00e9grations \u00e0 long terme.<\/p>\n<h3>Bugs de condition des limites<\/h3>\n<p>Les bogues des conditions aux limites sont courants car le code s'ex\u00e9cute souvent m\u00eame lorsque les mauvaises cellules ou faces sont contraintes.<\/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>V\u00e9rifiez toujours que les valeurs limites ne sont appliqu\u00e9es que lorsque cela est pr\u00e9vu et que les valeurs int\u00e9rieures ne sont pas \u00e9cras\u00e9es.<\/p>\n<h3>Convergence et stabilit\u00e9<\/h3>\n<p>Les simulations peuvent diverger silencieusement lorsque les pas de temps sont trop importants ou que les tol\u00e9rances des solveurs sont trop l\u00e2ches. Surveiller les quantit\u00e9s conserv\u00e9es ou d\u00e9limit\u00e9es au fil du temps.<\/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>Suivre l'\u00e9nergie, la masse, la quantit\u00e9 de mouvement, les limites, les r\u00e9sidus et le comportement de pas de temps. Tracez-les pendant le d\u00e9bogage.<\/p>\n<h2>L'\u00e9chelle de d\u00e9bogage d'escalade<\/h2>\n<p>Utilisez le niveau de d\u00e9bogage le plus bas qui donne suffisamment d'informations.<\/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>Monter en cas de besoin&nbsp;:<\/p>\n<ul>\n<li>Si <code>print()<\/code> donne suffisamment de contexte, restez au niveau 1.<\/li>\n<li>Si vous devez v\u00e9rifier les invariants, utilisez des assertions.<\/li>\n<li>Si vous devez inspecter l'\u00e9tat \u00e0 une ligne sp\u00e9cifique, utilisez <code>pdb<\/code> ou <code>ipdb<\/code>.<\/li>\n<li>Si le code est erron\u00e9 et lent, profilez-le.<\/li>\n<li>Si vous souhaitez emp\u00eacher le retour du bogue, r\u00e9digez un test.<\/li>\n<\/ul>\n<h2>Un exemple de d\u00e9bogage pratique&nbsp;: correction d'une simulation Fipy<\/h2>\n<p>Supposons qu'une simulation de diffusion FIPY produit des valeurs de concentration non limitatives au lieu d'une diffusion lisse. Commencez par \u00e9crire une version minimale et claire du probl\u00e8me.<\/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>Puis d\u00e9boguer syst\u00e9matiquement.<\/p>\n<h3>\u00c9tape&nbsp;1&nbsp;: faites-le \u00e9chouer de mani\u00e8re fiable<\/h3>\n<p>Enregistrer le minimum, le maximum, la masse et l'\u00e9nergie avant et apr\u00e8s la course.<\/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>\u00c9tape&nbsp;2&nbsp;: Diviser et conqu\u00e9rir<\/h3>\n<p>Testez le terme de diffusion seul. Supprimez les termes de r\u00e9action, les termes d'advection, les sources non lin\u00e9aires et les conditions aux limites complexes jusqu'\u00e0 ce que la d\u00e9faillance disparaisse ou soit isol\u00e9e.<\/p>\n<h3>\u00c9tape&nbsp;3&nbsp;: Changez une chose<\/h3>\n<p>Essayez un pas de temps plus petit, puis comparez les r\u00e9sultats.<\/p>\n<pre><code class=\"language-python\">dt = 0.001\n<\/code><\/pre>\n<p>Si le bogue persiste avec un pas de temps plus petit, le probl\u00e8me peut \u00eatre spatial, structurel ou li\u00e9 aux conditions aux limites plut\u00f4t qu'\u00e0 la stabilit\u00e9 du temps.<\/p>\n<h3>\u00c9tape&nbsp;4&nbsp;: Utilisez le d\u00e9bogueur<\/h3>\n<p>D\u00e9posez dans un d\u00e9bogueur pr\u00e8s de l'\u00e9tape d\u00e9faillante et inspectez les valeurs, la forme de maillage, les masques et les conditions aux limites.<\/p>\n<pre><code class=\"language-python\">import pdb\npdb.set_trace()\n<\/code><\/pre>\n<h3>\u00c9tape&nbsp;5&nbsp;: Ajoutez un test<\/h3>\n<p>Apr\u00e8s avoir corrig\u00e9 le bogue, ajoutez un test de r\u00e9gression.<\/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>R\u00e9sum\u00e9 et \u00e9tapes suivantes<\/h2>\n<p>Le d\u00e9bogage du code Python scientifique est une comp\u00e9tence progressive. Commencez par les instructions d'impression, passez aux assertions, utilisez les d\u00e9bogueurs lorsque l'inspection d'\u00e9tat est n\u00e9cessaire, profilez-le lorsque le code est lent ou suspect, et \u00e9crivez des tests pour \u00e9viter les r\u00e9gressions.<\/p>\n<p>Le principe le plus important est d'isoler le bogue avant de le corriger. Un bogue que vous pouvez reproduire dans un petit cas de test est beaucoup plus facile \u00e0 r\u00e9soudre. Un bug qui n'appara\u00eet qu'\u00e0 l'int\u00e9rieur d'une grande simulation reste un myst\u00e8re.<\/p>\n<p>Pour le code scientifique, n'oubliez pas ces r\u00e8gles :<\/p>\n<ul>\n<li>V\u00e9rifier la NAN et l'infini apr\u00e8s des op\u00e9rations num\u00e9riques.<\/li>\n<li>Imprimez des formes de tableaux lorsque l'indexation devient complexe.<\/li>\n<li>Surveillez les quantit\u00e9s conserv\u00e9es telles que la masse, l'\u00e9nergie et la quantit\u00e9 de mouvement.<\/li>\n<li>Testez les conditions aux limites s\u00e9par\u00e9es de la logique int\u00e9rieure.<\/li>\n<li>Utilisez des assertions comme premi\u00e8re ligne de d\u00e9fense contre une d\u00e9faillance num\u00e9rique silencieuse.<\/li>\n<\/ul>\n<h3>Prochaines \u00e9tapes<\/h3>\n<ul>\n<li>Commencez \u00e0 utiliser l'\u00e9chelle de d\u00e9bogage d'escalade&nbsp;: commencez par <code>print()<\/code> et montez uniquement en cas de besoin.<\/li>\n<li>Ajoutez des assertions avant et apr\u00e8s les fonctions du solveur.<\/li>\n<li>Configurez la journalisation pour les diagnostics de simulation.<\/li>\n<li>\u00c9crivez au moins un cas de test qui reproduit un bug commun.<\/li>\n<\/ul>\n<p>Le d\u00e9bogage n'est pas un signe de faiblesse. Cela fait partie de la r\u00e9daction de code qui compte. Les comp\u00e9tences que vous d\u00e9veloppez lors du d\u00e9bogage du code Python scientifique vous aideront dans tous les types de projets de logiciels num\u00e9riques et de recherche.<\/p>\n<h2>Guides connexes<\/h2>\n<ul>\n<li><a href=\"https:\/\/matforge.org\/performance-profiling-optimization-python-pde-solvers\/\">Profilage et optimisation des performances pour les solveurs PDE Python&nbsp;: un guide pratique<\/a> \u2014 outils de profilage, Numba et optimisation de la m\u00e9moire.<\/li>\n<li><a href=\"https:\/\/matforge.org\/unit-testing-scientific-code-pytest-strategies-research-projects\/\">Tests unitaires pour le code scientifique&nbsp;: strat\u00e9gies PyTest<\/a> \u2014 Pr\u00e9cision num\u00e9rique et mod\u00e8les de test.<\/li>\n<li><a href=\"https:\/\/matforge.org\/continuous-integration-research-software-automated-testing-validation\/\">Int\u00e9gration continue pour les logiciels de recherche&nbsp;: tests et validation automatis\u00e9s<\/a> \u2014 Pipelines CI et tests automatis\u00e9s.<\/li>\n<li><a href=\"https:\/\/matforge.org\/best-practices-for-maintaining-scientific-code\/\">Meilleures pratiques pour maintenir le code scientifique<\/a> \u2014 Structure du projet, contr\u00f4le des versions et documentation.<\/li>\n<li><a href=\"https:\/\/matforge.org\/using-fipy-for-phase-field-modeling\/\">Utilisation de Fipy pour la mod\u00e9lisation de champ de phase<\/a> \u2014 mod\u00e8les de mise en \u0153uvre FIPY.<\/li>\n<\/ul>\n<h2>Besoin d'aide pour d\u00e9boguer votre code de simulation&nbsp;?<\/h2>\n<p>Le d\u00e9bogage de simulations scientifiques complexes peut prendre du temps, surtout lorsque vous ne savez pas si le bogue est dans les param\u00e8tres de code, de maillage, de limites ou de solveur.<\/p>\n<p>Notre \u00e9quipe peut vous aider avec les workflows de d\u00e9bogage syst\u00e9matiques, les cas de test num\u00e9riques, les contr\u00f4les de sant\u00e9 mentale pour les lois de conservation et les pipelines CI avec des tests de r\u00e9gression.<\/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\"> 12<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>Points \u00e0 retenir cl\u00e9s Le d\u00e9bogage du code Python scientifique suit une \u00e9chelle d&rsquo;escalade&nbsp;: print, assert, pdb, profilage et tests. La plupart des bogues scientifiques sont num\u00e9riques, tels que les valeurs de NAN, les incompatibilit\u00e9s de forme de tableau, la perte de pr\u00e9cision ou les pas de temps instables. print() attrape de nombreux bogues pour [&hellip;]<\/p>\n","protected":false,"raw":""},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_locale":"fr_FR","_original_post":"https:\/\/matforge.org\/?p=336","iawp_total_views":2,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1269","post","type-post","status-publish","format-standard","hentry","category-simulation-modeling-projects","fr-FR"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>D\u00e9boguer le code Python scientifique<\/title>\n<meta name=\"description\" content=\"Apprenez \u00e0 d\u00e9boguer du code Python scientifique avec Print, Logging, Assertions, PDB, Profiling, PyTest et Numerical Sanity Checks.\" \/>\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\/fr\/python-debugging-scientific-code-print-statements-profiling\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"D\u00e9boguer le code Python scientifique\" \/>\n<meta property=\"og:description\" content=\"Apprenez \u00e0 d\u00e9boguer du code Python scientifique avec Print, Logging, Assertions, PDB, Profiling, PyTest et Numerical Sanity Checks.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/\" \/>\n<meta property=\"og:site_name\" content=\"matforge.org\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-21T14:31:33+00:00\" \/>\n<meta name=\"author\" content=\"Priya Nair\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"Priya Nair\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"20 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-debugging-scientific-code-print-statements-profiling\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-debugging-scientific-code-print-statements-profiling\\\/\"},\"author\":{\"name\":\"Priya Nair\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/2effd7bc155a5e6357f31dac970c5795\"},\"headline\":\"D\u00e9bogage Python pour le code scientifique : des relev\u00e9s d&rsquo;impression au profilage\",\"datePublished\":\"2026-08-21T14:31:33+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-debugging-scientific-code-print-statements-profiling\\\/\"},\"wordCount\":3262,\"commentCount\":0,\"articleSection\":[\"Simulation &amp; Projets de mod\u00e9lisation\"],\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/matforge.org\\\/fr\\\/python-debugging-scientific-code-print-statements-profiling\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-debugging-scientific-code-print-statements-profiling\\\/\",\"url\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-debugging-scientific-code-print-statements-profiling\\\/\",\"name\":\"D\u00e9boguer le code Python scientifique\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#website\"},\"datePublished\":\"2026-08-21T14:31:33+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/2effd7bc155a5e6357f31dac970c5795\"},\"description\":\"Apprenez \u00e0 d\u00e9boguer du code Python scientifique avec Print, Logging, Assertions, PDB, Profiling, PyTest et Numerical Sanity Checks.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-debugging-scientific-code-print-statements-profiling\\\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/matforge.org\\\/fr\\\/python-debugging-scientific-code-print-statements-profiling\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-debugging-scientific-code-print-statements-profiling\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/matforge.org\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"D\u00e9bogage Python pour le code scientifique : des relev\u00e9s d&#8217;impression au profilage\"}]},{\"@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\":\"fr-FR\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/2effd7bc155a5e6357f31dac970c5795\",\"name\":\"Priya Nair\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@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":"D\u00e9boguer le code Python scientifique","description":"Apprenez \u00e0 d\u00e9boguer du code Python scientifique avec Print, Logging, Assertions, PDB, Profiling, PyTest et Numerical Sanity Checks.","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\/fr\/python-debugging-scientific-code-print-statements-profiling\/","og_locale":"fr_FR","og_type":"article","og_title":"D\u00e9boguer le code Python scientifique","og_description":"Apprenez \u00e0 d\u00e9boguer du code Python scientifique avec Print, Logging, Assertions, PDB, Profiling, PyTest et Numerical Sanity Checks.","og_url":"https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/","og_site_name":"matforge.org","article_published_time":"2026-08-21T14:31:33+00:00","author":"Priya Nair","twitter_card":"summary_large_image","twitter_misc":{"\u00c9crit par":"Priya Nair","Dur\u00e9e de lecture estim\u00e9e":"20 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/#article","isPartOf":{"@id":"https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/"},"author":{"name":"Priya Nair","@id":"https:\/\/matforge.org\/#\/schema\/person\/2effd7bc155a5e6357f31dac970c5795"},"headline":"D\u00e9bogage Python pour le code scientifique : des relev\u00e9s d&rsquo;impression au profilage","datePublished":"2026-08-21T14:31:33+00:00","mainEntityOfPage":{"@id":"https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/"},"wordCount":3262,"commentCount":0,"articleSection":["Simulation &amp; Projets de mod\u00e9lisation"],"inLanguage":"fr-FR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/","url":"https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/","name":"D\u00e9boguer le code Python scientifique","isPartOf":{"@id":"https:\/\/matforge.org\/#website"},"datePublished":"2026-08-21T14:31:33+00:00","author":{"@id":"https:\/\/matforge.org\/#\/schema\/person\/2effd7bc155a5e6357f31dac970c5795"},"description":"Apprenez \u00e0 d\u00e9boguer du code Python scientifique avec Print, Logging, Assertions, PDB, Profiling, PyTest et Numerical Sanity Checks.","breadcrumb":{"@id":"https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/matforge.org\/fr\/python-debugging-scientific-code-print-statements-profiling\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/matforge.org\/"},{"@type":"ListItem","position":2,"name":"D\u00e9bogage Python pour le code scientifique : des relev\u00e9s d&#8217;impression au profilage"}]},{"@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":"fr-FR"},{"@type":"Person","@id":"https:\/\/matforge.org\/#\/schema\/person\/2effd7bc155a5e6357f31dac970c5795","name":"Priya Nair","image":{"@type":"ImageObject","inLanguage":"fr-FR","@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\/1269","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=1269"}],"version-history":[{"count":1,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/1269\/revisions"}],"predecessor-version":[{"id":1421,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/1269\/revisions\/1421"}],"wp:attachment":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/media?parent=1269"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/categories?post=1269"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/tags?post=1269"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}