def __init__(self, image_data, exposure_time=None, background_rms=None, noise_map=None, ra_at_xy_0=0, dec_at_xy_0=0, transform_pix2angle=None, ra_shift=0, dec_shift=0): """ :param image_data: 2d numpy array of the image data :param exposure_time: int or array of size the data; exposure time (common for all pixels or individually for each individual pixel) :param background_rms: root-mean-square value of Gaussian background noise :param noise_map: int or array of size the data; joint noise sqrt(variance) of each individual pixel. :param transform_pix2angle: 2x2 matrix, mapping of pixel to coordinate :param ra_at_xy_0: ra coordinate at pixel (0,0) :param dec_at_xy_0: dec coordinate at pixel (0,0) :param ra_shift: RA shift of pixel grid :param dec_shift: DEC shift of pixel grid """ nx, ny = np.shape(image_data) self._data = image_data if transform_pix2angle is None: transform_pix2angle = np.array([[1, 0], [0, 1]]) PixelGrid.__init__(self, nx, ny, transform_pix2angle, ra_at_xy_0 + ra_shift, dec_at_xy_0 + dec_shift) ImageNoise.__init__(self, image_data, exposure_time=exposure_time, background_rms=background_rms, noise_map=noise_map, verbose=False)
def test_raise(self): Mpix2coord = np.array([[1, 0], [0, 1]]) kwargs_grid = { 'ra_at_xy_0': 0, 'dec_at_xy_0': 0, 'transform_pix2angle': Mpix2coord, 'nx': 10, 'ny': 10 } pixel_grid = PixelGrid(**kwargs_grid) kernel = np.zeros((5, 5)) kernel[2, 2] = 1 kwargs_psf = { 'kernel_point_source': kernel, 'psf_type': 'PIXEL', 'psf_error_map': np.ones_like(kernel) } psf_class = PSF(**kwargs_psf) self._ps_rendering = PointSourceRendering(pixel_grid, supersampling_factor=1, psf=psf_class) with self.assertRaises(ValueError): self._ps_rendering.point_source_rendering(ra_pos=[1, 1], dec_pos=[0, 1], amp=[1])
def __init__(self, multi_band_list, kwargs_model, kwargs_params, multi_band_type='joint-linear', kwargs_likelihood=None, kwargs_pixel_grid=None, verbose=True): """ :param multi_band_list: list of imaging data configuration [[kwargs_data, kwargs_psf, kwargs_numerics], [...]] :param kwargs_model: model keyword argument list :param kwargs_params: keyword arguments of the model parameters, same as output of FittingSequence() 'kwargs_result' :param multi_band_type: string, option when having multiple imaging data sets modelled simultaneously. Options are: - 'multi-linear': linear amplitudes are inferred on single data set - 'linear-joint': linear amplitudes ae jointly inferred - 'single-band': single band :param kwargs_likelihood: likelihood keyword arguments as supported by the Likelihood() class :param kwargs_pixel_grid: keyword argument of PixelGrid() class. This is optional and overwrites a minimal grid Attention for consistent pixel grid definitions! :param verbose: if True (default), computes and prints the total log-likelihood. This can deactivated for speedup purposes (does not run linear inversion again), and reduces the number of prints. """ self._multi_band_list = multi_band_list if not multi_band_type == 'joint-linear': raise ValueError( 'MultiPatchPlot only works with multi_band_type="joint_linear". ' 'Setting choice was %s. ' % multi_band_type) MultiBandImageReconstruction.__init__( self, multi_band_list, kwargs_model, kwargs_params, multi_band_type=multi_band_type, kwargs_likelihood=kwargs_likelihood, verbose=verbose) if kwargs_pixel_grid is not None: self._pixel_grid_joint = PixelGrid(**kwargs_pixel_grid) else: self._pixel_grid_joint = self._joint_pixel_grid(multi_band_list)
def _sub_pixel_grid(self, pixel_grid): """ creates a PixelGrid instance covering the sub-frame area only :param pixel_grid: PixelGrid instance of the full image :return: PixelGrid instance """ if self._subframe_calc is True: transform_pix2angle = pixel_grid.transform_pix2angle nx_sub = self._x_max_sub - self._x_min_sub + 1 ny_sub = self._y_max_sub - self._y_min_sub + 1 ra_at_xy_0_sub, dec_at_xy_0_sub = pixel_grid.map_pix2coord(self._x_min_sub, self._y_min_sub) pixel_grid_sub = PixelGrid(nx=nx_sub, ny=ny_sub, transform_pix2angle=transform_pix2angle, ra_at_xy_0=ra_at_xy_0_sub, dec_at_xy_0=dec_at_xy_0_sub) else: pixel_grid_sub = pixel_grid return pixel_grid_sub
def source(self, num_pix, delta_pix, center=None): """ source in the same coordinate system as the image :param num_pix: number of pixels per axes :param delta_pix: pixel size :param center: list with two entries [center_x, center_y] (optional) :return: 2d surface brightness grid of the reconstructed source and PixelGrid() instance of source grid """ Mpix2coord = self._pixel_grid_joint.transform_pix2angle * delta_pix / self._pixel_grid_joint.pixel_width x_grid_source, y_grid_source = util.make_grid_transformed( num_pix, Mpix2Angle=Mpix2coord) ra_at_xy_0, dec_at_xy_0 = x_grid_source[0], y_grid_source[0] image_model = self.model_band_list[0].image_model_class kwargs_model = self.model_band_list[0].kwargs_model kwargs_source = kwargs_model['kwargs_source'] center_x = 0 center_y = 0 if center is not None: center_x, center_y = center[0], center[1] elif len(kwargs_source) > 0: center_x = kwargs_source[0]['center_x'] center_y = kwargs_source[0]['center_y'] x_grid_source += center_x y_grid_source += center_y pixel_grid = PixelGrid(nx=num_pix, ny=num_pix, transform_pix2angle=Mpix2coord, ra_at_xy_0=ra_at_xy_0 + center_x, dec_at_xy_0=dec_at_xy_0 + center_y) source = image_model.SourceModel.surface_brightness( x_grid_source, y_grid_source, kwargs_source) source = util.array2image(source) * delta_pix**2 return source, pixel_grid
def _joint_pixel_grid(multi_band_list): """ Joint PixelGrid() class instance. This routine only works when the individual patches have the same coordinate system orientation and pixel scale. :param multi_band_list: list of imaging data configuration [[kwargs_data, kwargs_psf, kwargs_numerics], [...]] :return: PixelGrid() class instance covering the entire window of the sky including all individual patches """ nx, ny = 0, 0 kwargs_data = copy.deepcopy(multi_band_list[0][0]) kwargs_pixel_grid = { 'nx': 0, 'ny': 0, 'transform_pix2angle': kwargs_data['transform_pix2angle'], 'ra_at_xy_0': kwargs_data['ra_at_xy_0'], 'dec_at_xy_0': kwargs_data['dec_at_xy_0'] } pixel_grid = PixelGrid(**kwargs_pixel_grid) Mpix2a = pixel_grid.transform_pix2angle # set up joint coordinate system and pixel size to include all frames for i in range(len(multi_band_list)): kwargs_data = multi_band_list[i][0] data_class_i = ImageData(**kwargs_data) Mpix2a_i = data_class_i.transform_pix2angle # check we are operating in the same coordinate system/rotation and pixel scale npt.assert_almost_equal(Mpix2a, Mpix2a_i, decimal=5) # evaluate pixel of zero point with the base coordinate system ra0, dec0 = data_class_i.radec_at_xy_0 x_min, y_min = pixel_grid.map_coord2pix(ra0, dec0) nx_i, ny_i = data_class_i.num_pixel_axes nx, ny = _update_frame_size(nx, ny, x_min, y_min, nx_i, ny_i) # select minimum in x- and y-axis # transform back in RA/DEC and make this the new zero point of the base coordinate system ra_at_xy_0_new, dec_at_xy_0_new = pixel_grid.map_pix2coord( np.minimum(x_min, 0), np.minimum(y_min, 0)) kwargs_pixel_grid['ra_at_xy_0'] = ra_at_xy_0_new kwargs_pixel_grid['dec_at_xy_0'] = dec_at_xy_0_new kwargs_pixel_grid['nx'] = nx kwargs_pixel_grid['ny'] = ny pixel_grid = PixelGrid(**kwargs_pixel_grid) return pixel_grid
def setup(self): # we define a model consisting of a singe Sersric profile from lenstronomy.LightModel.light_model import LightModel light_model_list = ['SERSIC_ELLIPSE'] self.lightModel = LightModel(light_model_list=light_model_list) self.kwargs_light = [ {'amp': 100, 'R_sersic': 0.5, 'n_sersic': 3, 'e1': 0, 'e2': 0, 'center_x': 0.02, 'center_y': 0}] # we define a pixel grid and a higher resolution super sampling factor self._supersampling_factor = 5 numPix = 61 # cutout pixel size deltaPix = 0.05 # pixel size in arcsec (area per pixel = deltaPix**2) x, y, ra_at_xy_0, dec_at_xy_0, x_at_radec_0, y_at_radec_0, Mpix2coord, Mcoord2pix = util.make_grid_with_coordtransform( numPix=numPix, deltapix=deltaPix, subgrid_res=1, left_lower=False, inverse=False) flux = self.lightModel.surface_brightness(x, y, kwargs_list=self.kwargs_light) flux = util.array2image(flux) flux_max = np.max(flux) conv_pixels_partial = np.zeros((numPix, numPix), dtype=bool) conv_pixels_partial[flux >= flux_max / 20] = True self._conv_pixels_partial = conv_pixels_partial # high resolution ray-tracing and high resolution convolution, the full calculation self.kwargs_numerics_true = {'supersampling_factor': self._supersampling_factor, # super sampling factor of (partial) high resolution ray-tracing 'compute_mode': 'regular', # 'regular' or 'adaptive' 'supersampling_convolution': True, # bool, if True, performs the supersampled convolution (either on regular or adaptive grid) 'supersampling_kernel_size': None, # size of the higher resolution kernel region (can be smaller than the original kernel). None leads to use the full size 'flux_evaluate_indexes': None, # bool mask, if None, it will evaluate all (sub) pixels 'supersampled_indexes': None, # bool mask of pixels to be computed in supersampled grid (only for adaptive mode) 'compute_indexes': None, # bool mask of pixels to be computed the PSF response (flux being added to). Only used for adaptive mode and can be set =likelihood mask. 'point_source_supersampling_factor': 1, # int, supersampling factor when rendering a point source (not used in this script) } # high resolution convolution on a smaller PSF with low resolution convolution on the edges of the PSF and high resolution ray tracing self.kwargs_numerics_high_res_narrow = {'supersampling_factor': self._supersampling_factor, 'compute_mode': 'regular', 'supersampling_convolution': True, 'supersampling_kernel_size': 5, } # low resolution convolution based on high resolution ray-tracing grid self.kwargs_numerics_low_conv_high_grid = {'supersampling_factor': self._supersampling_factor, 'compute_mode': 'regular', 'supersampling_convolution': False, # does not matter for supersampling_factor=1 'supersampling_kernel_size': None, # does not matter for supersampling_factor=1 } # low resolution convolution with a subset of pixels with high resolution ray-tracing self.kwargs_numerics_low_conv_high_adaptive = {'supersampling_factor': self._supersampling_factor, 'compute_mode': 'adaptive', 'supersampling_convolution': False, # does not matter for supersampling_factor=1 'supersampling_kernel_size': None, # does not matter for supersampling_factor=1 'supersampled_indexes': self._conv_pixels_partial, 'convolution_kernel_size': 9, } # low resolution convolution with a subset of pixels with high resolution ray-tracing and high resoluton convolution on smaller kernel size self.kwargs_numerics_high_adaptive = {'supersampling_factor': self._supersampling_factor, 'compute_mode': 'adaptive', 'supersampling_convolution': True, # does not matter for supersampling_factor=1 'supersampling_kernel_size': 5, # does not matter for supersampling_factor=1 'supersampled_indexes': self._conv_pixels_partial, 'convolution_kernel_size': 9, } # low resolution convolution and low resolution ray tracing, the simplest calculation self.kwargs_numerics_low_res = {'supersampling_factor': 1, 'compute_mode': 'regular', 'supersampling_convolution': False, # does not matter for supersampling_factor=1 'supersampling_kernel_size': None, # does not matter for supersampling_factor=1 'convolution_kernel_size': 9, } flux_evaluate_indexes = np.zeros((numPix, numPix), dtype=bool) flux_evaluate_indexes[flux >= flux_max / 1000] = True # low resolution convolution on subframe self.kwargs_numerics_partial = {'supersampling_factor': 1, 'compute_mode': 'regular', 'supersampling_convolution': False, # does not matter for supersampling_factor=1 'supersampling_kernel_size': None, # does not matter for supersampling_factor=1 'flux_evaluate_indexes': flux_evaluate_indexes, 'convolution_kernel_size': 9 } # import PSF file kernel_super = kernel_util.kernel_gaussian(kernel_numPix=11 * self._supersampling_factor, deltaPix=deltaPix / self._supersampling_factor, fwhm=0.1) kernel_size = 9 kernel_super = kernel_util.cut_psf(psf_data=kernel_super, psf_size=kernel_size * self._supersampling_factor) # make instance of the PixelGrid class from lenstronomy.Data.pixel_grid import PixelGrid kwargs_grid = {'nx': numPix, 'ny': numPix, 'transform_pix2angle': Mpix2coord, 'ra_at_xy_0': ra_at_xy_0, 'dec_at_xy_0': dec_at_xy_0} self.pixel_grid = PixelGrid(**kwargs_grid) # make instance of the PSF class from lenstronomy.Data.psf import PSF kwargs_psf = {'psf_type': 'PIXEL', 'kernel_point_source': kernel_super, 'point_source_supersampling_factor': self._supersampling_factor} self.psf_class = PSF(**kwargs_psf) # without convolution image_model_true = ImageModel(self.pixel_grid, self.psf_class, lens_light_model_class=self.lightModel, kwargs_numerics=self.kwargs_numerics_true) self.image_true = image_model_true.image(kwargs_lens_light=self.kwargs_light)
class MultiPatchReconstruction(MultiBandImageReconstruction): """ this class illustrates the model of disconnected multi-patch modeling with 'joint-linear' option in one single array. """ def __init__(self, multi_band_list, kwargs_model, kwargs_params, multi_band_type='joint-linear', kwargs_likelihood=None, kwargs_pixel_grid=None, verbose=True): """ :param multi_band_list: list of imaging data configuration [[kwargs_data, kwargs_psf, kwargs_numerics], [...]] :param kwargs_model: model keyword argument list :param kwargs_params: keyword arguments of the model parameters, same as output of FittingSequence() 'kwargs_result' :param multi_band_type: string, option when having multiple imaging data sets modelled simultaneously. Options are: - 'multi-linear': linear amplitudes are inferred on single data set - 'linear-joint': linear amplitudes ae jointly inferred - 'single-band': single band :param kwargs_likelihood: likelihood keyword arguments as supported by the Likelihood() class :param kwargs_pixel_grid: keyword argument of PixelGrid() class. This is optional and overwrites a minimal grid Attention for consistent pixel grid definitions! :param verbose: if True (default), computes and prints the total log-likelihood. This can deactivated for speedup purposes (does not run linear inversion again), and reduces the number of prints. """ self._multi_band_list = multi_band_list if not multi_band_type == 'joint-linear': raise ValueError( 'MultiPatchPlot only works with multi_band_type="joint_linear". ' 'Setting choice was %s. ' % multi_band_type) MultiBandImageReconstruction.__init__( self, multi_band_list, kwargs_model, kwargs_params, multi_band_type=multi_band_type, kwargs_likelihood=kwargs_likelihood, verbose=verbose) if kwargs_pixel_grid is not None: self._pixel_grid_joint = PixelGrid(**kwargs_pixel_grid) else: self._pixel_grid_joint = self._joint_pixel_grid(multi_band_list) @property def pixel_grid_joint(self): """ :return: PixelGrid() class instance covering the entire window of the sky including all individual patches """ return self._pixel_grid_joint @staticmethod def _joint_pixel_grid(multi_band_list): """ Joint PixelGrid() class instance. This routine only works when the individual patches have the same coordinate system orientation and pixel scale. :param multi_band_list: list of imaging data configuration [[kwargs_data, kwargs_psf, kwargs_numerics], [...]] :return: PixelGrid() class instance covering the entire window of the sky including all individual patches """ nx, ny = 0, 0 kwargs_data = copy.deepcopy(multi_band_list[0][0]) kwargs_pixel_grid = { 'nx': 0, 'ny': 0, 'transform_pix2angle': kwargs_data['transform_pix2angle'], 'ra_at_xy_0': kwargs_data['ra_at_xy_0'], 'dec_at_xy_0': kwargs_data['dec_at_xy_0'] } pixel_grid = PixelGrid(**kwargs_pixel_grid) Mpix2a = pixel_grid.transform_pix2angle # set up joint coordinate system and pixel size to include all frames for i in range(len(multi_band_list)): kwargs_data = multi_band_list[i][0] data_class_i = ImageData(**kwargs_data) Mpix2a_i = data_class_i.transform_pix2angle # check we are operating in the same coordinate system/rotation and pixel scale npt.assert_almost_equal(Mpix2a, Mpix2a_i, decimal=5) # evaluate pixel of zero point with the base coordinate system ra0, dec0 = data_class_i.radec_at_xy_0 x_min, y_min = pixel_grid.map_coord2pix(ra0, dec0) nx_i, ny_i = data_class_i.num_pixel_axes nx, ny = _update_frame_size(nx, ny, x_min, y_min, nx_i, ny_i) # select minimum in x- and y-axis # transform back in RA/DEC and make this the new zero point of the base coordinate system ra_at_xy_0_new, dec_at_xy_0_new = pixel_grid.map_pix2coord( np.minimum(x_min, 0), np.minimum(y_min, 0)) kwargs_pixel_grid['ra_at_xy_0'] = ra_at_xy_0_new kwargs_pixel_grid['dec_at_xy_0'] = dec_at_xy_0_new kwargs_pixel_grid['nx'] = nx kwargs_pixel_grid['ny'] = ny pixel_grid = PixelGrid(**kwargs_pixel_grid) return pixel_grid def image_joint(self): """ patch together the individual patches of data and models :return: image_joint, model_joint, norm_residuals_joint """ nx, ny = self._pixel_grid_joint.num_pixel_axes image_joint = np.zeros((ny, nx)) model_joint = np.zeros((ny, nx)) norm_residuals_joint = np.zeros((ny, nx)) for model_band in self.model_band_list: if model_band is not None: image_model = model_band.image_model_class kwargs_params = model_band.kwargs_model model = image_model.image(**kwargs_params) data_class_i = image_model.Data # evaluate pixel of zero point with the base coordinate system ra0, dec0 = data_class_i.radec_at_xy_0 x_min, y_min = self._pixel_grid_joint.map_coord2pix(ra0, dec0) nx_i, ny_i = data_class_i.num_pixel_axes image_joint[int(y_min):int(y_min + ny_i), int(x_min):int(x_min + nx_i)] = data_class_i.data model_joint[int(y_min):int(y_min + ny_i), int(x_min):int(x_min + nx_i)] = model norm_residuals_joint[ int(y_min):int(y_min + ny_i), int(x_min):int(x_min + nx_i)] = model_band.norm_residuals return image_joint, model_joint, norm_residuals_joint def lens_model_joint(self): """ patch together the individual patches of the lens model (can be discontinues) :return: 2d numpy arrays of kappa_joint, magnification_joint, alpha_x_joint, alpha_y_joint """ nx, ny = self._pixel_grid_joint.num_pixel_axes kappa_joint = np.zeros((ny, nx)) magnification_joint = np.zeros((ny, nx)) alpha_x_joint, alpha_y_joint = np.zeros((ny, nx)), np.zeros((ny, nx)) for model_band in self.model_band_list: if model_band is not None: image_model = model_band.image_model_class kwargs_params = model_band.kwargs_model kwargs_lens = kwargs_params['kwargs_lens'] lens_model = image_model.LensModel x_grid, y_grid = image_model.Data.pixel_coordinates kappa = lens_model.kappa(x_grid, y_grid, kwargs_lens) magnification = lens_model.magnification( x_grid, y_grid, kwargs_lens) alpha_x, alpha_y = lens_model.alpha(x_grid, y_grid, kwargs_lens) data_class_i = image_model.Data # evaluate pixel of zero point with the base coordinate system ra0, dec0 = data_class_i.radec_at_xy_0 x_min, y_min = self._pixel_grid_joint.map_coord2pix(ra0, dec0) nx_i, ny_i = data_class_i.num_pixel_axes kappa_joint[int(y_min):int(y_min + ny_i), int(x_min):int(x_min + nx_i)] = kappa magnification_joint[int(y_min):int(y_min + ny_i), int(x_min):int(x_min + nx_i)] = magnification alpha_x_joint[int(y_min):int(y_min + ny_i), int(x_min):int(x_min + nx_i)] = alpha_x alpha_y_joint[int(y_min):int(y_min + ny_i), int(x_min):int(x_min + nx_i)] = alpha_y return kappa_joint, magnification_joint, alpha_x_joint, alpha_y_joint def source(self, num_pix, delta_pix, center=None): """ source in the same coordinate system as the image :param num_pix: number of pixels per axes :param delta_pix: pixel size :param center: list with two entries [center_x, center_y] (optional) :return: 2d surface brightness grid of the reconstructed source and PixelGrid() instance of source grid """ Mpix2coord = self._pixel_grid_joint.transform_pix2angle * delta_pix / self._pixel_grid_joint.pixel_width x_grid_source, y_grid_source = util.make_grid_transformed( num_pix, Mpix2Angle=Mpix2coord) ra_at_xy_0, dec_at_xy_0 = x_grid_source[0], y_grid_source[0] image_model = self.model_band_list[0].image_model_class kwargs_model = self.model_band_list[0].kwargs_model kwargs_source = kwargs_model['kwargs_source'] center_x = 0 center_y = 0 if center is not None: center_x, center_y = center[0], center[1] elif len(kwargs_source) > 0: center_x = kwargs_source[0]['center_x'] center_y = kwargs_source[0]['center_y'] x_grid_source += center_x y_grid_source += center_y pixel_grid = PixelGrid(nx=num_pix, ny=num_pix, transform_pix2angle=Mpix2coord, ra_at_xy_0=ra_at_xy_0 + center_x, dec_at_xy_0=dec_at_xy_0 + center_y) source = image_model.SourceModel.surface_brightness( x_grid_source, y_grid_source, kwargs_source) source = util.array2image(source) * delta_pix**2 return source, pixel_grid