1+ import logging
12import matplotlib .pyplot as plt
23import numpy as np
34from typing import Optional , List
89from autoarray .plot .array import plot_array , _zoom_array_2d
910from autoarray .plot .utils import save_figure , hide_unused_axes , conf_subplot_figsize
1011from 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
1114from autogalaxy .plot .plot_utils import _critical_curves_from , _caustics_from
1215
16+ logger = logging .getLogger (__name__ )
17+
1318
1419def _get_source_vmax (fit ):
1520 """
@@ -39,7 +44,8 @@ def _get_source_vmax(fit):
3944
4045
4146def _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
93120def 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