Skip to content

Commit 511085b

Browse files
Jammy2211claude
authored andcommitted
fix(viz): make _compute_critical_curve_lines failures loud, not silent
The bare `except Exception: return None, None, None, None` in _compute_critical_curve_lines silently swallowed ZeroSolver failures during model fits, which is the root cause behind two recent viz default reverts: - PyAutoGalaxy abd7b717 (2026-04-19): zero_contour YAML default reverted because critical curves silently vanished on HPC. - PyAutoFit #1280 (2026-05-17): use_jax_for_visualization=True default reverted because Euclid source planes wrote all-zero. Both shared the same shape - a JAX-trace failure inside the viz path that the broad except masked. Tighten so: - ModuleNotFoundError (jax_zero_contour missing): silent (already warned upstream in plot_utils). - ValueError (no zero crossings in eigenvalue grid): silent (the curves genuinely do not exist for the model). - Anything else: WARNING log with exc_info=True so the next regression of this class fails loud. Closes PyAutoLabs/PyAutoGalaxy#433 alongside the PyAutoGalaxy companion PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ccf3f1b commit 511085b

2 files changed

Lines changed: 73 additions & 0 deletions

File tree

autolens/imaging/plot/fit_imaging_plots.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,22 @@ def _compute_critical_curve_lines(tracer, grid):
4949
["white"] * len(_tan_ca_lines) + ["yellow"] * len(_rad_ca_lines)
5050
)
5151
return image_plane_lines, image_plane_line_colors, source_plane_lines, source_plane_line_colors
52+
except (ModuleNotFoundError, ValueError):
53+
# ModuleNotFoundError: jax_zero_contour missing — already warned upstream in
54+
# plot_utils._critical_curves_method().
55+
# ValueError: no zero crossings in the eigenvalue grid (e.g. slope >= 2
56+
# isothermal where lambda_r > 0 everywhere). Curves don't exist for this
57+
# model, so rendering without overlays is correct.
58+
return None, None, None, None
5259
except Exception:
60+
# Anything else — log loudly with traceback so the next regression of the
61+
# "ZeroSolver raised inside model-fit, viz fell back to all-zero" failure
62+
# mode (PyAutoGalaxy abd7b717, PyAutoFit #1280) does not stay silent.
63+
logger.warning(
64+
"Critical-curve computation failed unexpectedly; rendering without "
65+
"overlays. Investigate — this used to be a silent fallback.",
66+
exc_info=True,
67+
)
5368
return None, None, None, None
5469

5570

test_autolens/imaging/plot/test_fit_imaging_plots.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import logging
12
from pathlib import Path
23

34
import pytest
45

6+
from autolens.imaging.plot import fit_imaging_plots
57
from autolens.imaging.plot.fit_imaging_plots import (
8+
_compute_critical_curve_lines,
69
subplot_fit,
710
subplot_fit_log10,
811
subplot_fit_x1_plane,
@@ -156,3 +159,58 @@ def test__subplot_fit_combined_log10__list_of_two_fits__output_file_created(
156159
output_format="png",
157160
)
158161
assert str(plot_path / "fit_combined_log10.png") in plot_patch.paths
162+
163+
164+
@pytest.mark.parametrize(
165+
"exc_cls",
166+
[ModuleNotFoundError, ValueError],
167+
ids=["jax_zero_contour_missing", "no_zero_crossings"],
168+
)
169+
def test__compute_critical_curve_lines__known_recoverable_exceptions__silent(
170+
monkeypatch, caplog, exc_cls
171+
):
172+
"""
173+
Two failure modes are expected and pre-handled upstream: ``jax_zero_contour``
174+
not installed (``ModuleNotFoundError``) and a model with no zero crossings
175+
(``ValueError`` raised by ``_init_guess_from_coarse_grid``). These must
176+
fall through silently — no WARNING log — so plot-time noise stays clean
177+
when the absence of critical curves is the correct rendering.
178+
"""
179+
def boom(*args, **kwargs):
180+
raise exc_cls("synthetic failure for test")
181+
182+
monkeypatch.setattr(fit_imaging_plots, "_critical_curves_from", boom)
183+
184+
with caplog.at_level(logging.WARNING, logger=fit_imaging_plots.__name__):
185+
result = _compute_critical_curve_lines(tracer=None, grid=None)
186+
187+
assert result == (None, None, None, None)
188+
assert caplog.records == [], (
189+
"known-recoverable failure must not emit a WARNING log"
190+
)
191+
192+
193+
def test__compute_critical_curve_lines__unexpected_exception__logs_warning(
194+
monkeypatch, caplog
195+
):
196+
"""
197+
Anything OTHER than ``ModuleNotFoundError`` / ``ValueError`` is treated as
198+
an unexpected failure (the silent failure mode that caused the
199+
2026-04-19 PyAutoGalaxy zero_contour revert and the 2026-05-16 Euclid
200+
pipeline regression). Such failures must surface as a WARNING log with
201+
a traceback — never silently swallowed.
202+
"""
203+
def boom(*args, **kwargs):
204+
raise RuntimeError("synthetic unexpected failure for test")
205+
206+
monkeypatch.setattr(fit_imaging_plots, "_critical_curves_from", boom)
207+
208+
with caplog.at_level(logging.WARNING, logger=fit_imaging_plots.__name__):
209+
result = _compute_critical_curve_lines(tracer=None, grid=None)
210+
211+
assert result == (None, None, None, None)
212+
assert len(caplog.records) == 1
213+
record = caplog.records[0]
214+
assert record.levelno == logging.WARNING
215+
assert record.exc_info is not None, "traceback must be attached"
216+
assert isinstance(record.exc_info[1], RuntimeError)

0 commit comments

Comments
 (0)