diff --git a/src/calviper/config/jones.param.json b/src/calviper/config/jones.param.json new file mode 100644 index 0000000..d99e6fa --- /dev/null +++ b/src/calviper/config/jones.param.json @@ -0,0 +1,11 @@ +{ + "CalibrationMatrix.create_jones": { + "factory": { + "nullable": false, + "required": true, + "type": [ + "string" + ] + } + } +} \ No newline at end of file diff --git a/src/calviper/config/subclasses.config.json b/src/calviper/config/subclasses.config.json new file mode 100644 index 0000000..e112191 --- /dev/null +++ b/src/calviper/config/subclasses.config.json @@ -0,0 +1,3 @@ +{ + "GainMatrixDataset": "calviper.factory.gain" +} \ No newline at end of file diff --git a/src/calviper/factory/_jones.py b/src/calviper/factory/_jones.py deleted file mode 100644 index ebc3cec..0000000 --- a/src/calviper/factory/_jones.py +++ /dev/null @@ -1,103 +0,0 @@ -import numpy as np -import xarray as xr - -import calviper.math.tools as tools -import toolviper.utils.logger as logger - -from calviper.base import JonesMatrix -from typing import TypeVar, Type, Union - -T = TypeVar('T', bound='Parent') - - -class GainJones(JonesMatrix): - def __init__(self): - super(GainJones, self).__init__() - - # Public parent variable - self.n_times = None - self.type: Union[str, None] = "G" - - #self.dtype = np.complex64 - self.n_polarizations: Union[int, None] = 4 - self.n_parameters: Union[int, None] = None - self.n_baselines: Union[int, None] = None - self.n_channels: Union[int, None] = None - self.channel_dependent_parameters: bool = False - - # Private variables - self._parameters = None - self._matrix = None - self._antenna_map = None - - self.name: str = "GainJonesMatrix" - - # This is just an example of how this would be done. There should certainly be checks and customization - # but for now just set the values simply as the original code doesn't do anything more complicated for now. - @property - def parameters(self) -> np.ndarray: - return self._parameters - - @parameters.setter - def parameters(self, array: np.ndarray) -> None: - self._parameters = array - - @property - def matrix(self) -> np.ndarray: - return self._matrix - - @matrix.setter - def matrix(self, array: np.ndarray) -> None: - #(self.n_times, self.n_baselines, self.n_channels, _, _) = array.shape - - # There should be a check on the shape here. I don't think we want to allow, for instance, - # an axis to be averaged while also having the dimensions stored in the object not change. - self._matrix = array - - def calculate(self) -> None: - #self.initialize_jones() - - self.matrix = np.identity(2, dtype=np.complex64) - self.matrix = np.tile(self.matrix, [self.n_times, self.n_antennas, self.n_channel_matrices, 1, 1]) - - @classmethod - def from_visibility(cls: Type[T], dataset: xr.Dataset, time_dependence: bool = False) -> T: - """ - Build Jones matrix from visibility data. - :param dataset: - :param time_dependence: - :return: - """ - - shape = dataset.VISIBILITY.shape - - # This will encode the antenna values into an integer list. - index, antennas = tools.encode(dataset.baseline_antenna1_name.to_numpy()) - - instance = cls() - - # There should be a gain value for each independent antenna. Here we choose antenna_1 names but either - # would work fine. - instance.n_antennas = np.unique(dataset.baseline_antenna1_name).shape[0] - - # With no polarization and one channel, n_parameters = n_antennas - # instance.n_parameters = n_parameters - instance.n_parameters = instance.n_antennas * instance.n_polarizations - - polarization_axis_ = int(instance.n_polarizations // 2) - - identity = np.identity(polarization_axis_, dtype=np.complex64) - - instance._antenna_map = {i: str(antenna) for i, antenna in enumerate(antennas)} - - instance.n_times, instance.n_baselines, instance.n_channels, instance.n_polarizations = shape - - # Initialize default parameters - instance.parameters = np.empty((instance.n_times, instance.n_channels, instance.n_parameters), - dtype=np.complex64) - - # Build on the above idea ... wrong as they may be. Simplicity first. - # instance.matrix = np.tile(identity, reps=[*shape, 1, 1]) - instance.matrix = np.tile(identity, reps=[instance.n_times, instance.n_baselines, instance.n_channels, 1, 1]) - - return instance diff --git a/src/calviper/factory/accessor.py b/src/calviper/factory/accessor.py index 42d068b..b6b894a 100644 --- a/src/calviper/factory/accessor.py +++ b/src/calviper/factory/accessor.py @@ -1,11 +1,24 @@ +import pathlib +import json +import importlib + +import toolviper.utils.logger as logger def register_subclass(cls): """ There will be a lot more added here bt for testing ..... """ - import importlib - if cls.__name__ == "GainMatrixDataSet": - importlib.import_module("calviper.factory.gain") + config_path = str(pathlib.Path(__file__).parent.parent.joinpath("config").joinpath("subclasses.config.json").resolve()) + + with open(config_path, "r") as file: + object_ = json.load(file) + if cls.__name__ in object_: + importlib.import_module(object_[cls.__name__]) + + else: + logger.error(f"No subclass named {cls.__name__}") + + + return cls - return cls \ No newline at end of file diff --git a/src/calviper/factory/base.py b/src/calviper/factory/base.py index 49c73be..3b34f45 100644 --- a/src/calviper/factory/base.py +++ b/src/calviper/factory/base.py @@ -28,13 +28,13 @@ def __init__(self): self.type: Union[str, None] = None #self.dtype: Union[type, None] = None - self.n_times: Union[int, None] = None - self.n_antennas: Union[int, None] = None - self.n_channels: Union[int, None] = None - self.n_polarizations: Union[int, None] = None + #self.n_times: Union[int, None] = None + #self.n_antennas: Union[int, None] = None + #self.n_channels: Union[int, None] = None + #self.n_polarizations: Union[int, None] = None #self.n_channel_matrices: Union[int, None] = None - self.n_parameters: Union[int, None] = None + #self.n_parameters: Union[int, None] = None self.caltable_name: Union[str, None] = None #self.channel_dependent_parameters: bool = False @@ -51,16 +51,6 @@ def shape(self) -> tuple: def shape(self, shape: tuple): return NotImplementedError - @property - @abstractmethod - def parameters(self) -> np.ndarray: - raise NotImplementedError - - @parameters.setter - @abstractmethod - def parameters(self, array: np.array) -> None: - raise NotImplementedError - @property @abstractmethod def matrix(self) -> np.ndarray: @@ -71,13 +61,10 @@ def matrix(self) -> np.ndarray: def matrix(self, array: np.array) -> np.ndarray: raise NotImplementedError - @abstractmethod - def calculate(self) -> None: - raise NotImplementedError - # Inherited method properties @classmethod def from_parameters(cls: Type[T], parameters: dict) -> T: + # This is deprecated at the moment, I'll add it back in when I figure out what is needed. import inspect obj = cls() @@ -105,7 +92,7 @@ def from_parameters(cls: Type[T], parameters: dict) -> T: vars(obj).update(updated_params) return obj - + ''' @classmethod def from_visibility(cls: Type[T], data: xr.Dataset, time_dependence: bool = False) -> T: return cls @@ -113,7 +100,7 @@ def from_visibility(cls: Type[T], data: xr.Dataset, time_dependence: bool = Fals # Commenting all these out for the moment. They were written in line with George's original code # but that was based on a different workflow than I am foreseeing at the moment. These will be # added back, if needed, as work progresses. - ''' + def initialize_parameters(self, dtype: np.dtype, shape: tuple = None): # Set data type self.type = dtype diff --git a/src/calviper/factory/gain.py b/src/calviper/factory/gain.py index 8c48380..b96e216 100644 --- a/src/calviper/factory/gain.py +++ b/src/calviper/factory/gain.py @@ -1,11 +1,41 @@ +from abc import ABC + +import numpy as np import xarray as xr +import calviper.math.tools as tools +import toolviper.utils.logger as logger + +from calviper.factory.base import JonesMatrix +from typing import TypeVar, Type, Union + +T = TypeVar('T', bound='Parent') + @xr.register_dataset_accessor("gain") -class GainMatrix: +class GainMatrix(JonesMatrix, ABC): def __init__(self, dataset: xr.Dataset): + super(GainMatrix, self).__init__() self._object = dataset + self.type: str = "G" + self.dtype: Type = np.complex64 + self.channel_dependent_parameters: bool = False + + self._matrix: Union[None, np.ndarray] = None + @property def data(self): - return self._object \ No newline at end of file + return self._object + + @property + def matrix(self) -> np.ndarray: + return self._matrix + + @matrix.setter + def matrix(self, array: np.ndarray) -> None: + # (self.n_times, self.n_baselines, self.n_channels, _, _) = array.shape + + # There should be a check on the shape here. I don't think we want to allow, for instance, + # an axis to be averaged while also having the dimensions stored in the object not change. + self._matrix = array \ No newline at end of file diff --git a/src/calviper/factory/jones.py b/src/calviper/factory/jones.py index 4b200b5..c3dbeb7 100644 --- a/src/calviper/factory/jones.py +++ b/src/calviper/factory/jones.py @@ -16,22 +16,22 @@ class BaseJonesMatrix(ABC): - # Base calibration table abstract class + # Base calibration matrix abstract class @abstractmethod def generate(self, coords: dict) -> Union[xr.Dataset, None]: pass class JonesFactory(ABC): - # Base factory class for table factory + # Base factory class for matrix factory @abstractmethod def create_jones(self, factory: Union[None, str]): pass @accessor.register_subclass -class GainMatrixDataSet(BaseJonesMatrix): +class GainMatrixDataset(BaseJonesMatrix): - # This is intended to be an implementation of a gain table simulator. It is + # This is intended to be an implementation of a gain jones simulator. It is # currently very rough and filled with random numbers. Generally based on the # original cal.py def generate(self, coords: dict) -> None: @@ -107,10 +107,10 @@ class CalibrationMatrix(JonesFactory, ABC): def __init__(self): self.factory_list = { - "gain": GainMatrixDataSet, + "gain": GainMatrixDataset, } - #@toolviper.utils.parameter.validate() + @toolviper.utils.parameter.validate() def create_jones(self, factory: str) -> Union[BaseJonesMatrix, None]: try: return self.factory_list[factory]() diff --git a/src/calviper/jones.py b/src/calviper/jones.py index ebc3cec..7947ecb 100644 --- a/src/calviper/jones.py +++ b/src/calviper/jones.py @@ -1,3 +1,5 @@ +from abc import ABC + import numpy as np import xarray as xr @@ -10,7 +12,7 @@ T = TypeVar('T', bound='Parent') -class GainJones(JonesMatrix): +class GainJones(JonesMatrix, ABC): def __init__(self): super(GainJones, self).__init__() @@ -31,7 +33,7 @@ def __init__(self): self._antenna_map = None self.name: str = "GainJonesMatrix" - + ''' # This is just an example of how this would be done. There should certainly be checks and customization # but for now just set the values simply as the original code doesn't do anything more complicated for now. @property @@ -41,7 +43,7 @@ def parameters(self) -> np.ndarray: @parameters.setter def parameters(self, array: np.ndarray) -> None: self._parameters = array - + ''' @property def matrix(self) -> np.ndarray: return self._matrix @@ -53,7 +55,7 @@ def matrix(self, array: np.ndarray) -> None: # There should be a check on the shape here. I don't think we want to allow, for instance, # an axis to be averaged while also having the dimensions stored in the object not change. self._matrix = array - + ''' def calculate(self) -> None: #self.initialize_jones() @@ -101,3 +103,4 @@ def from_visibility(cls: Type[T], dataset: xr.Dataset, time_dependence: bool = F instance.matrix = np.tile(identity, reps=[instance.n_times, instance.n_baselines, instance.n_channels, 1, 1]) return instance + ''' \ No newline at end of file