Skip to content

Commit b5d6389

Browse files
authored
Merge pull request #409 from Jammy2211/feature/plot-improvements-5
Plot improvements: critical curves once, plane_image_from, source vmax
2 parents a6d8317 + 3076177 commit b5d6389

5 files changed

Lines changed: 245 additions & 79 deletions

File tree

autolens/imaging/model/plotter.py

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import numpy as np
33
from typing import List
44

5+
import autoarray as aa
56
from autogalaxy.imaging.model.plotter import PlotterImaging as AgPlotterImaging
67
from autogalaxy.imaging.plot.fit_imaging_plots import (
78
fits_fit,
@@ -18,6 +19,7 @@
1819
subplot_tracer_from_fit,
1920
subplot_fit_combined,
2021
subplot_fit_combined_log10,
22+
_compute_critical_curve_lines,
2123
)
2224

2325
from autolens.analysis.plotter import plot_setting
@@ -50,29 +52,57 @@ def should_plot(name):
5052

5153
plane_indexes_to_plot = [i for i in fit.tracer.plane_indexes_with_images if i != 0]
5254

55+
# Compute critical curves and caustics once for all subplot functions.
56+
tracer = fit.tracer_linear_light_profiles_to_light_profiles
57+
_zoom = aa.Zoom2D(mask=fit.mask)
58+
_cc_grid = aa.Grid2D.from_extent(
59+
extent=_zoom.extent_from(buffer=0), shape_native=_zoom.shape_native
60+
)
61+
ip_lines, ip_colors, sp_lines, sp_colors = _compute_critical_curve_lines(tracer, _cc_grid)
62+
5363
if should_plot("subplot_fit") or quick_update:
5464

5565
if len(fit.tracer.planes) > 2:
5666
for plane_index in plane_indexes_to_plot:
57-
subplot_fit(fit, output_path=output_path, output_format=fmt,
58-
plane_index=plane_index)
67+
subplot_fit(
68+
fit, output_path=output_path, output_format=fmt,
69+
plane_index=plane_index,
70+
image_plane_lines=ip_lines, image_plane_line_colors=ip_colors,
71+
source_plane_lines=sp_lines, source_plane_line_colors=sp_colors,
72+
)
5973
else:
60-
subplot_fit(fit, output_path=output_path, output_format=fmt)
74+
subplot_fit(
75+
fit, output_path=output_path, output_format=fmt,
76+
image_plane_lines=ip_lines, image_plane_line_colors=ip_colors,
77+
source_plane_lines=sp_lines, source_plane_line_colors=sp_colors,
78+
)
6179

6280
if quick_update:
6381
return
6482

6583
if plot_setting(section="tracer", name="subplot_tracer"):
66-
subplot_tracer_from_fit(fit, output_path=output_path, output_format=fmt)
84+
subplot_tracer_from_fit(
85+
fit, output_path=output_path, output_format=fmt,
86+
image_plane_lines=ip_lines, image_plane_line_colors=ip_colors,
87+
source_plane_lines=sp_lines, source_plane_line_colors=sp_colors,
88+
)
6789

6890
if should_plot("subplot_fit_log10"):
6991
try:
7092
if len(fit.tracer.planes) > 2:
7193
for plane_index in plane_indexes_to_plot:
72-
subplot_fit_log10(fit, output_path=output_path, output_format=fmt,
73-
plane_index=plane_index)
94+
subplot_fit_log10(
95+
fit, output_path=output_path, output_format=fmt,
96+
plane_index=plane_index,
97+
image_plane_lines=ip_lines, image_plane_line_colors=ip_colors,
98+
source_plane_lines=sp_lines, source_plane_line_colors=sp_colors,
99+
)
74100
else:
75-
subplot_fit_log10(fit, output_path=output_path, output_format=fmt)
101+
subplot_fit_log10(
102+
fit, output_path=output_path, output_format=fmt,
103+
image_plane_lines=ip_lines, image_plane_line_colors=ip_colors,
104+
source_plane_lines=sp_lines, source_plane_line_colors=sp_colors,
105+
)
76106
except ValueError:
77107
pass
78108

autolens/imaging/plot/fit_imaging_plots.py

Lines changed: 105 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,43 @@
1616
logger = logging.getLogger(__name__)
1717

1818

