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,
                                                     Uniform as TemplateUniform,
                                                     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 Uniform(BaseLightProfile): """Uniform light distribution""" _units = 'per_ang' _template_class = TemplateUniform()
[docs] def surface_brightness(self, A=1.): raise ValueError("Uniform surface brightness can only be evaluated")
[docs] def evaluate_surface_brightness(self, x, y, A=1.): """Returns the surface brightness at the given position (x, y)""" return np.full_like(x, A)
[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): # NOTE: could be made a property to avoid re-doing the intanciation several times 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] ]