Skip to content

Commit 545ce1d

Browse files
Jammy2211claude
authored andcommitted
Plot improvements: subplot_tracer_from_fit overhaul, line_colors, caustics
- subplot_tracer_from_fit: compute critical curves and caustics before drawing panels so all source panels receive line overlays; add deflections Y/X and magnification to panels 6-8; use lens_galaxies.deflections_yx_2d_from for correct panel content - subplot_tracer: add critical curves (black=tangential, white=radial) and caustics; harden source image and source plane panels; add source_vmax scaling - _plot_source_plane: log exceptions instead of silently blanking the axis - plotter.py: remove standalone subplot_tracer call from tracer plotter (imaging plotter handles subplot_tracer via subplot_tracer_from_fit) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 085d027 commit 545ce1d

3 files changed

Lines changed: 192 additions & 71 deletions

File tree

autolens/analysis/plotter.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,6 @@ def should_plot(name):
5555
output_path = str(self.image_path)
5656
fmt = self.fmt
5757

58-
if should_plot("subplot_tracer"):
59-
subplot_tracer(
60-
tracer=tracer,
61-
grid=grid,
62-
output_path=output_path,
63-
output_format=fmt,
64-
)
65-
6658
if should_plot("subplot_galaxies_images"):
6759
subplot_galaxies_images(
6860
tracer=tracer,

autolens/imaging/plot/fit_imaging_plots.py

Lines changed: 166 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
import matplotlib.pyplot as plt
23
import numpy as np
34
from typing import Optional, List
@@ -8,8 +9,12 @@
89
from autoarray.plot.array import plot_array, _zoom_array_2d
910
from autoarray.plot.utils import save_figure, hide_unused_axes, conf_subplot_figsize
1011
from autoarray.plot.utils import numpy_lines as _to_lines
12+
from autoarray.inversion.mappers.abstract import Mapper
13+
from autoarray.inversion.plot.mapper_plots import plot_mapper
1114
from autogalaxy.plot.plot_utils import _critical_curves_from, _caustics_from
1215

16+
logger = logging.getLogger(__name__)
17+
1318

1419
def _get_source_vmax(fit):
1520
"""
@@ -39,7 +44,8 @@ def _get_source_vmax(fit):
3944

4045

4146
def _plot_source_plane(fit, ax, plane_index, zoom_to_brightest=True,
42-
colormap=None, use_log10=False):
47+
colormap=None, use_log10=False, title=None,
48+
lines=None, line_colors=None):
4349
"""
4450
Plot the source-plane image (or a blank inversion placeholder) into an axes.
4551
@@ -48,9 +54,10 @@ def _plot_source_plane(fit, ax, plane_index, zoom_to_brightest=True,
4854
function ray-traces a zoomed image-plane grid to the source plane,
4955
evaluates the source-galaxy light, and renders the resulting 2-D array
5056
via :func:`~autoarray.plot.array.plot_array`. When the plane *does*
51-
contain a pixelization (an inversion source), the axes are turned off
52-
and labelled as "Source Reconstruction" instead, because the inversion
53-
reconstruction is rendered separately by the inversion plotter.
57+
contain a pixelization (an inversion source), the source reconstruction
58+
is rendered via :func:`~autoarray.inversion.plot.mapper_plots.plot_mapper`
59+
using ``zoom_to_brightest`` to control whether the view is zoomed in on
60+
the brightest pixels or shown at full extent.
5461
5562
Parameters
5663
----------
@@ -62,8 +69,9 @@ def _plot_source_plane(fit, ax, plane_index, zoom_to_brightest=True,
6269
plane_index : int
6370
Index of the plane in ``fit.tracer.planes`` to visualise.
6471
zoom_to_brightest : bool, optional
65-
Passed through to the zoom logic (currently unused in the
66-
rendering call but reserved for future use).
72+
For inversion sources, zooms the colormap extent to the brightest
73+
reconstructed pixels. For parametric sources, this parameter has
74+
no effect.
6775
colormap : str, optional
6876
Matplotlib colormap name.
6977
use_log10 : bool, optional
@@ -80,14 +88,33 @@ def _plot_source_plane(fit, ax, plane_index, zoom_to_brightest=True,
8088
image = plane_galaxies.image_2d_from(grid=traced_grids[plane_index])
8189
plot_array(
8290
array=image, ax=ax,
83-
title=f"Source Plane {plane_index}",
84-
colormap=colormap, use_log10=use_log10,
91+
title=title if title is not None else f"Source Plane {plane_index}",
92+
colormap=colormap, use_log10=use_log10, lines=lines,
93+
line_colors=line_colors,
8594
)
8695
else:
87-
# Inversion path: in subplot context show a blank panel.
88-
if ax is not None:
89-
ax.axis("off")
90-
ax.set_title(f"Source Reconstruction (plane {plane_index})")
96+
# Inversion path: plot the source reconstruction via the mapper.
97+
try:
98+
inversion = fit.inversion
99+
mapper_list = inversion.cls_list_from(cls=Mapper)
100+
mapper = mapper_list[plane_index - 1] if plane_index > 0 else mapper_list[0]
101+
pixel_values = inversion.reconstruction_dict[mapper]
102+
plot_mapper(
103+
mapper,
104+
solution_vector=pixel_values,
105+
ax=ax,
106+
title=title if title is not None else f"Source Reconstruction (plane {plane_index})",
107+
colormap=colormap,
108+
use_log10=use_log10,
109+
zoom_to_brightest=zoom_to_brightest,
110+
lines=lines,
111+
line_colors=line_colors,
112+
)
113+
except Exception as exc:
114+
logger.warning(f"Could not plot source reconstruction for plane {plane_index}: {exc}")
115+
if ax is not None:
116+
ax.axis("off")
117+
ax.set_title(f"Source Reconstruction (plane {plane_index})")
91118

92119

93120
def subplot_fit(
@@ -144,6 +171,31 @@ def subplot_fit(
144171

145172
source_vmax = _get_source_vmax(fit)
146173

174+
tracer = fit.tracer_linear_light_profiles_to_light_profiles
175+
try:
176+
_zoom = aa.Zoom2D(mask=fit.mask)
177+
_cc_grid = aa.Grid2D.from_extent(
178+
extent=_zoom.extent_from(buffer=0),
179+
shape_native=_zoom.shape_native,
180+
)
181+
tan_cc, rad_cc = _critical_curves_from(tracer, _cc_grid)
182+
tan_ca, rad_ca = _caustics_from(tracer, _cc_grid)
183+
_tan_cc_lines = _to_lines(list(tan_cc) if tan_cc is not None else []) or []
184+
_rad_cc_lines = _to_lines(list(rad_cc) if rad_cc is not None else []) or []
185+
_tan_ca_lines = _to_lines(list(tan_ca) if tan_ca is not None else []) or []
186+
_rad_ca_lines = _to_lines(list(rad_ca) if rad_ca is not None else []) or []
187+
image_plane_lines = _tan_cc_lines + _rad_cc_lines
188+
image_plane_line_colors = ["black"] * len(_tan_cc_lines) + ["white"] * len(_rad_cc_lines)
189+
source_plane_lines = _tan_ca_lines + _rad_ca_lines
190+
source_plane_line_colors = ["black"] * len(_tan_ca_lines) + ["white"] * len(_rad_ca_lines)
191+
image_plane_lines = image_plane_lines or None
192+
source_plane_lines = source_plane_lines or None
193+
except Exception:
194+
image_plane_lines = None
195+
image_plane_line_colors = None
196+
source_plane_lines = None
197+
source_plane_line_colors = None
198+
147199
fig, axes = plt.subplots(3, 4, figsize=conf_subplot_figsize(3, 4))
148200
axes_flat = list(axes.flatten())
149201

@@ -156,7 +208,8 @@ def subplot_fit(
156208
plot_array(array=fit.signal_to_noise_map, ax=axes_flat[2],
157209
title="Signal-To-Noise Map", colormap=colormap)
158210
plot_array(array=fit.model_data, ax=axes_flat[3], title="Model Image",
159-
colormap=colormap)
211+
colormap=colormap, lines=image_plane_lines,
212+
line_colors=image_plane_line_colors)
160213

161214
# Lens model image
162215
try:
@@ -188,31 +241,34 @@ def subplot_fit(
188241
source_model_img = None
189242
if source_model_img is not None:
190243
plot_array(array=source_model_img, ax=axes_flat[6], title="Source Model Image",
191-
colormap=colormap, vmax=source_vmax)
244+
colormap=colormap, vmax=source_vmax, lines=image_plane_lines,
245+
line_colors=image_plane_line_colors)
192246
else:
193247
axes_flat[6].axis("off")
194248

195249
# Source plane zoomed
196250
_plot_source_plane(fit, axes_flat[7], final_plane_index, zoom_to_brightest=True,
197-
colormap=colormap)
251+
colormap=colormap, title="Source Plane (Zoomed)",
252+
lines=source_plane_lines, line_colors=source_plane_line_colors)
198253

199254
# Normalized residual map (symmetric)
200255
norm_resid = fit.normalized_residual_map
201256
_abs_max = _symmetric_vmax(norm_resid)
202257
plot_array(array=norm_resid, ax=axes_flat[8], title="Normalized Residual Map",
203-
colormap=colormap, vmin=-_abs_max, vmax=_abs_max, cb_unit=r"$\sigma$")
258+
colormap=colormap, vmin=-_abs_max, vmax=_abs_max)
204259

205260
# Normalized residual map clipped to [-1, 1]
206261
plot_array(array=norm_resid, ax=axes_flat[9],
207262
title=r"Normalized Residual Map $1\sigma$",
208-
colormap=colormap, vmin=-1.0, vmax=1.0, cb_unit=r"$\sigma$")
263+
colormap=colormap, vmin=-1.0, vmax=1.0)
209264

210265
plot_array(array=fit.chi_squared_map, ax=axes_flat[10],
211266
title="Chi-Squared Map", colormap=colormap, cb_unit=r"$\chi^2$")
212267

213268
# Source plane not zoomed
214269
_plot_source_plane(fit, axes_flat[11], final_plane_index, zoom_to_brightest=False,
215-
colormap=colormap)
270+
colormap=colormap, title="Source Plane (No Zoom)",
271+
lines=source_plane_lines, line_colors=source_plane_line_colors)
216272

217273
hide_unused_axes(axes_flat)
218274
plt.tight_layout()
@@ -530,17 +586,16 @@ def subplot_tracer_from_fit(
530586
"""
531587
Produce a 9-panel tracer subplot derived from a `FitImaging` object.
532588
533-
Uses the best-fit linear-light-profile tracer to render:
534-
535-
* Model image (full lensed image)
536-
* Source model image (source-plane brightness at image scale)
537-
* Source plane image (evaluated on the image-plane grid, full extent)
538-
* Lens-plane image with critical curves (log10 scale)
539-
* Panels 5–9 are reserved (currently blank) for future mass-map panels
540-
541-
The critical curves are computed from the tracer via
542-
:func:`~autogalaxy.plot.plot_utils._critical_curves_from` and overlaid
543-
on the lens-plane image.
589+
Panels (3x3 = 9 axes):
590+
0: Model image with critical curves
591+
1: Source model image (image-plane projection) with critical curves
592+
2: Source plane (no zoom) with caustics
593+
3: Lens image (log10) with critical curves
594+
4: Convergence (log10)
595+
5: Potential (log10)
596+
6: Deflections Y with critical curves
597+
7: Deflections X with critical curves
598+
8: Magnification with critical curves
544599
545600
Parameters
546601
----------
@@ -554,44 +609,106 @@ def subplot_tracer_from_fit(
554609
colormap : str, optional
555610
Matplotlib colormap name applied to all image panels.
556611
"""
612+
from autogalaxy.operate.lens_calc import LensCalc
613+
557614
final_plane_index = len(fit.tracer.planes) - 1
615+
tracer = fit.tracer_linear_light_profiles_to_light_profiles
616+
617+
# --- grid and critical curves (computed first so all panels can use them) ---
618+
zoom = aa.Zoom2D(mask=fit.mask)
619+
grid = aa.Grid2D.from_extent(
620+
extent=zoom.extent_from(buffer=0), shape_native=zoom.shape_native
621+
)
622+
623+
try:
624+
tan_cc, rad_cc = _critical_curves_from(tracer, grid)
625+
tan_ca, rad_ca = _caustics_from(tracer, grid)
626+
_tan_cc_lines = _to_lines(list(tan_cc) if tan_cc is not None else []) or []
627+
_rad_cc_lines = _to_lines(list(rad_cc) if rad_cc is not None else []) or []
628+
_tan_ca_lines = _to_lines(list(tan_ca) if tan_ca is not None else []) or []
629+
_rad_ca_lines = _to_lines(list(rad_ca) if rad_ca is not None else []) or []
630+
image_plane_lines = (_tan_cc_lines + _rad_cc_lines) or None
631+
image_plane_line_colors = ["black"] * len(_tan_cc_lines) + ["white"] * len(_rad_cc_lines)
632+
source_plane_lines = (_tan_ca_lines + _rad_ca_lines) or None
633+
source_plane_line_colors = ["black"] * len(_tan_ca_lines) + ["white"] * len(_rad_ca_lines)
634+
except Exception:
635+
image_plane_lines = None
636+
image_plane_line_colors = None
637+
source_plane_lines = None
638+
source_plane_line_colors = None
639+
640+
source_vmax = _get_source_vmax(fit)
641+
642+
traced_grids = tracer.traced_grid_2d_list_from(grid=grid)
643+
lens_galaxies = ag.Galaxies(galaxies=tracer.planes[0])
644+
lens_image = lens_galaxies.image_2d_from(grid=traced_grids[0])
645+
646+
deflections = lens_galaxies.deflections_yx_2d_from(grid=grid)
647+
deflections_y = aa.Array2D(values=deflections.slim[:, 0], mask=grid.mask)
648+
deflections_x = aa.Array2D(values=deflections.slim[:, 1], mask=grid.mask)
649+
650+
magnification = LensCalc.from_mass_obj(tracer).magnification_2d_from(grid=grid)
558651

559652
fig, axes = plt.subplots(3, 3, figsize=conf_subplot_figsize(3, 3))
560653
axes_flat = list(axes.flatten())
561654

562-
tracer = fit.tracer_linear_light_profiles_to_light_profiles
563-
655+
# Panel 0: Model Image
564656
plot_array(array=fit.model_data, ax=axes_flat[0], title="Model Image",
657+
lines=image_plane_lines, line_colors=image_plane_line_colors,
565658
colormap=colormap)
566659

660+
# Panel 1: Source Model Image (image-plane projection)
567661
try:
568662
source_model_img = fit.model_images_of_planes_list[final_plane_index]
569-
source_vmax = float(np.max(source_model_img.array))
663+
except Exception:
664+
source_model_img = None
665+
if source_model_img is not None:
570666
plot_array(array=source_model_img, ax=axes_flat[1], title="Source Model Image",
571-
colormap=colormap, vmax=source_vmax)
572-
except (IndexError, AttributeError, ValueError):
667+
colormap=colormap, vmax=source_vmax,
668+
lines=image_plane_lines, line_colors=image_plane_line_colors)
669+
else:
573670
axes_flat[1].axis("off")
574671

672+
# Panel 2: Source Plane (No Zoom)
575673
_plot_source_plane(fit, axes_flat[2], final_plane_index, zoom_to_brightest=False,
576-
colormap=colormap)
674+
colormap=colormap, title="Source Plane (No Zoom)",
675+
lines=source_plane_lines, line_colors=source_plane_line_colors)
577676

578-
# Lens plane mass quantities (log10)
579-
zoom = aa.Zoom2D(mask=fit.mask)
580-
grid = aa.Grid2D.from_extent(
581-
extent=zoom.extent_from(buffer=0), shape_native=zoom.shape_native
582-
)
677+
# Panel 3: Lens Image (log10)
678+
plot_array(array=lens_image, ax=axes_flat[3], title="Lens Image",
679+
lines=image_plane_lines, line_colors=image_plane_line_colors,
680+
colormap=colormap, use_log10=True)
583681

584-
tan_cc, rad_cc = _critical_curves_from(tracer, grid)
585-
image_plane_lines = _to_lines(list(tan_cc) + (list(rad_cc) if rad_cc is not None else []))
682+
# Panel 4: Convergence (log10)
683+
try:
684+
convergence = tracer.convergence_2d_from(grid=grid)
685+
plot_array(array=convergence, ax=axes_flat[4], title="Convergence",
686+
colormap=colormap, use_log10=True)
687+
except Exception:
688+
axes_flat[4].axis("off")
586689

587-
traced_grids = tracer.traced_grid_2d_list_from(grid=grid)
588-
lens_galaxies = ag.Galaxies(galaxies=tracer.planes[0])
589-
lens_image = lens_galaxies.image_2d_from(grid=traced_grids[0])
590-
plot_array(array=lens_image, ax=axes_flat[3], title="Lens Image",
591-
lines=image_plane_lines, colormap=colormap, use_log10=True)
690+
# Panel 5: Potential (log10)
691+
try:
692+
potential = tracer.potential_2d_from(grid=grid)
693+
plot_array(array=potential, ax=axes_flat[5], title="Potential",
694+
colormap=colormap, use_log10=True)
695+
except Exception:
696+
axes_flat[5].axis("off")
697+
698+
# Panel 6: Deflections Y
699+
plot_array(array=deflections_y, ax=axes_flat[6], title="Deflections Y",
700+
lines=image_plane_lines, line_colors=image_plane_line_colors,
701+
colormap=colormap)
592702

593-
for i in range(4, 9):
594-
axes_flat[i].axis("off")
703+
# Panel 7: Deflections X
704+
plot_array(array=deflections_x, ax=axes_flat[7], title="Deflections X",
705+
lines=image_plane_lines, line_colors=image_plane_line_colors,
706+
colormap=colormap)
707+
708+
# Panel 8: Magnification
709+
plot_array(array=magnification, ax=axes_flat[8], title="Magnification",
710+
lines=image_plane_lines, line_colors=image_plane_line_colors,
711+
colormap=colormap)
595712

596713
plt.tight_layout()
597714
save_figure(fig, path=output_path, filename="tracer", format=output_format)

0 commit comments

Comments
 (0)