19+
def _compute_critical_curve_lines(tracer, grid):
20+
"""Compute critical-curve and caustic lines for a tracer on a given grid.
21+
22+
Returns a 4-tuple ``(image_plane_lines, image_plane_line_colors,
23+
source_plane_lines, source_plane_line_colors)`` suitable for passing
24+
directly to :func:`~autoarray.plot.array.plot_array`. On failure
25+
(e.g. the mass model has no critical curves) returns
26+
``(None, None, None, None)``.
27+
28+
Parameters
29+
----------
30+
tracer
31+
The tracer whose mass distribution is used to trace critical curves
32+
and caustics.
33+
grid
34+
Image-plane grid on which the curves are evaluated.
35+
"""
36+
try:
37+
tan_cc, rad_cc = _critical_curves_from(tracer, grid)
38+
tan_ca, rad_ca = _caustics_from(tracer, grid)
39+
_tan_cc_lines = _to_lines(list(tan_cc) if tan_cc is not None else []) or []
40+
_rad_cc_lines = _to_lines(list(rad_cc) if rad_cc is not None else []) or []
41+
_tan_ca_lines = _to_lines(list(tan_ca) if tan_ca is not None else []) or []
42+
_rad_ca_lines = _to_lines(list(rad_ca) if rad_ca is not None else []) or []
43+
image_plane_lines = (_tan_cc_lines + _rad_cc_lines) or None
44+
image_plane_line_colors = (
45+
["black"] * len(_tan_cc_lines) + ["white"] * len(_rad_cc_lines)
46+
)
47+
source_plane_lines = (_tan_ca_lines + _rad_ca_lines) or None
48+
source_plane_line_colors = (
49+
["black"] * len(_tan_ca_lines) + ["white"] * len(_rad_ca_lines)
50+
)
51+
return image_plane_lines, image_plane_line_colors, source_plane_lines, source_plane_line_colors
52+
except Exception:
53+
return None, None, None, None
54+
55+
1956
def _get_source_vmax(fit):
2057
"""
2158
Return the colour-scale maximum for source-plane panels.
@@ -43,17 +80,22 @@ def _get_source_vmax(fit):
4380
return None
4481

4582

83+
from autolens.lens.plot.tracer_plots import plane_image_from
84+
85+
4686
def _plot_source_plane(fit, ax, plane_index, zoom_to_brightest=True,
4787
colormap=None, use_log10=False, title=None,
48-
lines=None, line_colors=None):
88+
lines=None, line_colors=None, vmax=None):
4989
"""
5090
Plot the source-plane image (or a blank inversion placeholder) into an axes.
5191
5292
When the plane at ``plane_index`` does not contain a
53-
`~autoarray.Pixelization` (i.e. it is a parametric source), the
54-
function ray-traces a zoomed image-plane grid to the source plane,
55-
evaluates the source-galaxy light, and renders the resulting 2-D array
56-
via :func:`~autoarray.plot.array.plot_array`. When the plane *does*
93+
`~autoarray.Pixelization` (i.e. it is a parametric source), the source
94+
galaxy light profiles are evaluated on a plain uniform grid
95+
(``fit.mask.derive_grid.all_false``) — **not** a ray-traced grid. This
96+
shows the source as it appears in its own plane, without lensing
97+
distortion. :func:`~autolens.lens.plot.tracer_plots.plane_image_from`
98+
handles the optional zoom to the brightest region. When the plane *does*
5799
contain a pixelization (an inversion source), the source reconstruction
58100
is rendered via :func:`~autoarray.inversion.plot.mapper_plots.plot_mapper`
59101
using ``zoom_to_brightest`` to control whether the view is zoomed in on
@@ -69,27 +111,25 @@ def _plot_source_plane(fit, ax, plane_index, zoom_to_brightest=True,
69111
plane_index : int
70112
Index of the plane in ``fit.tracer.planes`` to visualise.
71113
zoom_to_brightest : bool, optional
72-
For inversion sources, zooms the colormap extent to the brightest
73-
reconstructed pixels. For parametric sources, this parameter has
74-
no effect.
114+
For parametric sources, zooms the evaluation grid in on the brightest
115+
region of the source plane via :func:`plane_image_from`. For inversion
116+
sources, zooms the colormap extent to the brightest reconstructed pixels.
75117
colormap : str, optional
76118
Matplotlib colormap name.
77119
use_log10 : bool, optional
78120
If ``True`` the colour scale is applied on a log10 stretch.
79121
"""
80122
tracer = fit.tracer_linear_light_profiles_to_light_profiles
81123
if not tracer.planes[plane_index].has(cls=aa.Pixelization):
82-
zoom = aa.Zoom2D(mask=fit.mask)
83-
grid = aa.Grid2D.from_extent(
84-
extent=zoom.extent_from(buffer=0), shape_native=zoom.shape_native
124+
image = plane_image_from(
125+
galaxies=tracer.planes[plane_index],
126+
grid=fit.mask.derive_grid.all_false,
127+
zoom_to_brightest=zoom_to_brightest,
85128
)
86-
traced_grids = tracer.traced_grid_2d_list_from(grid=grid)
87-
plane_galaxies = ag.Galaxies(galaxies=tracer.planes[plane_index])
88-
image = plane_galaxies.image_2d_from(grid=traced_grids[0])
89129
plot_array(
90130
array=image, ax=ax,
91131
title=title if title is not None else f"Source Plane {plane_index}",
92-
colormap=colormap, use_log10=use_log10, lines=lines,
132+
colormap=colormap, use_log10=use_log10, vmax=vmax, lines=lines,
93133
line_colors=line_colors,
94134
)
95135
else:
@@ -106,6 +146,7 @@ def _plot_source_plane(fit, ax, plane_index, zoom_to_brightest=True,
106146
title=title if title is not None else f"Source Reconstruction (plane {plane_index})",
107147
colormap=colormap,
108148
use_log10=use_log10,
149+
vmax=vmax,
109150
zoom_to_brightest=zoom_to_brightest,
110151
lines=lines,
111152
line_colors=line_colors,
@@ -123,6 +164,10 @@ def subplot_fit(
123164
output_format: str = "png",
124165
colormap: Optional[str] = None,
125166
plane_index: Optional[int] = None,
167+
image_plane_lines=None,
168+
image_plane_line_colors=None,
169+
source_plane_lines=None,
170+
source_plane_line_colors=None,
126171
):
127172
"""
128173
Produce a 12-panel subplot summarising an imaging fit.
@@ -171,30 +216,16 @@ def subplot_fit(
171216

172217
source_vmax = _get_source_vmax(fit)
173218

174-
tracer = fit.tracer_linear_light_profiles_to_light_profiles
175-
try:
219+
if image_plane_lines is None and source_plane_lines is None:
220+
tracer = fit.tracer_linear_light_profiles_to_light_profiles
176221
_zoom = aa.Zoom2D(mask=fit.mask)
177222
_cc_grid = aa.Grid2D.from_extent(
178223
extent=_zoom.extent_from(buffer=0),
179224
shape_native=_zoom.shape_native,
180225
)
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
226+
image_plane_lines, image_plane_line_colors, source_plane_lines, source_plane_line_colors = (
227+
_compute_critical_curve_lines(tracer, _cc_grid)
228+
)
198229

199230
fig, axes = plt.subplots(3, 4, figsize=conf_subplot_figsize(3, 4))
200231
axes_flat = list(axes.flatten())
@@ -249,7 +280,8 @@ def subplot_fit(
249280
# Source plane zoomed
250281
_plot_source_plane(fit, axes_flat[7], final_plane_index, zoom_to_brightest=True,
251282
colormap=colormap, title="Source Plane (Zoomed)",
252-
lines=source_plane_lines, line_colors=source_plane_line_colors)
283+
lines=source_plane_lines, line_colors=source_plane_line_colors,
284+
vmax=source_vmax)
253285

254286
# Normalized residual map (symmetric)
255287
norm_resid = fit.normalized_residual_map
@@ -268,7 +300,8 @@ def subplot_fit(
268300
# Source plane not zoomed
269301
_plot_source_plane(fit, axes_flat[11], final_plane_index, zoom_to_brightest=False,
270302
colormap=colormap, title="Source Plane (No Zoom)",
271-
lines=source_plane_lines, line_colors=source_plane_line_colors)
303+
lines=source_plane_lines, line_colors=source_plane_line_colors,
304+
vmax=source_vmax)
272305

273306
hide_unused_axes(axes_flat)
274307
plt.tight_layout()
@@ -345,6 +378,10 @@ def subplot_fit_log10(
345378
output_format: str = "png",
346379
colormap: Optional[str] = None,
347380
plane_index: Optional[int] = None,
381+
image_plane_lines=None,
382+
image_plane_line_colors=None,
383+
source_plane_lines=None,
384+
source_plane_line_colors=None,
348385
):
349386
"""
350387
Produce a 12-panel subplot summarising an imaging fit with log10 colour scaling.
@@ -384,6 +421,17 @@ def subplot_fit_log10(
384421

385422
source_vmax = _get_source_vmax(fit)
386423

424+
if image_plane_lines is None and source_plane_lines is None:
425+
tracer = fit.tracer_linear_light_profiles_to_light_profiles
426+
_zoom = aa.Zoom2D(mask=fit.mask)
427+
_cc_grid = aa.Grid2D.from_extent(
428+
extent=_zoom.extent_from(buffer=0),
429+
shape_native=_zoom.shape_native,
430+
)
431+
image_plane_lines, image_plane_line_colors, source_plane_lines, source_plane_line_colors = (
432+
_compute_critical_curve_lines(tracer, _cc_grid)
433+
)
434+
387435
fig, axes = plt.subplots(3, 4, figsize=conf_subplot_figsize(3, 4))
388436
axes_flat = list(axes.flatten())
389437

@@ -403,7 +451,8 @@ def subplot_fit_log10(
403451
axes_flat[2].axis("off")
404452

405453
plot_array(array=fit.model_data, ax=axes_flat[3], title="Model Image",
406-
colormap=colormap, use_log10=True)
454+
colormap=colormap, use_log10=True, lines=image_plane_lines,
455+
line_colors=image_plane_line_colors)
407456

408457
try:
409458
lens_model_img = fit.model_images_of_planes_list[0]
@@ -422,12 +471,15 @@ def subplot_fit_log10(
422471
try:
423472
source_model_img = fit.model_images_of_planes_list[final_plane_index]
424473
plot_array(array=source_model_img, ax=axes_flat[6],
425-
title="Source Model Image", colormap=colormap, use_log10=True)
474+
title="Source Model Image", colormap=colormap, use_log10=True,
475+
lines=image_plane_lines, line_colors=image_plane_line_colors)
426476
except (IndexError, AttributeError):
427477
axes_flat[6].axis("off")
428478

429479
_plot_source_plane(fit, axes_flat[7], final_plane_index, zoom_to_brightest=True,
430-
colormap=colormap, use_log10=True)
480+
colormap=colormap, use_log10=True,
481+
lines=source_plane_lines, line_colors=source_plane_line_colors,
482+
vmax=source_vmax)
431483

432484
norm_resid = fit.normalized_residual_map
433485
_abs_max = _symmetric_vmax(norm_resid)
@@ -442,7 +494,9 @@ def subplot_fit_log10(
442494
colormap=colormap, use_log10=True, cb_unit=r"$\chi^2$")
443495

444496
_plot_source_plane(fit, axes_flat[11], final_plane_index, zoom_to_brightest=False,
445-
colormap=colormap, use_log10=True)
497+
colormap=colormap, use_log10=True,
498+
lines=source_plane_lines, line_colors=source_plane_line_colors,
499+
vmax=source_vmax)
446500

447501
plt.tight_layout()
448502
save_figure(fig, path=output_path, filename=f"fit_log10{plane_index_tag}", format=output_format)
@@ -582,6 +636,10 @@ def subplot_tracer_from_fit(
582636
output_path: Optional[str] = None,
583637
output_format: str = "png",
584638
colormap: Optional[str] = None,
639+
image_plane_lines=None,
640+
image_plane_line_colors=None,
641+
source_plane_lines=None,
642+
source_plane_line_colors=None,
585643
):
586644
"""
587645
Produce a 9-panel tracer subplot derived from a `FitImaging` object.
@@ -614,28 +672,16 @@ def subplot_tracer_from_fit(
614672
final_plane_index = len(fit.tracer.planes) - 1
615673
tracer = fit.tracer_linear_light_profiles_to_light_profiles
616674

617-
# --- grid and critical curves (computed first so all panels can use them) ---
675+
# --- grid ---
618676
zoom = aa.Zoom2D(mask=fit.mask)
619677
grid = aa.Grid2D.from_extent(
620678
extent=zoom.extent_from(buffer=0), shape_native=zoom.shape_native
621679
)
622680

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
681+
if image_plane_lines is None and source_plane_lines is None:
682+
image_plane_lines, image_plane_line_colors, source_plane_lines, source_plane_line_colors = (
683+
_compute_critical_curve_lines(tracer, grid)
684+
)
639685

640686
source_vmax = _get_source_vmax(fit)
641687

@@ -672,7 +718,8 @@ def subplot_tracer_from_fit(
672718
# Panel 2: Source Plane (No Zoom)
673719
_plot_source_plane(fit, axes_flat[2], final_plane_index, zoom_to_brightest=False,
674720
colormap=colormap, title="Source Plane (No Zoom)",
675-
lines=source_plane_lines, line_colors=source_plane_line_colors)
721+
lines=source_plane_lines, line_colors=source_plane_line_colors,
722+
vmax=source_vmax)
676723

677724
# Panel 3: Lens Image (log10)
678725
plot_array(array=lens_image, ax=axes_flat[3], title="Lens Image",

autolens/interferometer/plot/fit_interferometer_plots.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ def subplot_fit_real_space(
255255
for _ax in axes_flat:
256256
_ax.axis("off")
257257
axes_flat[0].set_title("Reconstructed Data")
258-
axes_flat[1].set_title("Source Reconstruction")
258+
axes_flat[1].set_title("Source Plane (Zoom)")
259259

260260
plt.tight_layout()
261261
save_figure(fig, path=output_path, filename="fit_real_space", format=output_format)

0 commit comments

Comments
 (0)