Source code for coolest.api.profiles.light

__author__ = 'aymgal', 'lynevdv', 'Giorgos Vernardos'


import numpy as np
from scipy import interpolate

from coolest.template.classes.profiles.light import (Sersic as TemplateSersic,
                                                     Shapelets as TemplateShapelets,
                                                     PixelatedRegularGrid as TemplatePixelatedRegularGrid,
                                                     IrregularGrid as TemplateIrregularGrid)
from coolest.api.profiles import util


[docs] class BaseLightProfile(object): _template_class = None _units = None
[docs] def surface_brightness(self, **params): raise NotImplementedError(f"The method surface_brightness() is not defined " f"for profile '{self.__class__.__name__}'")
[docs] def evaluate_surface_brightness(self, **params): raise NotImplementedError(f"The method evaluate_surface_brightness() is not defined " f"for profile '{self.__class__.__name__}'")
[docs] def get_extent(self): return None
[docs] def get_coordinates(self): return None
@property
[docs] def units(self): if not hasattr(self, '_units'): raise ValueError(f"Units for profile {self.__class__.__name__} must be specified") if self._units not in ('per_pix', 'per_ang'): raise ValueError(f"Unsupported units type {self._units}") return self._units
@property
[docs] def template_class(self): if self._template_class is None: raise RuntimeError("No template class has been set by light profile class") return self._template_class
@property
[docs] def type(self): return self.template_class.type
@property
[docs] def parameter_names(self): return list(self.template_class.parameters.keys())
[docs] class Sersic(BaseLightProfile): """Elliptical Sersic""" _units = 'per_ang' _template_class = TemplateSersic()
[docs] def surface_brightness(self, I_eff=1., theta_eff=2., n=4., phi=0., q=1., center_x=0., center_y=0.): raise ValueError("Sersic surface brightness can only be evaluated")
[docs] def evaluate_surface_brightness(self, x, y, I_eff=1., theta_eff=2., n=4., phi=0., q=1., center_x=0., center_y=0.): """Returns the surface brightness at the given position (x, y)""" phi_ = util.eastofnorth2normalradians(phi) x_t, y_t = util.shift_rotate_elliptical(x, y, phi_, q, center_x, center_y) bn = 1.9992*n - 0.3271 return I_eff * np.exp( - bn * ( (np.sqrt(x_t**2+y_t**2) / theta_eff )**(1./n) -1. ) )
[docs] class Shapelets(BaseLightProfile): """Elliptical Sersic The implementation uses functions from `lenstronomy` (:cite:t:`lenstronomy2018`, :cite:t:`lenstronomy2021`), based on the developments of :cite:t:`Refregier2003`. """ _units = 'per_ang' _template_class = TemplateShapelets() def __init__(self): from lenstronomy.LightModel.Profiles.shapelets import ShapeletSet self._backend = ShapeletSet()
[docs] def surface_brightness(self, amps=0, n_max=0, beta=0, center_x=0, center_y=0): raise ValueError("Surface brightness of a set of shapelets can only be evaluated")
[docs] def evaluate_surface_brightness(self, x, y, amps=0, n_max=0, beta=0, center_x=0, center_y=0): """Returns the surface brightness at the given position (x, y)""" x_, y_ = x.flatten(), y.flatten() flux = self._backend.function(x_, y_, amps, n_max, beta, center_x=center_x, center_y=center_y) return flux.reshape(*x.shape)
[docs] class PixelatedRegularGrid(BaseLightProfile): """Pixelated profile on a regular grid""" _units = 'per_pix' _template_class = TemplatePixelatedRegularGrid() def __init__(self, field_of_view_x, field_of_view_y, num_pix_x, num_pix_y, interpolation_method='cubic'): if num_pix_x == 0 or num_pix_y == 0: raise ValueError("Light profile defined on regular grid has zero pixels") self._fov_x = field_of_view_x self._fov_y = field_of_view_y self._nx = num_pix_x self._ny = num_pix_y self._shape = (num_pix_x, num_pix_y) self._pix_scl_x = np.abs(self._fov_x[0] - self._fov_x[1]) / self._nx self._pix_scl_y = np.abs(self._fov_y[0] - self._fov_y[1]) / self._ny self._interp_method = interpolation_method
[docs] def surface_brightness(self, pixels=None): """Returns the surface brightness pixels""" if pixels is None: return np.zeros(self._shape) return pixels
[docs] def evaluate_surface_brightness(self, x, y, pixels=None): coordinates = self.get_coordinates() points = coordinates.pixel_axes interp = util.CartesianGridInterpolator(points, pixels, method=self._interp_method) points_eval = np.array([y.ravel(), x.ravel()]).T pixels_eval = interp(points_eval).reshape(*x.shape) return pixels_eval
[docs] def get_extent(self): coordinates = self.get_coordinates() return coordinates.plt_extent
[docs] def get_coordinates(self): from coolest.api.util import get_coordinates_from_regular_grid return get_coordinates_from_regular_grid(self._fov_x, self._fov_y, self._nx, self._ny)
[docs] class IrregularGrid(BaseLightProfile): """Pixelated profile on an irregular grid of points {x, y, z}""" _units = 'per_pix' _template_class = TemplateIrregularGrid() def __init__(self, field_of_view_x, field_of_view_y, num_pix, interpolation_method='cubic'): self._fov_x = field_of_view_x self._fov_y = field_of_view_y self._n = num_pix self._interp_method = interpolation_method
[docs] def surface_brightness(self, x=None, y=None, z=None): """Returns the surface brightness pixels""" if x is None or y is None or z is None: return np.zeros(self._n), np.zeros(self._n), np.zeros(self._n) return x, y, z
[docs] def evaluate_surface_brightness(self, x_eval, y_eval, x=None, y=None, z=None): z_eval = interpolate.griddata((x, y), z, (x_eval, y_eval), method=self._interp_method) return z_eval
[docs] def get_extent(self): return [ self._fov_x[0], self._fov_x[1], self._fov_y[0], self._fov_y[1] ]