Source code for lenstronomy.LightModel.linear_basis

__author__ = "sibirrer"

# this file contains a class which describes the surface brightness of the light models

import numpy as np
from lenstronomy.LightModel.light_model_base import LightModelBase

__all__ = ["LinearBasis"]


[docs] class LinearBasis(LightModelBase): """Class to handle source and lens light models."""
[docs] def __init__(self, **kwargs): """ :param kwargs: keyword arguments for LightModelBase class """ super(LinearBasis, self).__init__(**kwargs)
[docs] def functions_split(self, x, y, kwargs_list, k=None): """Split model in different components. :param x: coordinate in units of arcsec relative to the center of the image :param y: coordinate in units of arcsec relative to the center of the image :param kwargs_list: keyword argument list of light profile :param k: integer or list of integers for selecting subsets of light profiles """ response = [] n = 0 bool_list = self._bool_list(k=k) for i, model in enumerate(self.profile_type_list): if bool_list[i] is True: if model in [ "CHAMELEON", "CORE_SERSIC", "DOUBLE_CHAMELEON", "ELLIPSOID", "GAUSSIAN", "GAUSSIAN_ELLIPSE", "HERNQUIST", "HERNQUIST_ELLIPSE", "INTERPOL", "LINEAR", "LINEAR_ELLIPSE", "LINE_PROFILE", "NIE", "PJAFFE", "PJAFFE_ELLIPSE", "PL_SERSIC", "POWER_LAW", "SERSIC", "SERSIC_ELLIPSE", "SERSIC_ELLIPSE_FLEXION", "SERSIC_ELLIPSE_Q_PHI", "TRIPLE_CHAMELEON", "UNIFORM", ]: kwargs_new = kwargs_list[i].copy() new = {"amp": 1} kwargs_new.update(new) response += [self.func_list[i].function(x, y, **kwargs_new)] n += 1 elif model in ["MULTI_GAUSSIAN", "MULTI_GAUSSIAN_ELLIPSE"]: num = len(kwargs_list[i]["sigma"]) new = {"amp": np.ones(num)} kwargs_new = kwargs_list[i].copy() kwargs_new.update(new) response += self.func_list[i].function_split(x, y, **kwargs_new) n += num elif model in ["MGE_SET", "MGE_SET_ELLIPSE"]: num = self.func_list[i].num_linear new = {"amp": np.ones(num)} kwargs_new = kwargs_list[i].copy() kwargs_new.update(new) response += self.func_list[i].function_split(x, y, **kwargs_new) n += num elif model in [ "SHAPELETS", "SHAPELETS_POLAR", "SHAPELETS_POLAR_EXP", "SHAPELETS_ELLIPSE", ]: kwargs = kwargs_list[i] n_max = kwargs["n_max"] if model in ["SHAPELETS_POLAR_EXP"]: num_param = int((n_max + 1) ** 2) else: num_param = int((n_max + 1) * (n_max + 2) / 2) new = {"amp": np.ones(num_param)} kwargs_new = kwargs_list[i].copy() kwargs_new.update(new) response += self.func_list[i].function_split(x, y, **kwargs_new) n += num_param elif model in ["SLIT_STARLETS", "SLIT_STARLETS_GEN2"]: raise ValueError( "'{}' model does not support function split".format(model) ) else: raise ValueError("model type %s not valid!" % model) return response, n
[docs] def num_param_linear(self, kwargs_list, list_return=False): """ :param kwargs_list: list of keyword arguments of the light profiles :param list_return: bool, if True returns list of individual number of parameters :return: number of linear basis set coefficients """ n_list = self.num_param_linear_list(kwargs_list) if not list_return: return np.sum(n_list) return n_list
[docs] def num_param_linear_list(self, kwargs_list): """Returns the list (in order of the light profiles) of the number of linear components per model. :param kwargs_list: list of keyword arguments of the light profiles :return: number of linear basis set coefficients """ n_list = [] for i, model in enumerate(self.profile_type_list): if model in [ "CHAMELEON", "CORE_SERSIC", "DOUBLE_CHAMELEON", "ELLIPSOID", "GAUSSIAN", "GAUSSIAN_ELLIPSE", "HERNQUIST", "HERNQUIST_ELLIPSE", "INTERPOL", "LINEAR", "LINEAR_ELLIPSE", "LINE_PROFILE", "NIE", "PJAFFE", "PJAFFE_ELLIPSE", "PL_SERSIC", "POWER_LAW", "SERSIC", "SERSIC_ELLIPSE", "SERSIC_ELLIPSE_FLEXION", "TRIPLE_CHAMELEON", "UNIFORM", ]: n_list += [1] elif model in ["MULTI_GAUSSIAN", "MULTI_GAUSSIAN_ELLIPSE"]: num = len(kwargs_list[i]["sigma"]) n_list += [num] elif model in ["MGE_SET", "MGE_SET_ELLIPSE"]: num = self.func_list[i].num_linear n_list += [num] elif model in [ "SHAPELETS", "SHAPELETS_POLAR", "SHAPELETS_POLAR_EXP", "SHAPELETS_ELLIPSE", ]: n_max = kwargs_list[i]["n_max"] if model in ["SHAPELETS_POLAR_EXP"]: num_param = int((n_max + 1) ** 2) else: num_param = int((n_max + 1) * (n_max + 2) / 2) n_list += [num_param] elif model in ["SLIT_STARLETS", "SLIT_STARLETS_GEN2"]: n_scales = kwargs_list[i]["n_scales"] n_pixels = kwargs_list[i]["n_pixels"] num_param = int(n_scales * n_pixels) n_list += [ num_param ] # TODO : find a way to make it the number of source pixels else: raise ValueError("model type %s not valid!" % model) return n_list
[docs] def update_linear(self, param, i, kwargs_list): """ :param param: array of linear amplitude coefficients in the order of the linear minimization of the ImSim module :param i: index of first coefficient to start reading out the linear parameters associated with the model components of this class :param kwargs_list: list of keyword arguments of the model components :return: kwargs list with over-written or added 'amp' parameters according to the coefficients in param """ for k, model in enumerate(self.profile_type_list): if model in [ "CHAMELEON", "CORE_SERSIC", "DOUBLE_CHAMELEON", "ELLIPSOID", "GAUSSIAN", "GAUSSIAN_ELLIPSE", "HERNQUIST", "HERNQUIST_ELLIPSE", "INTERPOL", "LINEAR", "LINEAR_ELLIPSE", "LINE_PROFILE", "NIE", "PJAFFE", "PJAFFE_ELLIPSE", "PL_SERSIC", "POWER_LAW", "SERSIC", "SERSIC_ELLIPSE", "SERSIC_ELLIPSE_FLEXION", "SERSIC_ELLIPSE_Q_PHI", "TRIPLE_CHAMELEON", "UNIFORM", ]: kwargs_list[k]["amp"] = param[i] i += 1 elif model in ["MULTI_GAUSSIAN", "MULTI_GAUSSIAN_ELLIPSE"]: num_param = len(kwargs_list[k]["sigma"]) kwargs_list[k]["amp"] = param[i : i + num_param] i += num_param elif model in ["MGE_SET", "MGE_SET_ELLIPSE"]: num_param = self.func_list[k].num_linear kwargs_list[k]["amp"] = param[i : i + num_param] i += num_param elif model in [ "SHAPELETS", "SHAPELETS_ELLIPSE", "SHAPELETS_POLAR", "SHAPELETS_POLAR_EXP", ]: n_max = kwargs_list[k]["n_max"] if model in ["SHAPELETS_POLAR_EXP"]: num_param = int((n_max + 1) ** 2) else: num_param = int((n_max + 1) * (n_max + 2) / 2) kwargs_list[k]["amp"] = param[i : i + num_param] i += num_param elif model in ["SLIT_STARLETS", "SLIT_STARLETS_GEN2"]: n_scales = kwargs_list[k]["n_scales"] n_pixels = kwargs_list[k]["n_pixels"] num_param = int(n_scales * n_pixels) kwargs_list[k]["amp"] = param[i : i + num_param] i += num_param else: raise ValueError("model type %s not valid!" % model) return kwargs_list, i
[docs] def add_fixed_linear(self, kwargs_fixed_list): """ :param kwargs_fixed_list: list of fixed keyword arguments :return: updated kwargs_fixed_list with additional linear parameters being fixed. """ for k, model in enumerate(self.profile_type_list): kwargs_fixed = kwargs_fixed_list[k] param_names = self.param_name_list[k] if "amp" in param_names: if "amp" not in kwargs_fixed: kwargs_fixed["amp"] = 1 return kwargs_fixed_list
[docs] def linear_param_from_kwargs(self, kwargs_list): """Inverse function of update_linear() returning the linear amplitude list for the keyword argument list. :param kwargs_list: model parameters including the linear amplitude parameters :type kwargs_list: list of keyword arguments :return: list of linear amplitude parameters :rtype: list """ param = [] for k, model in enumerate(self.profile_type_list): kwargs_ = kwargs_list[k] param_names = self.param_name_list[k] if "amp" in param_names: amp = kwargs_["amp"] amp_list = np.atleast_1d(amp) for a in amp_list: param.append(a) return param
[docs] def check_positive_flux_profile(self, kwargs_list): """Check whether linear amplitude parameter are non-negative for specified list of lens models that have a physical amplitude interpretation. :param kwargs_list: light model parameter keyword argument list :return: bool, if True, no specified model has negative flux """ pos_bool = True for k, model in enumerate(self.profile_type_list): if "amp" in kwargs_list[k]: if model in [ "MULTI_GAUSSIAN", "MULTI_GAUSSIAN_ELLIPSE", "MGE_SET", "MGE_SET_ELLIPSE", "CHAMELEON", "CORE_SERSIC", "DOUBLE_CHAMELEON", "GAUSSIAN", "GAUSSIAN_ELLIPSE", "HERNQUIST", "HERNQUIST_ELLIPSE", "NIE", "PJAFFE", "PJAFFE_ELLIPSE", "PL_SERSIC", "POWER_LAW", "SERSIC", "SERSIC_ELLIPSE", "SERSIC_ELLIPSE_FLEXION", ]: if np.any(np.array(kwargs_list[k]["amp"]) < 0): pos_bool = False break return pos_bool