diff --git a/src/roman_datamodels/maker_utils/__init__.py b/src/roman_datamodels/_maker_utils/__init__.py similarity index 89% rename from src/roman_datamodels/maker_utils/__init__.py rename to src/roman_datamodels/_maker_utils/__init__.py index 452cf6d0..f413def7 100644 --- a/src/roman_datamodels/maker_utils/__init__.py +++ b/src/roman_datamodels/_maker_utils/__init__.py @@ -1,5 +1,3 @@ -from roman_datamodels.datamodels import MODEL_REGISTRY as _MODEL_REGISTRY # Hide from public API - from ._basic_meta import * # noqa: F403 from ._common_meta import * # noqa: F403 from ._datamodels import * # noqa: F403 @@ -19,9 +17,6 @@ "WfiMosaic": "mk_level3_mosaic", } -# This is static at runtime, so we might as well compute it once -NODE_REGISTRY = {mdl: node for node, mdl in _MODEL_REGISTRY.items()} - def _camel_case_to_snake_case(value): """ @@ -99,4 +94,4 @@ def mk_datamodel(model_class, **kwargs): `roman_datamodels.datamodels.Datamodel` """ - return model_class(mk_node(NODE_REGISTRY[model_class], **kwargs)) + return model_class.create_default(**kwargs) diff --git a/src/roman_datamodels/maker_utils/_base.py b/src/roman_datamodels/_maker_utils/_base.py similarity index 100% rename from src/roman_datamodels/maker_utils/_base.py rename to src/roman_datamodels/_maker_utils/_base.py diff --git a/src/roman_datamodels/maker_utils/_basic_meta.py b/src/roman_datamodels/_maker_utils/_basic_meta.py similarity index 100% rename from src/roman_datamodels/maker_utils/_basic_meta.py rename to src/roman_datamodels/_maker_utils/_basic_meta.py diff --git a/src/roman_datamodels/maker_utils/_common_meta.py b/src/roman_datamodels/_maker_utils/_common_meta.py similarity index 100% rename from src/roman_datamodels/maker_utils/_common_meta.py rename to src/roman_datamodels/_maker_utils/_common_meta.py diff --git a/src/roman_datamodels/maker_utils/_datamodels.py b/src/roman_datamodels/_maker_utils/_datamodels.py similarity index 99% rename from src/roman_datamodels/maker_utils/_datamodels.py rename to src/roman_datamodels/_maker_utils/_datamodels.py index 32ed5260..2319f030 100644 --- a/src/roman_datamodels/maker_utils/_datamodels.py +++ b/src/roman_datamodels/_maker_utils/_datamodels.py @@ -337,7 +337,7 @@ def mk_associations(*, shape=(2, 3, 1), filepath=None, **kwargs): Returns ------- - roman_datamodels.stnode.AssociationsModel + roman_datamodels.stnode.Associations """ associations = stnode.Associations() diff --git a/src/roman_datamodels/maker_utils/_fps.py b/src/roman_datamodels/_maker_utils/_fps.py similarity index 100% rename from src/roman_datamodels/maker_utils/_fps.py rename to src/roman_datamodels/_maker_utils/_fps.py diff --git a/src/roman_datamodels/maker_utils/_fps_basic_meta.py b/src/roman_datamodels/_maker_utils/_fps_basic_meta.py similarity index 100% rename from src/roman_datamodels/maker_utils/_fps_basic_meta.py rename to src/roman_datamodels/_maker_utils/_fps_basic_meta.py diff --git a/src/roman_datamodels/maker_utils/_fps_common_meta.py b/src/roman_datamodels/_maker_utils/_fps_common_meta.py similarity index 100% rename from src/roman_datamodels/maker_utils/_fps_common_meta.py rename to src/roman_datamodels/_maker_utils/_fps_common_meta.py diff --git a/src/roman_datamodels/maker_utils/_ref_files.py b/src/roman_datamodels/_maker_utils/_ref_files.py similarity index 99% rename from src/roman_datamodels/maker_utils/_ref_files.py rename to src/roman_datamodels/_maker_utils/_ref_files.py index 6811dac0..829fee57 100644 --- a/src/roman_datamodels/maker_utils/_ref_files.py +++ b/src/roman_datamodels/_maker_utils/_ref_files.py @@ -243,7 +243,7 @@ def mk_epsf(*, shape=(3, 6, 9, 361, 361), filepath=None, **kwargs): ---------- shape (optional, keyword-only) Shape of arrays in the model. - If shape is greater than 1D, the first dimension is used. + If shape is greater than 5D, the first 5 dimensions are used. filepath (optional, keyword-only) File name and path to write model to. @@ -376,7 +376,7 @@ def mk_inverselinearity(*, shape=(2, 4096, 4096), filepath=None, **kwargs): Returns ------- - roman_datamodels.stnode.InverseLinearityRef + roman_datamodels.stnode.InverselinearityRef """ if len(shape) != 3: shape = (2, 4096, 4096) diff --git a/src/roman_datamodels/maker_utils/_tagged_nodes.py b/src/roman_datamodels/_maker_utils/_tagged_nodes.py similarity index 100% rename from src/roman_datamodels/maker_utils/_tagged_nodes.py rename to src/roman_datamodels/_maker_utils/_tagged_nodes.py diff --git a/src/roman_datamodels/maker_utils/_tvac.py b/src/roman_datamodels/_maker_utils/_tvac.py similarity index 100% rename from src/roman_datamodels/maker_utils/_tvac.py rename to src/roman_datamodels/_maker_utils/_tvac.py diff --git a/src/roman_datamodels/maker_utils/_tvac_basic_meta.py b/src/roman_datamodels/_maker_utils/_tvac_basic_meta.py similarity index 100% rename from src/roman_datamodels/maker_utils/_tvac_basic_meta.py rename to src/roman_datamodels/_maker_utils/_tvac_basic_meta.py diff --git a/src/roman_datamodels/maker_utils/_tvac_common_meta.py b/src/roman_datamodels/_maker_utils/_tvac_common_meta.py similarity index 100% rename from src/roman_datamodels/maker_utils/_tvac_common_meta.py rename to src/roman_datamodels/_maker_utils/_tvac_common_meta.py diff --git a/src/roman_datamodels/datamodels/_core.py b/src/roman_datamodels/datamodels/_core.py index 9ab38c23..8cdd494e 100644 --- a/src/roman_datamodels/datamodels/_core.py +++ b/src/roman_datamodels/datamodels/_core.py @@ -77,11 +77,28 @@ class DataModel(abc.ABC): crds_observatory = "roman" - @abc.abstractproperty + @property + @abc.abstractmethod def _node_type(self): """Define the top-level node type for this model""" pass + @classmethod + def create_default(cls, *, filepath=None, **kwargs): + """ + Create a default instance of this model using the maker_utils + """ + from roman_datamodels._maker_utils import mk_node + + # don't use the maker util save, instead save through + # the datamodel itself + default = cls(mk_node(cls._node_type, filepath=None, **kwargs)) + + if filepath is not None: + default.save(filepath) + + return default + def __init_subclass__(cls, **kwargs): """Register each subclass in the MODEL_REGISTRY""" super().__init_subclass__(**kwargs) diff --git a/src/roman_datamodels/datamodels/_datamodels.py b/src/roman_datamodels/datamodels/_datamodels.py index 563f484c..9a95b132 100644 --- a/src/roman_datamodels/datamodels/_datamodels.py +++ b/src/roman_datamodels/datamodels/_datamodels.py @@ -51,6 +51,31 @@ def __init__(self, init=None, **kwargs): class MosaicModel(_RomanDataModel): _node_type = stnode.WfiMosaic + @classmethod + def create_default(cls, shape=(4088, 4088), n_images=2, filepath=None, **kwargs): + """ + Create a MosaicModel with all data required for writing a file filled. + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape (y, x) of data array in the model (and + its corresponding dq/err arrays). Default is 4088 x 4088. + If shape is a tuple of length 3, the first element is assumed to be + n_images and will override the n_images parameter. + + n_images : int + Number of images used to create the level 3 image. Defaults to 2. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + + return super().create_default(shape=shape, n_images=n_images, filepath=filepath, **kwargs) + def append_individual_image_meta(self, meta): """ Add the contents of meta to the appropriate keyword in individual_image_meta as an astropy QTable. @@ -126,18 +151,113 @@ def append_individual_image_meta(self, meta): class ImageModel(_RomanDataModel): _node_type = stnode.WfiImage + @classmethod + def create_default(cls, shape=(4088, 4088), n_groups=8, filepath=None, **kwargs): + """ + Create a ImageModel with all data required for writing a file filled. + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape (y, x) of data array in the model (and + its corresponding dq/err arrays). This specified size does NOT include + the four-pixel border of reference pixels - those are trimmed at level + 2. This size, however, is used to construct the additional arrays that + contain the original border reference pixels (i.e if shape = (10, 10), + the border reference pixel arrays will have (y, x) dimensions (14, 4) + and (4, 14)). Default is 4088 x 4088. + If shape is a tuple of length 3, the first element is assumed to be the + n_groups and will override any settings there. + + n_groups : int + (optional, keyword-only) The level 2 file is flattened, but it contains + arrays for the original reference pixels which remain 3D. n_groups + specifies what the z dimension of these arrays should be. Defaults to 8. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, n_groups=n_groups, filepath=filepath, **kwargs) + class ScienceRawModel(_RomanDataModel): _node_type = stnode.WfiScienceRaw + @classmethod + def create_default(cls, shape=(8, 4096, 4096), dq=False, filepath=None, **kwargs): + """ + Create a default instance of this model using the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) (z, y, x) Shape of data array. This includes a + four-pixel border representing the reference pixels. Default is + (8, 4096, 4096) + (8 integrations, 4088 x 4088 represent the science pixels, with the + additional being the border reference pixels). + + dq : bool + (optional, keyword-only) Toggle to add a data quality array for + dropout pixels + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, dq=dq, filepath=filepath, **kwargs) + class MsosStackModel(_RomanDataModel): _node_type = stnode.MsosStack + @classmethod + def create_default(cls, shape=(4096, 4096), filepath=None, **kwargs): + """ + Create a MsosStackModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) File name and path to write model to. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class RampModel(_RomanDataModel): _node_type = stnode.Ramp + @classmethod + def create_default(cls, shape=(8, 4096, 4096), filepath=None, **kwargs): + """ + Create a RampModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional) Shape (z, y, x) of data array in the model (and its + corresponding dq/err arrays). This specified size includes the + four-pixel border of reference pixels. Default is 8 x 4096 x 4096. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + @classmethod def from_science_raw(cls, model): """ @@ -165,7 +285,7 @@ def from_science_raw(cls, model): raise ValueError(f"Input must be one of {ALLOWED_MODELS}") # Create base ramp node with dummy values (for validation) - from roman_datamodels.maker_utils import mk_ramp + from roman_datamodels._maker_utils import mk_ramp ramp = mk_ramp(shape=model.shape) @@ -201,11 +321,49 @@ def node_update(ramp, other): class RampFitOutputModel(_RomanDataModel): _node_type = stnode.RampFitOutput + @classmethod + def create_default(cls, shape=(8, 4096, 4096), filepath=None, **kwargs): + """ + Create a RampFitOutputModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional) Shape (z, y, x) of data array in the model (and its + corresponding dq/err arrays). This specified size includes the + four-pixel border of reference pixels. Default is 8 x 4096 x 4096. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class AssociationsModel(_DataModel): # Need an init to allow instantiation from a JSON file _node_type = stnode.Associations + @classmethod + def create_default(cls, shape=(8, 4096, 4096), filepath=None, **kwargs): + """ + Create a AssociationsModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) The shape of the member elements of products. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + @classmethod def is_association(cls, asn_data): """ @@ -222,42 +380,216 @@ def is_association(cls, asn_data): class GuidewindowModel(_RomanDataModel): _node_type = stnode.Guidewindow + @classmethod + def create_default(cls, shape=(2, 8, 16, 32, 32), filepath=None, **kwargs): + """ + Create a GuideWondowModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class FlatRefModel(_DataModel): _node_type = stnode.FlatRef + @classmethod + def create_default(cls, shape=(4096, 4096), filepath=None, **kwargs): + """ + Create a FlatRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class AbvegaoffsetRefModel(_DataModel): _node_type = stnode.AbvegaoffsetRef + @classmethod + def create_default(cls, filepath=None, **kwargs): + """ + Create a AbvegaoffsetRefModel with all data required for writing the maker_utils + + Parameters + ---------- + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(filepath=filepath, **kwargs) + class ApcorrRefModel(_DataModel): _node_type = stnode.ApcorrRef + @classmethod + def create_default(cls, shape=(10,), filepath=None, **kwargs): + """ + Create a ApcorrRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class DarkRefModel(_DataModel): _node_type = stnode.DarkRef + @classmethod + def create_default(cls, shape=(2, 4096, 4096), filepath=None, **kwargs): + """ + Create a DarkRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class DistortionRefModel(_DataModel): _node_type = stnode.DistortionRef + @classmethod + def create_default(cls, filepath=None, **kwargs): + """ + Create a DistortionRefModel with all data required for writing the maker_utils + + Parameters + ---------- + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(filepath=filepath, **kwargs) + class EpsfRefModel(_DataModel): _node_type = stnode.EpsfRef + @classmethod + def create_default(cls, shape=(3, 6, 9, 361, 361), filepath=None, **kwargs): + """ + Create EpsfRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class GainRefModel(_DataModel): _node_type = stnode.GainRef + @classmethod + def create_default(cls, shape=(4096, 4096), filepath=None, **kwargs): + """ + Create GainRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class IpcRefModel(_DataModel): _node_type = stnode.IpcRef + @classmethod + def create_default(cls, shape=(3, 3), filepath=None, **kwargs): + """ + Create IpcRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class LinearityRefModel(_DataModel): _node_type = stnode.LinearityRef + @classmethod + def create_default(cls, shape=(2, 4096, 4096), filepath=None, **kwargs): + """ + Create LinearityRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + def get_primary_array_name(self): """ Returns the name "primary" array for this model, which @@ -271,6 +603,24 @@ def get_primary_array_name(self): class InverselinearityRefModel(_DataModel): _node_type = stnode.InverselinearityRef + @classmethod + def create_default(cls, shape=(2, 4096, 4096), filepath=None, **kwargs): + """ + Create InverselinearityRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + def get_primary_array_name(self): """ Returns the name "primary" array for this model, which @@ -284,6 +634,24 @@ def get_primary_array_name(self): class MaskRefModel(_DataModel): _node_type = stnode.MaskRef + @classmethod + def create_default(cls, shape=(4096, 4096), filepath=None, **kwargs): + """ + Create MaskRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + def get_primary_array_name(self): """ Returns the name "primary" array for this model, which @@ -297,46 +665,264 @@ def get_primary_array_name(self): class PixelareaRefModel(_DataModel): _node_type = stnode.PixelareaRef + @classmethod + def create_default(cls, shape=(4096, 4096), filepath=None, **kwargs): + """ + Create PixelareaRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class ReadnoiseRefModel(_DataModel): _node_type = stnode.ReadnoiseRef + @classmethod + def create_default(cls, shape=(4096, 4096), filepath=None, **kwargs): + """ + Create ReadnoiseRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class SuperbiasRefModel(_DataModel): _node_type = stnode.SuperbiasRef + @classmethod + def create_default(cls, shape=(4096, 4096), filepath=None, **kwargs): + """ + Create SuperbiasRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class SaturationRefModel(_DataModel): _node_type = stnode.SaturationRef + @classmethod + def create_default(cls, shape=(4096, 4096), filepath=None, **kwargs): + """ + Create SaturationRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class WfiImgPhotomRefModel(_DataModel): _node_type = stnode.WfiImgPhotomRef + @classmethod + def create_default(cls, filepath=None, **kwargs): + """ + Create WfiImgPhotomRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(filepath=filepath, **kwargs) + class RefpixRefModel(_DataModel): _node_type = stnode.RefpixRef + @classmethod + def create_default(cls, shape=(32, 286721), filepath=None, **kwargs): + """ + Create RefpixRefModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) Shape of arrays in the model. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class FpsModel(_DataModel): _node_type = stnode.Fps + @classmethod + def create_default(cls, shape=(8, 4096, 4096), filepath=None, **kwargs): + """ + Create FpsModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) (z, y, x) Shape of data array. This includes a + four-pixel border representing the reference pixels. Default is + (8, 4096, 4096) + (8 integrations, 4088 x 4088 represent the science pixels, with the + additional being the border reference pixels). + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class TvacModel(_DataModel): _node_type = stnode.Tvac + @classmethod + def create_default(cls, shape=(8, 4096, 4096), filepath=None, **kwargs): + """ + Create TvacModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) (z, y, x) Shape of data array. This includes a + four-pixel border representing the reference pixels. Default is + (8, 4096, 4096) + (8 integrations, 4088 x 4088 represent the science pixels, with the + additional being the border reference pixels). + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class MosaicSourceCatalogModel(_RomanDataModel): _node_type = stnode.MosaicSourceCatalog + @classmethod + def create_default(cls, filepath=None, **kwargs): + """ + Create MosaicSourceCatalogModel with all data required for writing the maker_utils + + Parameters + ---------- + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(filepath=filepath, **kwargs) + class MosaicSegmentationMapModel(_RomanDataModel): _node_type = stnode.MosaicSegmentationMap + @classmethod + def create_default(cls, shape=(4096, 4096), filepath=None, **kwargs): + """ + Create a MosaicSegmentationMapModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) File name and path to write model to. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) + class SourceCatalogModel(_RomanDataModel): _node_type = stnode.SourceCatalog + @classmethod + def create_default(cls, filepath=None, **kwargs): + """ + Create SourceCatalogModel with all data required for writing the maker_utils + + Parameters + ---------- + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(filepath=filepath, **kwargs) + class SegmentationMapModel(_RomanDataModel): _node_type = stnode.SegmentationMap + + @classmethod + def create_default(cls, shape=(4096, 4096), filepath=None, **kwargs): + """ + Create a SegmentationMapModel with all data required for writing the maker_utils + + Parameters + ---------- + shape : tuple, int + (optional, keyword-only) File name and path to write model to. + + filepath : str + (optional) File name and path to write model to. + + kwargs : dict[str, Any] + (optional) Override default construction for any node + """ + return super().create_default(shape=shape, filepath=filepath, **kwargs) diff --git a/src/roman_datamodels/maker_utils.py b/src/roman_datamodels/maker_utils.py new file mode 100644 index 00000000..b7bf6f1a --- /dev/null +++ b/src/roman_datamodels/maker_utils.py @@ -0,0 +1,10 @@ +import warnings + +from ._maker_utils import * # noqa: F403 + +_MSG = """ +Use of `roman_datamodels.maker_utils` is deprecated and will be removed in the +future! Please use the `.create_default()` constructor for the datamodel that +you wish to make a default datamodel for. +""" +warnings.warn(_MSG, DeprecationWarning, stacklevel=2) diff --git a/tests/test_dqflags.py b/tests/test_dqflags.py index 02bab7eb..58b464ff 100644 --- a/tests/test_dqflags.py +++ b/tests/test_dqflags.py @@ -6,7 +6,7 @@ from roman_datamodels import datamodels as rdm from roman_datamodels import dqflags -from roman_datamodels.maker_utils import mk_datamodel +from roman_datamodels._maker_utils import mk_datamodel def _is_power_of_two(x): diff --git a/tests/test_maker_utils.py b/tests/test_maker_utils.py index d46f9edf..6432910a 100644 --- a/tests/test_maker_utils.py +++ b/tests/test_maker_utils.py @@ -7,9 +7,9 @@ from astropy import units as u from astropy.time import Time -from roman_datamodels import datamodels, maker_utils, stnode +from roman_datamodels import _maker_utils, datamodels, stnode +from roman_datamodels._maker_utils import _ref_files as ref_files from roman_datamodels.datamodels._datamodels import _RomanDataModel -from roman_datamodels.maker_utils import _ref_files as ref_files from roman_datamodels.testing import assert_node_equal @@ -20,7 +20,7 @@ def test_maker_utility_implemented(node_class): (note: will be using full defaults for this one) """ - instance = maker_utils.mk_node(node_class) + instance = _maker_utils.mk_node(node_class) assert isinstance(instance, node_class) @@ -34,7 +34,7 @@ def test_instance_valid(node_class): is valid against its schema. """ with asdf.AsdfFile() as af: - af["node"] = maker_utils.mk_node(node_class, shape=(8, 8, 8)) + af["node"] = _maker_utils.mk_node(node_class, shape=(8, 8, 8)) af.validate() @@ -43,7 +43,7 @@ def test_instance_valid(node_class): @pytest.mark.filterwarnings("ignore:Input shape must be 4D") @pytest.mark.filterwarnings("ignore:Input shape must be 5D") def test_no_extra_fields(node_class, manifest): - instance = maker_utils.mk_node(node_class, shape=(8, 8, 8)) + instance = _maker_utils.mk_node(node_class, shape=(8, 8, 8)) instance_keys = set(instance.keys()) schema_uri = next(t["schema_uri"] for t in manifest["tags"] if t["tag_uri"] == instance.tag) @@ -65,7 +65,7 @@ def test_ref_files_all(name): """ Meta test to confirm that the __all__ in _ref_files.py has an entry for every ref file maker. """ - from roman_datamodels.maker_utils import _camel_case_to_snake_case + from roman_datamodels._maker_utils import _camel_case_to_snake_case method_name = f"mk_{_camel_case_to_snake_case(name)}" assert method_name[:-4] in ref_files.__all__ @@ -76,12 +76,12 @@ def test_make_datamodel_tests(node_class): """ Meta test to confirm that correct tests exist for each datamodel maker utility. """ - from roman_datamodels.maker_utils import _camel_case_to_snake_case + from roman_datamodels._maker_utils import _camel_case_to_snake_case from . import test_models as tests name = node_class.__name__ - name = maker_utils.SPECIAL_MAKERS.get(name, _camel_case_to_snake_case(name)) + name = _maker_utils.SPECIAL_MAKERS.get(name, _camel_case_to_snake_case(name)) if name.startswith("mk_"): name = name[3:] if name.endswith("_ref"): @@ -97,10 +97,10 @@ def test_deprecated(): """ with pytest.warns(DeprecationWarning): - maker_utils.mk_rampfitoutput(shape=(8, 8, 8)) + _maker_utils.mk_rampfitoutput(shape=(8, 8, 8)) -@pytest.mark.parametrize("model_class", [mdl for mdl in maker_utils.NODE_REGISTRY]) +@pytest.mark.parametrize("model_class", [mdl for mdl in datamodels.MODEL_REGISTRY.values()]) @pytest.mark.filterwarnings("ignore:This function assumes shape is 2D") @pytest.mark.filterwarnings("ignore:Input shape must be 4D") @pytest.mark.filterwarnings("ignore:Input shape must be 5D") @@ -109,7 +109,7 @@ def test_datamodel_maker(model_class): Test that the datamodel maker utility creates a valid datamodel. """ - model = maker_utils.mk_datamodel(model_class, shape=(8, 8, 8)) + model = _maker_utils.mk_datamodel(model_class, shape=(8, 8, 8)) assert isinstance(model, model_class) model.validate() @@ -183,11 +183,11 @@ def mutate_node(node): return mutate_value(node) # Create a node then mutate it. - node = maker_utils.mk_node(node_class, shape=(8, 8, 8)) + node = _maker_utils.mk_node(node_class, shape=(8, 8, 8)) kwargs = mutate_node(node) # Create a new node using the recorded mutation data. Then check it is equal to the mutated object. - new_node = maker_utils.mk_node(node_class, **kwargs) + new_node = _maker_utils.mk_node(node_class, **kwargs) assert new_node is not node assert_node_equal(new_node, node) @@ -198,7 +198,7 @@ def test_keyword_only(node_class): Ensure all the maker utils at the top level are keyword only. """ - maker = maker_utils._get_node_maker(node_class) + maker = _maker_utils._get_node_maker(node_class) sig = inspect.signature(maker) assert "kwargs" in sig.parameters @@ -216,6 +216,6 @@ def test_mk_level2_image_shape(): Regression test for https://github.com/spacetelescope/roman_datamodels/issues/377 where n_groups was incorrect when provided a 3d shape """ - n = maker_utils.mk_level2_image(shape=(2, 3, 4)) + n = _maker_utils.mk_level2_image(shape=(2, 3, 4)) assert n.amp33.shape == (2, 4096, 128) assert n.data.shape == (3, 4) diff --git a/tests/test_models.py b/tests/test_models.py index e1ee6f84..a810e2b1 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -10,8 +10,8 @@ from astropy.table import QTable, Table from numpy.testing import assert_array_equal +from roman_datamodels import _maker_utils as utils from roman_datamodels import datamodels, stnode, validate -from roman_datamodels import maker_utils as utils from roman_datamodels.testing import assert_node_equal from .conftest import MANIFEST diff --git a/tests/test_open.py b/tests/test_open.py index 170224ed..12f29dc1 100644 --- a/tests/test_open.py +++ b/tests/test_open.py @@ -8,8 +8,8 @@ from astropy.io import fits from numpy.testing import assert_array_equal +from roman_datamodels import _maker_utils as utils from roman_datamodels import datamodels, stnode -from roman_datamodels import maker_utils as utils from roman_datamodels.testing import assert_node_equal diff --git a/tests/test_stnode.py b/tests/test_stnode.py index 4f7e78cf..2bf2fd2e 100644 --- a/tests/test_stnode.py +++ b/tests/test_stnode.py @@ -4,9 +4,9 @@ import asdf import pytest -from roman_datamodels import datamodels, maker_utils, stnode, validate -from roman_datamodels import maker_utils as utils -from roman_datamodels.maker_utils._base import NOFN, NONUM, NOSTR +from roman_datamodels import _maker_utils, datamodels, stnode, validate +from roman_datamodels import _maker_utils as utils +from roman_datamodels._maker_utils._base import NOFN, NONUM, NOSTR from roman_datamodels.testing import assert_node_equal, assert_node_is_copy, wraps_hashable from .conftest import MANIFEST @@ -31,7 +31,7 @@ def test_generated_node_classes(tag): @pytest.mark.filterwarnings("ignore:Input shape must be 5D") def test_copy(node_class): """Demonstrate nodes can copy themselves, but don't always deepcopy.""" - node = maker_utils.mk_node(node_class, shape=(8, 8, 8)) + node = _maker_utils.mk_node(node_class, shape=(8, 8, 8)) node_copy = node.copy() # Assert the copy is shallow: @@ -49,7 +49,7 @@ def test_copy(node_class): @pytest.mark.filterwarnings("ignore:Input shape must be 4D") @pytest.mark.filterwarnings("ignore:Input shape must be 5D") def test_deepcopy_model(node_class): - node = maker_utils.mk_node(node_class, shape=(8, 8, 8)) + node = _maker_utils.mk_node(node_class, shape=(8, 8, 8)) model = datamodels.MODEL_REGISTRY[node_class](node) model_copy = model.copy() @@ -93,7 +93,7 @@ def test_wfi_mode(): def test_serialization(node_class, tmp_path): file_path = tmp_path / "test.asdf" - node = maker_utils.mk_node(node_class, shape=(8, 8, 8)) + node = _maker_utils.mk_node(node_class, shape=(8, 8, 8)) with asdf.AsdfFile() as af: af["node"] = node af.write_to(file_path) @@ -213,7 +213,7 @@ def test_nuke_validation(nuke_env_var, tmp_path): # Break model without outside validation with nullcontext() if nuke_env_var[1] else pytest.warns(validate.ValidationWarning): - mdl = datamodels.WfiImgPhotomRefModel(maker_utils.mk_wfi_img_photom()) + mdl = datamodels.WfiImgPhotomRefModel(_maker_utils.mk_wfi_img_photom()) mdl._instance["phot_table"] = "THIS IS NOT VALID" # Broken can be written to file @@ -259,7 +259,7 @@ def test_node_representation(model): the representation of the object. The reported issue was with ``mdl.meta.instrument``, so that is directly checked here. """ - mdl = maker_utils.mk_datamodel(model) + mdl = _maker_utils.mk_datamodel(model) if hasattr(mdl, "meta"): if isinstance(mdl, datamodels.MosaicModel | datamodels.MosaicSegmentationMapModel | datamodels.MosaicSourceCatalogModel):