import sys
from typing import Optional
# Check for Python >= 3.12, "# pragma: no cover" tells coverage to
# ignore these lines as the number of accessed lines will be different
# for different Python versions
if sys.version_info >= (3, 12): # pragma: no cover
from typing import Unpack
else: # pragma: no cover
try: # pragma: no cover
from typing_extensions import Unpack
except ImportError: # pragma: no cover
pass
import lenstronomy.Util.util as util
import lenstronomy.Plots.plot_util as plot_util
from lenstronomy.Data.coord_transforms import Coordinates
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from lenstronomy.LensModel.lens_model_extensions import LensModelExtensions
from lenstronomy.Analysis.image_reconstruction import ModelBand
from lenstronomy.LensModel.lens_model import LensModel
from lenstronomy.ImSim.image_linear_solve import ImageLinearFit, ImageModel
__all__ = ["ModelBandPlot"]
[docs]
class ModelBandPlot(ModelBand):
"""Class to plot a single band given the modeling results."""
[docs]
def __init__(
self,
multi_band_list,
kwargs_model,
model,
error_map,
cov_param,
param,
kwargs_params,
likelihood_mask_list=None,
band_index=0,
fast_caustic=True,
linear_solver=True,
):
"""Initialize the model-band plotting class.
:param multi_band_list: Imaging data configuration [[kwargs_data, kwargs_psf, kwargs_numerics], [...]]
:type multi_band_list: list
:param kwargs_model: model keyword argument list for the full multi-band modeling
:type kwargs_model: dict
:param model: Of modeled image for the specified band
:type model: numpy.ndarray
:param error_map: Of size of the image, additional error in the pixels coming from PSF uncertainties
:type error_map: numpy.ndarray
:param cov_param: covariance matrix of the linear inversion
:type cov_param: numpy.ndarray
:param param: 1d numpy array of the linear coefficients of this imaging band
:type param: numpy.ndarray or list
:param kwargs_params: keyword argument of keyword argument lists of the different model components selected for
the imaging band, NOT including linear amplitudes (not required as being overwritten by the param list)
:type kwargs_params: dict
:param likelihood_mask_list: 2d numpy arrays of likelihood masks (for all bands)
:type likelihood_mask_list: list
:param band_index: Of the band to be considered in this class
:type band_index: int
:param fast_caustic: ; if True, uses fast (but less accurate) caustic calculation method
:type fast_caustic: bool
:param linear_solver: If True (default) fixes the linear amplitude parameters 'amp' (avoid sampling) such
that they get overwritten by the linear solver solution.
:type linear_solver: bool
"""
ModelBand.__init__(
self,
multi_band_list,
kwargs_model,
model,
error_map,
cov_param,
param,
kwargs_params,
image_likelihood_mask_list=likelihood_mask_list,
band_index=band_index,
linear_solver=linear_solver,
)
self._lens_model = self._bandmodel.LensModel
self._lens_model_ext = LensModelExtensions(self._lens_model)
log_model = np.log10(model)
log_model[np.isnan(log_model)] = -5
self._vmin_default = np.nanpercentile(log_model, 1)
self._vmax_default = np.nanpercentile(log_model, 99)
self._coords = self._bandmodel.Data
self._width_x, self._width_y = self._coords.width
self._data = self._coords.data
self._delta_pix = self._coords.pixel_width
self._frame_size = np.max(self._coords.width)
x_grid, y_grid = self._coords.pixel_coordinates
self._x_grid = util.image2array(x_grid)
self._y_grid = util.image2array(y_grid)
self._x_center, self._y_center = self._coords.center
self._fast_caustic = fast_caustic
self._font_size = 15
self._image_extent = [
-self._delta_pix / 2,
self._width_x - self._delta_pix / 2,
-self._delta_pix / 2,
self._width_y - self._delta_pix / 2,
]
@property
def font_size(self):
"""Default font size for all texts in the subplots.
Font size in individual subplots can be adjusted by font_size argument in the
plotting methods. Font size for different text elements can be further fine-
tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and
kwargs_coordinate_arrows arguments in the plotting methods.
"""
return self._font_size
@font_size.setter
def font_size(self, value):
"""Set default font size for all texts in the subplots.
Font size in individual subplots can be adjusted by font_size argument in the
plotting methods. Font size for different text elements can be further fine-
tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and
kwargs_coordinate_arrows arguments in the plotting methods.
"""
self._font_size = value
def _critical_curves(self):
"""Compute and cache critical curves."""
if not hasattr(self, "_ra_crit_list") or not hasattr(self, "_dec_crit_list"):
# self._ra_crit_list, self._dec_crit_list, self._ra_caustic_list, self._dec_caustic_list = self._lensModelExt.critical_curve_caustics(
# self._ra_crit_list, self._dec_crit_list, self._ra_caustic_list, self._dec_caustic_list = self._lens_model_ext.critical_curve_caustics(
# self._kwargs_lens_partial, compute_window=self._frame_size, grid_scale=self._delta_pix / 5.,
# center_x=self._x_center, center_y=self._y_center)
if self._fast_caustic:
(
self._ra_crit_list,
self._dec_crit_list,
self._ra_caustic_list,
self._dec_caustic_list,
) = self._lens_model_ext.critical_curve_caustics(
self._kwargs_lens_partial,
compute_window=self._frame_size,
grid_scale=self._delta_pix,
center_x=self._x_center,
center_y=self._y_center,
)
self._caustic_points_only = False
else:
# only supports individual points due to output of critical_curve_tiling definition
self._caustic_points_only = True
(
self._ra_crit_list,
self._dec_crit_list,
) = self._lens_model_ext.critical_curve_tiling(
self._kwargs_lens_partial,
compute_window=self._frame_size,
start_scale=self._delta_pix / 5.0,
max_order=10,
center_x=self._x_center,
center_y=self._y_center,
)
(
self._ra_caustic_list,
self._dec_caustic_list,
) = self._lens_model.ray_shooting(
self._ra_crit_list, self._dec_crit_list, self._kwargs_lens_partial
)
return self._ra_crit_list, self._dec_crit_list
def _caustics(self):
"""Compute and cache caustics."""
if not hasattr(self, "_ra_caustic_list") or not hasattr(
self, "_dec_caustic_list"
):
_, _ = self._critical_curves()
return self._ra_caustic_list, self._dec_caustic_list
[docs]
def data_plot(
self,
ax,
font_size=None,
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Plot observed imaging data.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: matplotlib axis instance
"""
if font_size is None:
font_size = self._font_size
kwargs_matshow.setdefault("cmap", "cubehelix")
kwargs_matshow.setdefault("vmin", self._vmin_default)
kwargs_matshow.setdefault("vmax", self._vmax_default)
im = ax.matshow(
np.log10(self._data),
origin="lower",
extent=self._image_extent,
**kwargs_matshow,
)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 1.0)
plot_util.show_scale_bar(
ax,
self._frame_size,
**kwargs_scale_bar,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Observed")
plot_util.show_title_text(
ax,
**kwargs_title,
)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
plot_util.show_coordinate_arrows(
ax,
self._frame_size,
self._coords,
**kwargs_coordinate_arrows,
)
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax, orientation="vertical")
kwargs_colorbar.setdefault("label", r"log$_{10}$ flux")
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
return ax
[docs]
def model_plot(
self,
ax,
image_names=False,
original_position=True,
image_name_list=None,
font_size=None,
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Plot reconstructed imaging model.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param image_names: If True, prints image names
:type image_names: bool
:param label: Label for the colorbar
:type label: str
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param original_position: If True, uses original image positions
:type original_position: bool
:param image_name_list: Names for images
:type image_name_list: list
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: matplotlib axis instance
"""
if font_size is None:
font_size = self._font_size
kwargs_matshow.setdefault("cmap", "cubehelix")
kwargs_matshow.setdefault("vmin", self._vmin_default)
kwargs_matshow.setdefault("vmax", self._vmax_default)
im = ax.matshow(
np.log10(self._model),
origin="lower",
extent=self._image_extent,
**kwargs_matshow,
)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 1.0)
plot_util.show_scale_bar(
ax,
self._frame_size,
**kwargs_scale_bar,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Reconstructed")
plot_util.show_title_text(
ax,
**kwargs_title,
)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
plot_util.show_coordinate_arrows(
ax,
self._frame_size,
self._coords,
**kwargs_coordinate_arrows,
)
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax)
kwargs_colorbar.setdefault("label", r"log$_{10}$ flux")
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
# plot_line_set(ax, self._coords, self._ra_caustic_list, self._dec_caustic_list, color='b')
# plot_line_set(ax, self._coords, self._ra_crit_list, self._dec_crit_list, color='r')
if image_names is True:
ra_image, dec_image = self._bandmodel.PointSource.image_position(
self._kwargs_ps_partial,
self._kwargs_lens_partial,
original_position=original_position,
)
plot_util.image_position_plot(
ax,
self._coords,
ra_image,
dec_image,
image_name_list=image_name_list,
plot_out_of_image=False,
)
# source_position_plot(ax, self._coords, self._kwargs_source)
[docs]
def convergence_plot(
self,
ax,
font_size=None,
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Plot lensing convergence in the data frame.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param label: Label for the colorbar
:type label: str
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: convergence plot in ax instance
"""
if font_size is None:
font_size = self._font_size
kwargs_matshow.setdefault("cmap", "gist_heat")
kappa_result = util.array2image(
self._lens_model.kappa(
self._x_grid, self._y_grid, self._kwargs_lens_partial
)
)
im = ax.matshow(
np.log10(kappa_result),
origin="lower",
extent=self._image_extent,
**kwargs_matshow,
)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 1.0)
plot_util.show_scale_bar(
ax,
self._frame_size,
**kwargs_scale_bar,
)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
plot_util.show_coordinate_arrows(
ax,
self._frame_size,
self._coords,
**kwargs_coordinate_arrows,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Convergence")
plot_util.show_title_text(
ax,
**kwargs_title,
)
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax)
kwargs_colorbar.setdefault("label", r"$\log_{10}\ \kappa$")
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
return ax
[docs]
def substructure_plot(
self,
ax,
index_macromodel,
subtract_mean=True,
font_size=None,
with_critical_curves=False,
critical_curve_color="k",
image_name_list=None,
super_sample_factor=None,
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Plots the convergence of a full lens model minus the convergence from a few
specified lens models to more clearly show the presence of substructure.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param index_macromodel: a list of indices corresponding to the lens models with convergence to be subtracted
:type index_macromodel: list
:param subtract_mean: ; displays the substructure convergence relative to the mean convergence in the frame
:type subtract_mean: bool
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param label: label for the color bar
:type label: str
:param with_critical_curves: ; plots the critical curves in the frame
:type with_critical_curves: bool
:param critical_curve_color: color of the critical curves
:type critical_curve_color: str
:param image_name_list: labels the images, default is A, B, C, ...
:type image_name_list: list
:param super_sample_factor: a integer the specifies supersampling of the coordinate grid to create the convergence map
:type super_sample_factor: int
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: matplotib axis and colorbar
"""
if font_size is None:
font_size = self._font_size
kwargs_matshow.setdefault("cmap", "coolwarm")
kwargs_lens_macro = []
lens_model_list_macro = []
profile_kwargs_list_macro = []
multi_plane = self._lens_model.multi_plane
if multi_plane:
lens_redshift_list = self._lens_model.redshift_list
lens_redshift_list_macro = []
z_source = self._lens_model.z_source
cosmo = self._lens_model.cosmo
else:
lens_redshift_list = None
lens_redshift_list_macro = None
z_source = None
cosmo = None
for idx in index_macromodel:
lens_model_list_macro.append(self._lens_model.lens_model_list[idx])
kwargs_lens_macro.append(self._kwargs_lens_partial[idx])
if multi_plane:
lens_redshift_list_macro.append(lens_redshift_list[idx])
profile_kwargs_list_macro.append(self._lens_model.profile_kwargs_list[idx])
lens_model_macro = LensModel(
lens_model_list_macro,
multi_plane=multi_plane,
lens_redshift_list=lens_redshift_list_macro,
z_source=z_source,
cosmo=cosmo,
profile_kwargs_list=profile_kwargs_list_macro,
)
if super_sample_factor is None:
x_grid = self._x_grid
y_grid = self._y_grid
else:
x_grid, y_grid = util.make_subgrid(
self._x_grid, self._y_grid, super_sample_factor
)
kappa_full = util.array2image(
self._lens_model.kappa(x_grid, y_grid, self._kwargs_lens_partial)
)
kappa_macro = util.array2image(
lens_model_macro.kappa(x_grid, y_grid, kwargs_lens_macro)
)
residual_kappa = kappa_full - kappa_macro
if subtract_mean:
mean_kappa = np.mean(residual_kappa)
residual_kappa -= mean_kappa
else:
pass
kwargs_matshow.setdefault("vmin", -0.05)
kwargs_matshow.setdefault("vmax", 0.05)
alpha = 1.0
im = ax.imshow(
residual_kappa,
origin="lower",
extent=self._image_extent,
alpha=alpha,
**kwargs_matshow,
)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 1.0)
kwargs_scale_bar.setdefault("color", "k")
plot_util.show_scale_bar(
ax,
self._frame_size,
**kwargs_scale_bar,
)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
kwargs_coordinate_arrows.setdefault("arrow_color_north", "k")
kwargs_coordinate_arrows.setdefault("arrow_color_east", "k")
plot_util.show_coordinate_arrows(
ax,
self._frame_size,
self._coords,
**kwargs_coordinate_arrows,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Substructure convergence")
kwargs_title.setdefault("color", "k")
kwargs_title.setdefault("backgroundcolor", "w")
plot_util.show_title_text(
ax,
**kwargs_title,
)
if with_critical_curves is True:
ra_crit_list, dec_crit_list = self._critical_curves()
plot_util.plot_line_set(
ax,
self._coords,
ra_crit_list,
dec_crit_list,
color=critical_curve_color,
points_only=self._caustic_points_only,
)
ra_image, dec_image = self._bandmodel.PointSource.image_position(
self._kwargs_ps_partial, self._kwargs_lens_partial
)
plot_util.image_position_plot(
ax,
self._coords,
ra_image,
dec_image,
color="k",
image_name_list=image_name_list,
plot_out_of_image=False,
)
cb = None
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
if subtract_mean:
label = r"$\kappa_{\rm{sub}} - \langle \kappa_{\rm{sub}} \rangle$"
else:
label = r"$\kappa - \kappa_{\rm{macro}}$"
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax)
kwargs_colorbar.setdefault("label", label)
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
return ax, cb
[docs]
def normalized_residual_plot(
self,
ax,
font_size=None,
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Plot normalized residuals between data and model.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param label: label for the color bar
:type label: str
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: matplotlib axis instance
"""
if font_size is None:
font_size = self._font_size
kwargs_matshow.setdefault("cmap", "RdBu_r")
kwargs_matshow.setdefault("vmin", -5)
kwargs_matshow.setdefault("vmax", 5)
im = ax.matshow(
self._norm_residuals,
extent=self._image_extent,
origin="lower",
**kwargs_matshow,
)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 1.0)
kwargs_scale_bar.setdefault("color", "k")
plot_util.show_scale_bar(
ax,
self._frame_size,
**kwargs_scale_bar,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Normalized Residuals")
kwargs_title.setdefault("color", "k")
kwargs_title.setdefault("backgroundcolor", "w")
plot_util.show_title_text(
ax,
**kwargs_title,
)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
kwargs_coordinate_arrows.setdefault("arrow_color_north", "k")
kwargs_coordinate_arrows.setdefault("arrow_color_east", "k")
plot_util.show_coordinate_arrows(
ax,
self._frame_size,
self._coords,
**kwargs_coordinate_arrows,
)
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax)
kwargs_colorbar.setdefault(
"label", r"(f$_{\rm data}$ - f$_{\rm model}$)/$\sigma$"
)
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
return ax
[docs]
def absolute_residual_plot(
self,
ax,
font_size=None,
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Plot absolute residuals between data and model.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param label: label for the color bar
:type label: str
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: matplotlib axis instance
"""
if font_size is None:
font_size = self._font_size
kwargs_matshow.setdefault("cmap", "RdBu_r")
im = ax.matshow(
self._data - self._model,
extent=self._image_extent,
origin="lower",
**kwargs_matshow,
)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 1.0)
kwargs_scale_bar.setdefault("color", "k")
plot_util.show_scale_bar(
ax,
self._frame_size,
**kwargs_scale_bar,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Residuals")
kwargs_title.setdefault("color", "k")
kwargs_title.setdefault("backgroundcolor", "w")
plot_util.show_title_text(
ax,
**kwargs_title,
)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
kwargs_coordinate_arrows.setdefault("arrow_color_north", "k")
kwargs_coordinate_arrows.setdefault("arrow_color_east", "k")
plot_util.show_coordinate_arrows(
ax,
self._frame_size,
self._coords,
**kwargs_coordinate_arrows,
)
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax)
kwargs_colorbar.setdefault("label", r"(f$_{\rm data}$-f$_{\rm model}$)")
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
return ax
[docs]
def source(self, num_pix, delta_pix, center=None, image_orientation=True):
"""Compute source surface brightness on a source grid.
:param num_pix: number of pixels per axes
:type num_pix: int
:param delta_pix: pixel size
:type delta_pix: float
:param image_orientation: If True, uses frame in orientation of the image,
:type image_orientation: bool otherwise in RA-DEC coordinates
:return: 2d surface brightness grid of the reconstructed source and
Coordinates() instance of source grid
"""
if image_orientation is True:
transform_pix2coord = (
self._coords.transform_pix2angle * delta_pix / self._delta_pix
)
x_grid_source, y_grid_source = util.make_grid_transformed(
num_pix, transform_pix2angle=transform_pix2coord
)
ra_at_xy_0, dec_at_xy_0 = x_grid_source[0], y_grid_source[0]
else:
(
x_grid_source,
y_grid_source,
ra_at_xy_0,
dec_at_xy_0,
x_at_radec_0,
y_at_radec_0,
transform_pix2coord,
transform_coord2pix,
) = util.make_grid_with_coordtransform(num_pix, delta_pix)
center_x = 0
center_y = 0
if center is not None:
center_x, center_y = center[0], center[1]
elif len(self._kwargs_source_partial) > 0:
center_x = self._kwargs_source_partial[0]["center_x"]
center_y = self._kwargs_source_partial[0]["center_y"]
x_grid_source += center_x
y_grid_source += center_y
coords_source = Coordinates(
transform_pix2angle=transform_pix2coord,
ra_at_xy_0=ra_at_xy_0 + center_x,
dec_at_xy_0=dec_at_xy_0 + center_y,
)
source = self._bandmodel.SourceModel.surface_brightness(
x_grid_source, y_grid_source, self._kwargs_source_partial
)
source = util.array2image(source) * delta_pix**2
return source, coords_source
[docs]
def source_plot(
self,
ax,
num_pix,
delta_pix_source,
center=None,
font_size=None,
plot_scale="log",
point_source_position=True,
kwargs_caustics: Optional[plot_util.CausticKwargs] = {},
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Plot reconstructed source brightness.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param num_pix: number of pixels in plot per axis
:type num_pix: int
:param delta_pix_source: pixel spacing in the source resolution illustrated in
plot
:type delta_pix_source: float
:param center: [center_x, center_y], if specified, uses this as the center
:type center: list or None
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param plot_scale: Log or linear, scale of surface brightness plot
:type plot_scale: str
:param label: Label for the colorbar
:type label: str
:param point_source_position: If True, plots a point at the position of
the point source
:type point_source_position: bool
:param kwargs_caustics: keyword arguments for caustic plotting, see :class:`~lenstronomy.Plots.plot_util.CausticKwargs`. Set to None to exclude this element from the plot.
:type kwargs_caustics: dict
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: matplotlib axis instance
"""
if font_size is None:
font_size = self._font_size
d_s = num_pix * delta_pix_source
source, coords_source = self.source(num_pix, delta_pix_source, center=center)
if plot_scale == "log":
source_scale = np.log10(source)
elif plot_scale == "linear":
source_scale = source
else:
raise ValueError(
'variable plot_scale needs to be "log" or "linear", not %s.'
% plot_scale
)
kwargs_matshow.setdefault("cmap", "cubehelix")
if plot_scale == "log":
kwargs_matshow.setdefault("vmin", self._vmin_default)
kwargs_matshow.setdefault("vmax", self._vmax_default)
im = ax.matshow(
source_scale,
origin="lower",
extent=[
-delta_pix_source / 2,
d_s - delta_pix_source / 2,
-delta_pix_source / 2,
d_s - delta_pix_source / 2,
],
**kwargs_matshow,
) # source
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax)
kwargs_colorbar.setdefault("label", r"log$_{10}$ flux")
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
if kwargs_caustics is not None:
kwargs_caustics = dict(kwargs_caustics)
kwargs_caustics.setdefault("color", "yellow")
ra_caustic_list, dec_caustic_list = self._caustics()
plot_util.plot_line_set(
ax,
coords_source,
ra_caustic_list,
dec_caustic_list,
points_only=self._caustic_points_only,
**kwargs_caustics,
)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 0.1)
if kwargs_scale_bar.get("scale_size", 1.0) > 0:
plot_util.show_scale_bar(ax, d_s, **kwargs_scale_bar)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
kwargs_coordinate_arrows.setdefault("font_size", font_size)
kwargs_coordinate_arrows.setdefault("arrow_color_north", "w")
kwargs_coordinate_arrows.setdefault("arrow_color_east", "w")
plot_util.show_coordinate_arrows(
ax,
self._frame_size,
self._coords,
**kwargs_coordinate_arrows,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Reconstructed source")
kwargs_title.setdefault("flipped", False)
kwargs_title.setdefault("font_size", font_size)
plot_util.show_title_text(ax, **kwargs_title)
if point_source_position is True:
ra_source, dec_source = self._bandmodel.PointSource.source_position(
self._kwargs_ps_partial, self._kwargs_lens
)
plot_util.source_position_plot(ax, coords_source, ra_source, dec_source)
return ax
[docs]
def error_map_source_plot(
self,
ax,
num_pix,
delta_pix_source,
font_size=None,
point_source_position=True,
kwargs_caustics: Optional[plot_util.CausticKwargs] = {},
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Plots the uncertainty in the surface brightness in the source from the linear
inversion by taking the diagonal elements of the covariance matrix of the
inversion of the basis set to be propagated to the source plane. #TODO
illustration of the uncertainties in real space with the full covariance matrix
is subtle. # The best way is probably to draw realizations from the covariance
matrix.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param num_pix: number of pixels in plot per axis
:type num_pix: int
:param delta_pix_source: pixel spacing in the source resolution illustrated in
plot
:type delta_pix_source: float
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param point_source_position: If True, plots a point at the position of
the point source
:type point_source_position: bool
:param kwargs_caustics: keyword arguments for caustic plotting. Set to None to exclude this element from the plot. Set to None to exclude this element from the plot.
:type kwargs_caustics: dict
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: plot of source surface brightness errors in the reconstruction on the
axis instance
"""
if font_size is None:
font_size = self._font_size
x_grid_source, y_grid_source = util.make_grid_transformed(
num_pix,
self._coords.transform_pix2angle * delta_pix_source / self._delta_pix,
)
x_center = self._kwargs_source_partial[0]["center_x"]
y_center = self._kwargs_source_partial[0]["center_y"]
x_grid_source += x_center
y_grid_source += y_center
coords_source = Coordinates(
self._coords.transform_pix2angle * delta_pix_source / self._delta_pix,
ra_at_xy_0=x_grid_source[0],
dec_at_xy_0=y_grid_source[0],
)
error_map_source = ImageLinearFit.error_map_source(
self._bandmodel,
self._kwargs_source_partial,
x_grid_source,
y_grid_source,
self._cov_param,
)
error_map_source = util.array2image(error_map_source)
d_s = num_pix * delta_pix_source
kwargs_matshow.setdefault("cmap", "CMRmap")
im = ax.matshow(
error_map_source,
origin="lower",
extent=[
-delta_pix_source / 2,
d_s - delta_pix_source / 2,
-delta_pix_source / 2,
d_s - delta_pix_source / 2,
],
**kwargs_matshow,
) # source
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax)
kwargs_colorbar.setdefault("label", r"log$_{10}$ flux")
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
if kwargs_caustics is not None:
ra_caustic_list, dec_caustic_list = self._caustics()
kwargs_caustics = dict(kwargs_caustics)
kwargs_caustics = dict(kwargs_caustics)
kwargs_caustics.setdefault("color", "b")
plot_util.plot_line_set(
ax,
coords_source,
ra_caustic_list,
dec_caustic_list,
points_only=self._caustic_points_only,
**kwargs_caustics,
)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 0.1)
plot_util.show_scale_bar(
ax,
d_s,
**kwargs_scale_bar,
)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
plot_util.show_coordinate_arrows(
ax,
d_s,
coords_source,
**kwargs_coordinate_arrows,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Error map in source")
plot_util.show_title_text(
ax,
flipped=False,
**kwargs_title,
)
if point_source_position is True:
ra_source, dec_source = self._bandmodel.PointSource.source_position(
self._kwargs_ps_partial, self._kwargs_lens
)
plot_util.source_position_plot(ax, coords_source, ra_source, dec_source)
return ax
[docs]
def magnification_plot(
self,
ax,
image_name_list=None,
font_size=None,
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Plot magnification map in the data frame.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param image_name_list: Strings for names of the images in the same
order as the positions
:type image_name_list: list
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param label: Label for the colorbar
:type label: str
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: matplotlib axis instance
"""
if font_size is None:
font_size = self._font_size
kwargs_matshow.setdefault("cmap", "RdYlBu_r")
kwargs_matshow.setdefault("vmin", -10)
kwargs_matshow.setdefault("vmax", 10)
kwargs_matshow.setdefault("alpha", 0.5)
mag_result = util.array2image(
self._lens_model.magnification(
self._x_grid, self._y_grid, self._kwargs_lens_partial
)
)
im = ax.matshow(
mag_result,
origin="lower",
extent=self._image_extent,
**kwargs_matshow,
)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 1.0)
kwargs_scale_bar.setdefault("color", "k")
plot_util.show_scale_bar(
ax,
self._frame_size,
**kwargs_scale_bar,
)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
kwargs_coordinate_arrows.setdefault("arrow_color_north", "k")
kwargs_coordinate_arrows.setdefault("arrow_color_east", "k")
plot_util.show_coordinate_arrows(
ax,
self._frame_size,
self._coords,
**kwargs_coordinate_arrows,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Magnification model")
kwargs_title.setdefault("color", "k")
kwargs_title.setdefault("backgroundcolor", "w")
plot_util.show_title_text(
ax,
**kwargs_title,
)
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax)
kwargs_colorbar.setdefault("label", r"$\det\ (\mathsf{A}^{-1})$")
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
ra_image, dec_image = self._bandmodel.PointSource.image_position(
self._kwargs_ps_partial, self._kwargs_lens_partial
)
plot_util.image_position_plot(
ax,
self._coords,
ra_image,
dec_image,
color="k",
image_name_list=image_name_list,
plot_out_of_image=False,
)
return ax
[docs]
def deflection_plot(
self,
ax,
axis=0,
image_name_list=None,
font_size=None,
kwargs_caustics: Optional[plot_util.CausticCriticalKwargs] = {},
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Plot deflection-angle map in the data frame.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param axis: 0 or 1, specifies the deflection angle axis to be plotted
:type axis: int
:param image_name_list: Strings for names of the images
:type image_name_list: list
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param label: Label for the colorbar
:type label: str
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_caustics: keyword arguments for caustic and critical-curve plotting, see :class:`~lenstronomy.Plots.plot_util.CausticCriticalKwargs`. Set to None to exclude this element from the plot. The dictionary takes ``"critical_curve_color"`` as an additional optional key to specify the color of the critical curves.
:type kwargs_caustics: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: matplotlib axis instance
"""
if font_size is None:
font_size = self._font_size
alpha1, alpha2 = self._lens_model.alpha(
self._x_grid, self._y_grid, self._kwargs_lens_partial
)
alpha1 = util.array2image(alpha1)
alpha2 = util.array2image(alpha2)
if axis == 0:
alpha = alpha1
else:
alpha = alpha2
kwargs_matshow.setdefault("cmap", "PiYG")
im = ax.matshow(
alpha,
origin="lower",
extent=self._image_extent,
alpha=0.5,
**kwargs_matshow,
)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 1.0)
kwargs_scale_bar.setdefault("color", "k")
plot_util.show_scale_bar(
ax,
self._frame_size,
**kwargs_scale_bar,
)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
kwargs_coordinate_arrows.setdefault("arrow_color_north", "k")
kwargs_coordinate_arrows.setdefault("arrow_color_east", "k")
plot_util.show_coordinate_arrows(
ax,
self._frame_size,
self._coords,
**kwargs_coordinate_arrows,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Deflection model")
kwargs_title.setdefault("color", "k")
kwargs_title.setdefault("backgroundcolor", "w")
plot_util.show_title_text(
ax,
**kwargs_title,
)
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax)
kwargs_colorbar.setdefault("label", r"arcsec")
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
if kwargs_caustics is not None:
ra_crit_list, dec_crit_list = self._critical_curves()
ra_caustic_list, dec_caustic_list = self._caustics()
kwargs_caustics = dict(kwargs_caustics)
kwargs_caustics.setdefault("color", "yellow")
critical_curve_color = kwargs_caustics.pop("critical_curve_color", "red")
plot_util.plot_line_set(
ax,
self._coords,
ra_caustic_list,
dec_caustic_list,
points_only=self._caustic_points_only,
**kwargs_caustics,
)
kwargs_caustics.setdefault("color", critical_curve_color)
plot_util.plot_line_set(
ax,
self._coords,
ra_crit_list,
dec_crit_list,
points_only=self._caustic_points_only,
**kwargs_caustics,
)
ra_image, dec_image = self._bandmodel.PointSource.image_position(
self._kwargs_ps_partial, self._kwargs_lens_partial
)
plot_util.image_position_plot(
ax,
self._coords,
ra_image,
dec_image,
image_name_list=image_name_list,
plot_out_of_image=False,
)
return ax
[docs]
def decomposition_plot(
self,
ax,
unconvolved=False,
point_source_add=False,
font_size=None,
source_add=False,
lens_light_add=False,
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Make a plot displaying all or a subset of light components.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param unconvolved: If True, does not perform PSF convolution on the image
:type unconvolved: bool
:param point_source_add: If True, includes the lensed point source(s) in
the plot
:type point_source_add: bool
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param source_add: If True, includes the lensed image of the source in the
plot
:type source_add: bool
:param lens_light_add: If True, includes the lens light in the plot
from the plot
:type lens_light_add: bool
:param label: Label for the colorbar
:type label: str
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: the instance of matplotlib.axes.Axes
"""
if font_size is None:
font_size = self._font_size
model = ImageModel.image(
self._bandmodel,
self._kwargs_lens_partial,
self._kwargs_source_partial,
self._kwargs_lens_light_partial,
self._kwargs_ps_partial,
kwargs_special=self._kwargs_special_partial,
unconvolved=unconvolved,
source_add=source_add,
lens_light_add=lens_light_add,
point_source_add=point_source_add,
)
kwargs_matshow.setdefault("cmap", "cubehelix")
kwargs_matshow.setdefault("vmin", self._vmin_default)
kwargs_matshow.setdefault("vmax", self._vmax_default)
im = ax.matshow(
np.log10(model),
origin="lower",
extent=self._image_extent,
**kwargs_matshow,
)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 1.0)
plot_util.show_scale_bar(
ax,
self._frame_size,
**kwargs_scale_bar,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Reconstructed")
plot_util.show_title_text(
ax,
**kwargs_title,
)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
plot_util.show_coordinate_arrows(
ax,
self._frame_size,
self._coords,
**kwargs_coordinate_arrows,
)
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax)
kwargs_colorbar.setdefault("label", r"log$_{10}$ flux")
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
return ax
[docs]
def subtract_from_data_plot(
self,
ax,
subtract_point_source=False,
subtract_source=False,
subtract_lens_light=False,
font_size=None,
kwargs_colorbar: Optional[plot_util.ColorBarKwargs] = {},
kwargs_title: Optional[plot_util.TitleKwargs] = {},
kwargs_scale_bar: Optional[plot_util.ScaleBarKwargs] = {},
kwargs_coordinate_arrows: Optional[plot_util.CoordArrowKwargs] = {},
**kwargs_matshow: "Unpack[plot_util.MatshowKwargs]",
):
"""Plot data after subtracting selected model components.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param subtract_point_source: If True, subtracts the lensed point source(s) from the data
in the plot
:type subtract_point_source: bool
:param subtract_source: If True, subtracts the lensed image of the source from the data in the plot
:type subtract_source: bool
:param subtract_lens_light: If True, subtracts the lens light from the data
in the plot
:type subtract_lens_light: bool
:param font_size: Font size to override the class-level default. Font size for different text elements can be further fine-tuned by kwargs_colorbar, kwargs_title, kwargs_scale_bar, and kwargs_coordinate_arrows arguments in the plotting methods.
:type font_size: int
:param kwargs_colorbar: keyword arguments for the colorbar, see :class:`~lenstronomy.Plots.plot_util.ColorBarKwargs`
:type kwargs_colorbar: dict
:param kwargs_title: keyword arguments for the title, see :class:`~lenstronomy.Plots.plot_util.TitleKwargs`. Set to None to exclude this element from the plot.
:type kwargs_title: dict
:param kwargs_scale_bar: keyword arguments for the scale bar, see :class:`~lenstronomy.Plots.plot_util.ScaleBarKwargs`. Set to None to exclude this element from the plot.
:type kwargs_scale_bar: dict
:param kwargs_coordinate_arrows: keyword arguments for coordinate arrows, see :class:`~lenstronomy.Plots.plot_util.CoordArrowKwargs`. Set to None to exclude this element from the plot.
:type kwargs_coordinate_arrows: dict
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: the instance of matplotlib.axes.Axes
"""
if font_size is None:
font_size = self._font_size
model = ImageModel.image(
self._bandmodel,
self._kwargs_lens_partial,
self._kwargs_source_partial,
self._kwargs_lens_light_partial,
self._kwargs_ps_partial,
kwargs_special=self._kwargs_special_partial,
unconvolved=False,
source_add=subtract_source,
lens_light_add=subtract_lens_light,
point_source_add=subtract_point_source,
)
kwargs_matshow.setdefault("cmap", "cubehelix")
kwargs_matshow.setdefault("vmin", self._vmin_default)
kwargs_matshow.setdefault("vmax", self._vmax_default)
im = ax.matshow(
np.log10(self._data - model),
origin="lower",
extent=self._image_extent,
**kwargs_matshow,
)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.autoscale(False)
if kwargs_scale_bar is not None:
kwargs_scale_bar = dict(kwargs_scale_bar)
kwargs_scale_bar.setdefault("scale_size", 1.0)
plot_util.show_scale_bar(
ax,
self._frame_size,
**kwargs_scale_bar,
)
if kwargs_title is not None:
kwargs_title = dict(kwargs_title)
kwargs_title.setdefault("text", "Subtracted")
plot_util.show_title_text(
ax,
**kwargs_title,
)
if kwargs_coordinate_arrows is not None:
kwargs_coordinate_arrows = dict(kwargs_coordinate_arrows)
plot_util.show_coordinate_arrows(
ax,
self._frame_size,
self._coords,
**kwargs_coordinate_arrows,
)
if kwargs_colorbar is not None:
kwargs_colorbar = dict(kwargs_colorbar)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cb = plt.colorbar(im, cax=cax)
kwargs_colorbar.setdefault("label", r"log$_{10}$ flux")
plot_util.show_colorbar(
cb,
font_size=font_size,
**kwargs_colorbar,
)
return ax
[docs]
def plot_main(self, kwargs_caustics=None):
"""Print the main plots together in a joint frame.
:return:
"""
f, axes = plt.subplots(2, 3, figsize=(16, 8))
self.data_plot(ax=axes[0, 0])
self.model_plot(ax=axes[0, 1], image_names=True)
self.normalized_residual_plot(ax=axes[0, 2])
self.source_plot(
ax=axes[1, 0],
delta_pix_source=0.01,
num_pix=100,
kwargs_caustics=kwargs_caustics,
)
self.convergence_plot(ax=axes[1, 1])
self.magnification_plot(ax=axes[1, 2])
f.tight_layout()
f.subplots_adjust(
left=None, bottom=None, right=None, top=None, wspace=0.0, hspace=0.05
)
return f, axes
[docs]
def plot_separate(self):
"""Plot the different model components separately.
:return:
"""
f, axes = plt.subplots(2, 3, figsize=(16, 8))
self.decomposition_plot(
ax=axes[0, 0],
kwargs_title={"text": "Lens light"},
lens_light_add=True,
unconvolved=True,
)
self.decomposition_plot(
ax=axes[1, 0],
kwargs_title={"text": "Lens light convolved"},
lens_light_add=True,
)
self.decomposition_plot(
ax=axes[0, 1],
kwargs_title={"text": "Source light"},
source_add=True,
unconvolved=True,
)
self.decomposition_plot(
ax=axes[1, 1],
kwargs_title={"text": "Source light convolved"},
source_add=True,
)
self.decomposition_plot(
ax=axes[0, 2],
kwargs_title={"text": "All components"},
source_add=True,
lens_light_add=True,
unconvolved=True,
)
self.decomposition_plot(
ax=axes[1, 2],
kwargs_title={"text": "All components convolved"},
source_add=True,
lens_light_add=True,
point_source_add=True,
)
f.tight_layout()
f.subplots_adjust(
left=None, bottom=None, right=None, top=None, wspace=0.0, hspace=0.05
)
return f, axes
[docs]
def plot_subtract_from_data_all(self):
"""Subtract model components from data.
:return:
"""
f, axes = plt.subplots(2, 3, figsize=(16, 8))
self.subtract_from_data_plot(ax=axes[0, 0], kwargs_title={"text": "Data"})
self.subtract_from_data_plot(
ax=axes[0, 1],
kwargs_title={"text": "Data - Point Source"},
subtract_point_source=True,
)
self.subtract_from_data_plot(
ax=axes[0, 2],
kwargs_title={"text": "Data - Lens Light"},
subtract_lens_light=True,
)
self.subtract_from_data_plot(
ax=axes[1, 0],
kwargs_title={"text": "Data - Source Light"},
subtract_source=True,
)
self.subtract_from_data_plot(
ax=axes[1, 1],
kwargs_title={"text": "Data - Source Light - Point Source"},
subtract_source=True,
subtract_point_source=True,
)
self.subtract_from_data_plot(
ax=axes[1, 2],
kwargs_title={"text": "Data - Lens Light - Point Source"},
subtract_lens_light=True,
subtract_point_source=True,
)
f.tight_layout()
f.subplots_adjust(
left=None, bottom=None, right=None, top=None, wspace=0.0, hspace=0.05
)
return f, axes
[docs]
def plot_extinction_map(
self, ax, **kwargs_matshow: "Unpack[plot_util.MatshowKwargs]"
):
"""Plot differential extinction map.
:param ax: Matplotlib axes instance
:type ax: matplotlib.axes.Axes
:param kwargs_matshow: keyword arguments passed to :func:`matplotlib.pyplot.matshow`
:type kwargs_matshow: dict
:return: matplotlib axis instance
"""
model = ImageModel.extinction_map(
self._bandmodel,
self._kwargs_extinction_partial,
self._kwargs_special_partial,
)
kwargs_matshow.setdefault("cmap", "afmhot")
_ = ax.matshow(
model,
origin="lower",
extent=self._image_extent,
**kwargs_matshow,
)
return ax