44from typing import Optional , Union
55
66from autoconf import cached_property
7+ from autoconf import instance
78
89from autoarray .dataset .abstract .dataset import AbstractDataset
910from autoarray .dataset .grids import GridsDataset
@@ -29,7 +30,7 @@ def __init__(
2930 noise_covariance_matrix : Optional [np .ndarray ] = None ,
3031 over_sample_size_lp : Union [int , Array2D ] = 4 ,
3132 over_sample_size_pixelization : Union [int , Array2D ] = 4 ,
32- pad_for_psf : bool = False ,
33+ disable_fft_pad : bool = True ,
3334 use_normalized_psf : Optional [bool ] = True ,
3435 check_noise_map : bool = True ,
3536 ):
@@ -76,63 +77,59 @@ def __init__(
7677 over_sample_size_pixelization
7778 How over sampling is performed for the grid which is associated with a pixelization, which is therefore
7879 passed into the calculations performed in the `inversion` module.
79- pad_for_psf
80- The PSF convolution may extend beyond the edges of the image mask , which can lead to edge effects in the
81- convolved image. If `True`, the image and noise-map are padded to ensure the PSF convolution does not
82- extend beyond the edge of the image .
80+ disable_fft_pad
81+ The FFT PSF convolution is optimal for a certain 2D FFT padding or trimming , which places the fewest zeros
82+ around the image. If this is set to `True`, this optimal padding is not performed and the image is used
83+ as-is .
8384 use_normalized_psf
8485 If `True`, the PSF kernel values are rescaled such that they sum to 1.0. This can be important for ensuring
8586 the PSF kernel does not change the overall normalization of the image when it is convolved with it.
8687 check_noise_map
8788 If True, the noise-map is checked to ensure all values are above zero.
8889 """
8990
90- self .unmasked = None
91+ self .disable_fft_pad = disable_fft_pad
9192
92- self . pad_for_psf = pad_for_psf
93+ if psf is not None :
9394
94- if pad_for_psf and psf is not None :
95- try :
96- data .mask .derive_mask .blurring_from (
97- kernel_shape_native = psf .shape_native
98- )
99- except exc .MaskException :
100- over_sample_size_lp = (
101- over_sample_util .over_sample_size_convert_to_array_2d_from (
102- over_sample_size = over_sample_size_lp , mask = data .mask
103- )
104- )
105- over_sample_size_lp = (
106- over_sample_size_lp .padded_before_convolution_from (
107- kernel_shape = psf .shape_native , mask_pad_value = 1
108- )
109- )
95+ full_shape , fft_shape , mask_shape = psf .fft_shape_from (mask = data .mask )
11096
111- over_sample_size_pixelization = (
112- over_sample_util .over_sample_size_convert_to_array_2d_from (
113- over_sample_size = over_sample_size_pixelization , mask = data .mask
114- )
115- )
116- over_sample_size_pixelization = (
117- over_sample_size_pixelization .padded_before_convolution_from (
118- kernel_shape = psf .shape_native , mask_pad_value = 1
119- )
97+ if psf is not None and not disable_fft_pad and data .mask .shape != fft_shape :
98+
99+ # If using real-space convolution instead of FFT, enforce odd-odd shapes
100+ if not psf .use_fft :
101+ fft_shape = tuple (s + 1 if s % 2 == 0 else s for s in fft_shape )
102+
103+ logger .info (
104+ f"Imaging data has been trimmed or padded for FFT convolution.\n "
105+ f" - Original shape : { data .mask .shape } \n "
106+ f" - FFT shape : { fft_shape } \n "
107+ f"Padding ensures accurate PSF convolution in Fourier space. "
108+ f"Set `disable_fft_pad=True` in Imaging object to turn off automatic padding."
109+ )
110+
111+ over_sample_size_lp = (
112+ over_sample_util .over_sample_size_convert_to_array_2d_from (
113+ over_sample_size = over_sample_size_lp , mask = data .mask
120114 )
115+ )
116+ over_sample_size_lp = over_sample_size_lp .resized_from (
117+ new_shape = fft_shape , mask_pad_value = 1
118+ )
121119
122- data = data .padded_before_convolution_from (
123- kernel_shape = psf .shape_native , mask_pad_value = 1
120+ over_sample_size_pixelization = (
121+ over_sample_util .over_sample_size_convert_to_array_2d_from (
122+ over_sample_size = over_sample_size_pixelization , mask = data .mask
124123 )
125- if noise_map is not None :
126- noise_map = noise_map .padded_before_convolution_from (
127- kernel_shape = psf .shape_native , mask_pad_value = 1
128- )
129- logger .info (
130- f"The image and noise map of the `Imaging` objected have been padded to the dimensions"
131- f"{ data .shape } . This is because the blurring region around the mask (which defines where"
132- f"PSF flux may be convolved into the masked region) extended beyond the edge of the image."
133- f""
134- f"This can be prevented by using a smaller mask, smaller PSF kernel size or manually padding"
135- f"the image and noise-map yourself."
124+ )
125+ over_sample_size_pixelization = over_sample_size_pixelization .resized_from (
126+ new_shape = fft_shape , mask_pad_value = 1
127+ )
128+
129+ data = data .resized_from (new_shape = fft_shape , mask_pad_value = 1 )
130+ if noise_map is not None :
131+ noise_map = noise_map .resized_from (
132+ new_shape = fft_shape , mask_pad_value = 1
136133 )
137134
138135 super ().__init__ (
@@ -179,6 +176,9 @@ def __init__(
179176 normalize = use_normalized_psf ,
180177 image_mask = image_mask ,
181178 blurring_mask = blurring_mask ,
179+ mask_shape = mask_shape ,
180+ full_shape = full_shape ,
181+ fft_shape = fft_shape ,
182182 )
183183
184184 self .psf = psf
@@ -337,31 +337,34 @@ def from_fits(
337337 over_sample_size_pixelization = over_sample_size_pixelization ,
338338 )
339339
340- def apply_mask (self , mask : Mask2D ) -> "Imaging" :
340+ def apply_mask (self , mask : Mask2D , disable_fft_pad : bool = False ) -> "Imaging" :
341341 """
342342 Apply a mask to the imaging dataset, whereby the mask is applied to the image data, noise-map and other
343343 quantities one-by-one.
344344
345- The original unmasked imaging data is stored as the `self.unmasked` attribute. This is used to ensure that if
346- the `apply_mask` function is called multiple times, every mask is always applied to the original unmasked
347- imaging dataset.
345+ The `apply_mask` function cannot be called multiple times, if it is a mask may remove data, therefore
346+ an exception is raised. If you wish to apply a new mask, reload the dataset from .fits files.
348347
349348 Parameters
350349 ----------
351350 mask
352351 The 2D mask that is applied to the image.
353352 """
354- if self .data .mask .is_all_false :
355- unmasked_dataset = self
356- else :
357- unmasked_dataset = self .unmasked
353+ invalid = np .logical_and (self .data .mask , np .logical_not (mask ))
354+
355+ if np .any (invalid ):
356+ raise exc .DatasetException (
357+ "The new mask overlaps with pixels that are already unmasked in the dataset. "
358+ "You cannot apply a new mask on top of an existing one. "
359+ "If you wish to apply a different mask, please reload the dataset from .fits files."
360+ )
358361
359- data = Array2D (values = unmasked_dataset .data .native , mask = mask )
362+ data = Array2D (values = self .data .native , mask = mask )
360363
361- noise_map = Array2D (values = unmasked_dataset .noise_map .native , mask = mask )
364+ noise_map = Array2D (values = self .noise_map .native , mask = mask )
362365
363- if unmasked_dataset .noise_covariance_matrix is not None :
364- noise_covariance_matrix = unmasked_dataset .noise_covariance_matrix
366+ if self .noise_covariance_matrix is not None :
367+ noise_covariance_matrix = self .noise_covariance_matrix
365368
366369 noise_covariance_matrix = np .delete (
367370 noise_covariance_matrix , mask .derive_indexes .masked_slim , 0
@@ -385,11 +388,9 @@ def apply_mask(self, mask: Mask2D) -> "Imaging":
385388 noise_covariance_matrix = noise_covariance_matrix ,
386389 over_sample_size_lp = over_sample_size_lp ,
387390 over_sample_size_pixelization = over_sample_size_pixelization ,
388- pad_for_psf = True ,
391+ disable_fft_pad = disable_fft_pad ,
389392 )
390393
391- dataset .unmasked = unmasked_dataset
392-
393394 logger .info (
394395 f"IMAGING - Data masked, contains a total of { mask .pixels_in_mask } image-pixels"
395396 )
@@ -400,6 +401,7 @@ def apply_noise_scaling(
400401 self ,
401402 mask : Mask2D ,
402403 noise_value : float = 1e8 ,
404+ disable_fft_pad : bool = False ,
403405 signal_to_noise_value : Optional [float ] = None ,
404406 should_zero_data : bool = True ,
405407 ) -> "Imaging" :
@@ -455,18 +457,6 @@ def apply_noise_scaling(
455457 else :
456458 data = self .data .native .array
457459
458- data_unmasked = Array2D .no_mask (
459- values = data ,
460- shape_native = self .data .shape_native ,
461- pixel_scales = self .data .pixel_scales ,
462- )
463-
464- noise_map_unmasked = Array2D .no_mask (
465- values = noise_map ,
466- shape_native = self .noise_map .shape_native ,
467- pixel_scales = self .noise_map .pixel_scales ,
468- )
469-
470460 data = Array2D (values = data , mask = self .data .mask )
471461
472462 noise_map = Array2D (values = noise_map , mask = self .data .mask )
@@ -478,15 +468,10 @@ def apply_noise_scaling(
478468 noise_covariance_matrix = self .noise_covariance_matrix ,
479469 over_sample_size_lp = self .over_sample_size_lp ,
480470 over_sample_size_pixelization = self .over_sample_size_pixelization ,
481- pad_for_psf = False ,
471+ disable_fft_pad = disable_fft_pad ,
482472 check_noise_map = False ,
483473 )
484474
485- if self .unmasked is not None :
486- dataset .unmasked = self .unmasked
487- dataset .unmasked .data = data_unmasked
488- dataset .unmasked .noise_map = noise_map_unmasked
489-
490475 logger .info (
491476 f"IMAGING - Data noise scaling applied, a total of { mask .pixels_in_mask } pixels were scaled to large noise values."
492477 )
@@ -497,6 +482,7 @@ def apply_over_sampling(
497482 self ,
498483 over_sample_size_lp : Union [int , Array2D ] = None ,
499484 over_sample_size_pixelization : Union [int , Array2D ] = None ,
485+ disable_fft_pad : bool = False ,
500486 ) -> "AbstractDataset" :
501487 """
502488 Apply new over sampling objects to the grid and grid pixelization of the dataset.
@@ -526,7 +512,7 @@ def apply_over_sampling(
526512 over_sample_size_lp = over_sample_size_lp or self .over_sample_size_lp ,
527513 over_sample_size_pixelization = over_sample_size_pixelization
528514 or self .over_sample_size_pixelization ,
529- pad_for_psf = False ,
515+ disable_fft_pad = disable_fft_pad ,
530516 check_noise_map = False ,
531517 )
532518
0 commit comments