import numpy as np
from pathlib import Path
from typing import Optional, Dict, Union
import autoarray as aa
from autocti import exc
from autocti.dataset_1d.dataset_1d.settings import SettingsDataset1D
from autocti.extract.settings import SettingsExtract
from autocti.layout.one_d import Layout1D
[docs]class Dataset1D(aa.AbstractDataset):
[docs] def __init__(
self,
data: aa.Array1D,
noise_map: aa.Array1D,
pre_cti_data: aa.Array1D,
layout: Layout1D,
fpr_value: Optional[float] = None,
settings: SettingsDataset1D = SettingsDataset1D(),
settings_dict: Optional[Dict] = None,
):
super().__init__(data=data, noise_map=noise_map, settings=settings)
self.data = data
self.noise_map = noise_map
self.pre_cti_data = pre_cti_data
self.layout = layout
if fpr_value is None:
fpr_value = np.round(
np.median(
self.layout.extract.fpr.stacked_array_1d_from(
array=self.data,
settings=SettingsExtract(
pixels_from_end=min(
5, self.layout.extract.fpr.total_pixels_min
)
),
)
),
2,
)
self.fpr_value = fpr_value
self.settings_dict = settings_dict
def apply_mask(self, mask: aa.Mask1D) -> "Dataset1D":
data = aa.Array1D(values=self.data, mask=mask).native
noise_map = aa.Array1D(values=self.noise_map.astype("float"), mask=mask).native
return Dataset1D(
data=data,
noise_map=noise_map,
pre_cti_data=self.pre_cti_data,
layout=self.layout,
fpr_value=self.fpr_value,
settings_dict=self.settings_dict,
)
def apply_settings(self, settings: SettingsDataset1D) -> "Dataset1D":
return self
@classmethod
def from_fits(
cls,
pixel_scales: aa.type.PixelScales,
layout: Layout1D,
data_path: Optional[Union[Path, str]] = None,
data_hdu: int = 0,
noise_map_path: Optional[Union[Path, str]] = None,
noise_map_hdu: int = 0,
noise_map_from_single_value: float = None,
pre_cti_data_path: Optional[Union[Path, str]] = None,
pre_cti_data_hdu: int = 0,
pre_cti_data: aa.Array1D = None,
settings_dict: Optional[Dict] = None,
):
"""
Load 1D dataset from multiple .fits file.
For each attribute of the 1D dataset (e.g. `data`, `noise_map`, `pre_cti_data`) the path to the .fits and
the `hdu` containing the data can be specified.
The `noise_map` assumes the noise value in each `data` value are independent, where these values are the
RMS standard deviation error in each pixel.
If the dataset has a mask associated with it (e.g. in a `mask.fits` file) the file must be loaded separately
via the `Mask1D` object and applied to the imaging after loading via fits using the `from_fits` method.
Parameters
----------
pixel_scales
The (y,x) arcsecond-to-pixel units conversion factor of every pixel. If this is input as a `float`,
it is converted to a (float, float).
layout
The layout of the 1D dataset, containing information like where the FPR and EPER are located.
data_path
The path to the data .fits file containing the data (e.g. '/path/to/data.fits').
data_hdu
The hdu the image data is contained in the .fits file specified by `data_path`.
noise_map_path
The path to the noise_map .fits file containing the noise_map (e.g. '/path/to/noise_map.fits').
noise_map_hdu
The hdu the noise map is contained in the .fits file specified by `noise_map_path`.
noise_map_from_single_value
Creates a `noise_map` of constant values if this is input instead of loading via .fits.
pre_cti_data_path
The path to the pre CTI data .fits file containing the image data (e.g. '/path/to/pre_cti_data.fits').
pre_cti_data_hdu
The hdu the pre cti data is contained in the .fits file specified by `pre_cti_data_path`.
pre_cti_data
Manually input the pre CTI data as an `Array1D` instead of loading it via a .fits file.
settings_dict
A dictionary of settings associated with the charge injeciton imaging (e.g. voltage settings) which is
used for visualization.
"""
data = aa.Array1D.from_fits(
file_path=data_path, hdu=data_hdu, pixel_scales=pixel_scales
)
if noise_map_path is not None:
noise_map = aa.util.array_1d.numpy_array_1d_via_fits_from(
file_path=noise_map_path, hdu=noise_map_hdu
).astype("float")
else:
noise_map = np.ones(data.shape_native) * noise_map_from_single_value
noise_map = aa.Array1D.no_mask(values=noise_map, pixel_scales=pixel_scales)
if pre_cti_data_path is not None and pre_cti_data is None:
pre_cti_data = aa.Array1D.from_fits(
file_path=pre_cti_data_path,
hdu=pre_cti_data_hdu,
pixel_scales=pixel_scales,
)
else:
raise exc.LayoutException(
"Cannot estimate pre_cti_data data from non-uniform charge injectiono pattern"
)
pre_cti_data = aa.Array1D.no_mask(
values=pre_cti_data.native, pixel_scales=pixel_scales
)
return Dataset1D(
data=data,
noise_map=noise_map,
pre_cti_data=pre_cti_data,
layout=layout,
settings_dict=settings_dict,
)
def output_to_fits(
self,
data_path: Union[Path, str],
noise_map_path: Optional[Union[Path, str]] = None,
pre_cti_data_path: Optional[Union[Path, str]] = None,
overwrite: bool = False,
):
"""
Output the 1D dataset to multiple .fits file.
For each attribute of the 1D dataset data (e.g. `data`, `noise_map`, `pre_cti_data`) the path to
the .fits can be specified, with `hdu=0` assumed automatically.
If the `data` has been masked, the masked data is output to .fits files. A mask can be separately output to
a file `mask.fits` via the `Mask` objects `output_to_fits` method.
Parameters
----------
data_path
The path to the data .fits file where the image data is output (e.g. '/path/to/data.fits').
noise_map_path
The path to the noise_map .fits where the noise_map is output (e.g. '/path/to/noise_map.fits').
pre_cti_data_path
The path to the pre CTI data .fits file where the pre CTI data is output (e.g. '/path/to/pre_cti_data.fits').
overwrite
If `True`, the .fits files are overwritten if they already exist, if `False` they are not and an
exception is raised.
"""
self.data.output_to_fits(file_path=data_path, overwrite=overwrite)
self.noise_map.output_to_fits(file_path=noise_map_path, overwrite=overwrite)
self.pre_cti_data.output_to_fits(
file_path=pre_cti_data_path, overwrite=overwrite
)
@classmethod
def from_pixel_line_dict(
cls,
pixel_line_dict: dict,
size: int,
) -> "Dataset1D":
"""
Parse a pixel line output from the warm-pixels script.
Pixel lines are individual or averaged lines found by searching for
warm pixels or consistent warm pixels in CCD data. The warm pixel
and its are extracted and saved as a JSON which can then be loaded
and fit as part of autocti.
Parameters
----------
pixel_line_dict
A dictionary describing a pixel line collection.
e.g.
{
"location": [
2,
4,
],
"flux": 1234.,
"data": [
5.0,
3.0,
2.0,
1.0,
],
"noise": [
1.0,
1.0,
1.0,
1.0,
]
}
location
The location of the warm pixel in (row, column) where row is the
distance to the serial register - 1
flux
The computed flux of the warm pixel prior to CTI
data
The extracted pixel line. A 1D array where the first entry is
the warm pixel (FPR) and the remaining entries are the trail
(EPER)
noise
The noise map for the pixel line.
size
The size of the CCD. That is, the number of pixels in the parallel
direction.
Returns
-------
A Dataset1D initialised to represent the pixel line. The pixel line
and noise are embedded in Array1Ds of the same size as the array in
the parallel direction
"""
serial_distance, _ = map(int, pixel_line_dict["location"])
def make_array(data):
array = np.zeros(size)
array[serial_distance : serial_distance + len(data)] = data
return aa.Array1D.no_mask(array, pixel_scales=0.1)
return Dataset1D(
data=make_array(pixel_line_dict["data"]),
noise_map=make_array(pixel_line_dict["noise"]),
pre_cti_data=make_array(np.array([pixel_line_dict["flux"]])),
layout=Layout1D(
shape_1d=(size,),
region_list=[
aa.Region1D(region=(serial_distance, serial_distance + 1))
],
),
)