def relocated_grid_from_grid(self, grid): """ Relocate the coordinates of a grid to the border of this grid if they are outside the border, where the border is defined as all pixels at the edge of the grid's mask (see *mask._border_1d_indexes*). This is performed as follows: 1) Use the mean value of the grid's y and x coordinates to determine the origin of the grid. 2) Compute the radial distance of every grid coordinate from the origin. 3) For every coordinate, find its nearest pixel in the border. 4) Determine if it is outside the border, by comparing its radial distance from the origin to its paired \ border pixel's radial distance. 5) If its radial distance is larger, use the ratio of radial distances to move the coordinate to the border \ (if its inside the border, do nothing). The method can be used on uniform or irregular grids, however for irregular grids the border of the 'image-plane' mask is used to define border pixels. Parameters ---------- grid : Grid2D The grid (uniform or irregular) whose pixels are to be relocated to the border edge if outside it. """ if len(self.sub_border_grid) == 0: return grid return grid_2d.Grid2D( grid=self.relocated_grid_from_grid_jit( grid=grid, border_grid=self.sub_border_grid), mask=grid.mask, sub_size=grid.mask.sub_size, )
def grid_pixel_centres_from_grid_scaled_1d(self, grid_scaled_1d): """ Convert a grid of (y,x) scaled coordinates to a grid of (y,x) pixel values. Pixel coordinates are \ returned as integers such that they map directly to the pixel they are contained within. The pixel coordinate origin is at the top left corner of the grid, such that the pixel [0,0] corresponds to \ higher y scaled coordinate value and lowest x scaled coordinate. The scaled coordinate origin is defined by the class attribute origin, and coordinates are shifted to this \ origin before computing their 1D grid pixel indexes. Parameters ---------- grid_scaled_1d: np.ndarray The grid of (y,x) coordinates in scaled units. """ grid_pixel_centres_1d = grid_2d_util.grid_pixel_centres_2d_slim_from( grid_scaled_2d_slim=grid_scaled_1d, shape_native=self.shape, pixel_scales=self.pixel_scales, origin=self.origin, ).astype("int") return grid_2d.Grid2D(grid=grid_pixel_centres_1d, mask=self.edge_mask.mask_sub_1)
def masked_grid_sub_1(self): grid_slim = grid_2d_util.grid_2d_slim_via_mask_from( mask_2d=self, pixel_scales=self.pixel_scales, sub_size=1, origin=self.origin) return grid_2d.Grid2D(grid=grid_slim, mask=self.mask_sub_1)
def masked_grid(self): sub_grid_1d = grid_2d_util.grid_2d_slim_via_mask_from( mask_2d=self, pixel_scales=self.pixel_scales, sub_size=self.sub_size, origin=self.origin, ) return grid_2d.Grid2D(grid=sub_grid_1d, mask=self.edge_mask.mask_sub_1)
def border_grid_sub_1(self): """ The indicies of the mask's border pixels, where a border pixel is any unmasked pixel on an exterior edge (e.g. next to at least one pixel with a `True` value but not central pixels like those within \ an annulus mask). """ border_grid_1d = self.masked_grid_sub_1[self._border_1d_indexes] return grid_2d.Grid2D(grid=border_grid_1d, mask=self.border_mask.mask_sub_1)
def unmasked_grid_sub_1(self): """ The scaled-grid of (y,x) coordinates of every pixel. This is defined from the top-left corner, such that the first pixel at location [0, 0] will have a negative x \ value y value in scaled units. """ grid_slim = grid_2d_util.grid_2d_slim_via_shape_native_from( shape_native=self.shape, pixel_scales=self.pixel_scales, sub_size=1, origin=self.origin, ) return grid_2d.Grid2D(grid=grid_slim, mask=self.unmasked_mask.mask_sub_1)
def grid_scaled_from_grid_pixels_1d_for_marching_squares( self, grid_pixels_1d, shape_native): grid_scaled_1d = grid_2d_util.grid_scaled_2d_slim_from( grid_pixels_2d_slim=grid_pixels_1d, shape_native=shape_native, pixel_scales=( self.pixel_scales[0] / self.sub_size, self.pixel_scales[1] / self.sub_size, ), origin=self.origin, ) grid_scaled_1d[:, 0] -= self.pixel_scales[0] / (2.0 * self.sub_size) grid_scaled_1d[:, 1] += self.pixel_scales[1] / (2.0 * self.sub_size) return grid_2d.Grid2D(grid=grid_scaled_1d, mask=self.edge_mask.mask_sub_1)
def interpolated_grid_from_grid_interp(self, grid_interp) -> grid_2d.Grid2D: """Use the precomputed vertexes and weight_list of a Delaunay gridding to interpolate a grid of (y,x) values values computed on the interpolation grid to the Grid2DInterpolate's full grid. This function is taken from the SciPy interpolation method griddata (see https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.griddata.html). It is adapted here to reuse pre-computed interpolation vertexes and weight_list for efficiency. Parameters ---------- grid_interp : Grid2D The results of the function evaluated using the interpolation grid, which is interpolated to the native resolution Grid2D. """ y_values = self.interpolated_array_from_array_interp( array_interp=grid_interp[:, 0]) x_values = self.interpolated_array_from_array_interp( array_interp=grid_interp[:, 1]) grid = np.asarray([y_values, x_values]).T return grid_2d.Grid2D(grid=grid, mask=self.mask)
def structure_2d_from_result(self, result: np.ndarray): """ Convert a result from an ndarray to an aa.Array2D or aa.Grid2D structure, where the conversion depends on type(result) as follows: - 1D np.ndarray -> aa.Array2D - 2D np.ndarray -> aa.Grid2D This function is used by the grid_2d_to_structure decorator to convert the output result of a function to an autoarray structure when a `Grid2D` instance is passed to the decorated function. Parameters ---------- result : np.ndarray or [np.ndarray] The input result (e.g. of a decorated function) that is converted to a PyAutoArray structure. """ if len(result.shape) == 1: return array_1d.Array1D(array=result, mask=self.mask) else: if isinstance(result, grid_2d.Grid2DTransformedNumpy): return grid_2d.Grid2DTransformed(grid=result, mask=self.mask) return grid_2d.Grid2D(grid=result, mask=self.mask.to_mask_2d)
def grid_pixels_from_grid_scaled_1d(self, grid_scaled_1d): """ Convert a grid of (y,x) scaled coordinates to a grid of (y,x) pixel values. Pixel coordinates are \ returned as floats such that they include the decimal offset from each pixel's top-left corner. The pixel coordinate origin is at the top left corner of the grid, such that the pixel [0,0] corresponds to \ highest y scaled coordinate value and lowest x scaled coordinate. The scaled coordinate origin is defined by the class attribute origin, and coordinates are shifted to this \ origin before computing their 1D grid pixel indexes. Parameters ---------- grid_scaled_1d: np.ndarray A grid of (y,x) coordinates in scaled units. """ grid_pixels_1d = grid_2d_util.grid_pixels_2d_slim_from( grid_scaled_2d_slim=grid_scaled_1d, shape_native=self.shape, pixel_scales=self.pixel_scales, origin=self.origin, ) return grid_2d.Grid2D(grid=grid_pixels_1d, mask=self.mask_sub_1)
def iterated_grid_from_func(self, func, cls, grid_lower_sub_2d): """ Iterate over a function that returns a grid of values until the it meets a specified fractional accuracy. The function returns a result on a pixel-grid where evaluating it on more points on a higher resolution sub-grid followed by binning lead to a more precise evaluation of the function. For the fractional accuracy of the grid to be met, both the y and x values must meet it. The function is first called for a sub-grid size of 1 and a higher resolution grid. The ratio of values give the fractional accuracy of each function evaluation. Pixels which do not meet the fractional accuracy are iteratively revaulated on higher resolution sub-grids. This is repeated until all pixels meet the fractional accuracy or the highest sub-size specified in the *sub_steps* attribute is computed. If the function return all zeros, the iteration is terminated early given that all levels of sub-gridding will return zeros. This occurs when a function is missing optional objects that contribute to the calculation. An example use case of this function is when a "deflections_2d_from_grid" methods in **PyAutoLens**'s `MassProfile` module is computed, which by evaluating the function on a higher resolution sub-grid samples the analytic mass profile at more points and thus more precisely. Parameters ---------- func : func The function which is iterated over to compute a more precise evaluation. cls : object The class the function belongs to. grid_lower_sub_2d : array_2d.Array2D The results computed by the function using a lower sub-grid size """ if not np.any(grid_lower_sub_2d): return grid_lower_sub_2d.slim iterated_grid = np.zeros(shape=(self.shape_native[0], self.shape_native[1], 2)) fractional_mask_lower_sub = self.mask for sub_size in self.sub_steps[:-1]: grid_higher_sub = self.grid_at_sub_size_from_func_and_mask( func=func, cls=cls, mask=fractional_mask_lower_sub, sub_size=sub_size) fractional_mask_higher_sub = self.fractional_mask_from_grids( grid_lower_sub_2d=grid_lower_sub_2d, grid_higher_sub_2d=grid_higher_sub) iterated_grid = self.iterated_grid_jit_from( iterated_grid=iterated_grid, fractional_mask_higher_sub=fractional_mask_higher_sub, fractional_mask_lower_sub=fractional_mask_lower_sub, grid_higher_sub_2d=grid_higher_sub, ) if fractional_mask_higher_sub.is_all_true: iterated_grid_1d = grid_2d_util.grid_2d_slim_from( mask=self.mask, grid_2d_native=iterated_grid, sub_size=1) return grid_2d.Grid2D(grid=iterated_grid_1d, mask=self.mask.mask_sub_1) grid_lower_sub_2d = grid_higher_sub fractional_mask_lower_sub = fractional_mask_higher_sub grid_higher_sub = self.grid_at_sub_size_from_func_and_mask( func=func, cls=cls, mask=fractional_mask_lower_sub, sub_size=self.sub_steps[-1], ) iterated_grid_2d = iterated_grid + grid_higher_sub.binned.native iterated_grid_1d = grid_2d_util.grid_2d_slim_from( mask=self.mask, grid_2d_native=iterated_grid_2d, sub_size=1) return grid_2d.Grid2D(grid=iterated_grid_1d, mask=self.mask.mask_sub_1)