From b7b95cb59c323c146399d4d49b75b8206a9b1fb5 Mon Sep 17 00:00:00 2001 From: jemorrison Date: Mon, 6 Apr 2026 15:31:06 -0700 Subject: [PATCH 01/13] added print --- jwst/cube_build/ifu_cube.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jwst/cube_build/ifu_cube.py b/jwst/cube_build/ifu_cube.py index c475e238018..243de2a5a48 100644 --- a/jwst/cube_build/ifu_cube.py +++ b/jwst/cube_build/ifu_cube.py @@ -307,6 +307,7 @@ def define_cubename(self): if self.output_type != "single": log.info(f"Output Name: {newname}") + return newname # _______________________________________________________________________ @@ -2664,7 +2665,8 @@ def setup_final_ifucube_model(self, model_ref): ifucube_model.update(model_ref) ifucube_model.meta.filename = self.output_name - + print('before blending', ifucube_model.meta.filename) + # Call model_blender if there are multiple inputs if len(self.input_models) > 1: saved_model_type = ifucube_model.meta.model_type From a0166a9c507b59c2eb15e10f9c5bb8e5c871449a Mon Sep 17 00:00:00 2001 From: jemorrison Date: Mon, 6 Apr 2026 15:49:38 -0700 Subject: [PATCH 02/13] testing --- jwst/cube_build/ifu_cube.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jwst/cube_build/ifu_cube.py b/jwst/cube_build/ifu_cube.py index 243de2a5a48..4b7a10853b8 100644 --- a/jwst/cube_build/ifu_cube.py +++ b/jwst/cube_build/ifu_cube.py @@ -2843,6 +2843,9 @@ def blend_output_metadata(self, ifu_cube): ifu_cube : `~stdatamodels.jwst.datamodels.IFUCubeModel` IFU cube data model """ + + print(ifu_cube) + print(self.input_models_this_cube) blendmeta.blendmodels( ifu_cube, self.input_models_this_cube, From 45dc715093fa52f3e928a21861b563654bd0bd38 Mon Sep 17 00:00:00 2001 From: jemorrison Date: Tue, 7 Apr 2026 10:57:51 -0700 Subject: [PATCH 03/13] Loaded for comments --- jwst/cube_build/cube_build_step.py | 63 +++++++++----------------- jwst/cube_build/ifu_cube.py | 71 ++++++++++++++---------------- 2 files changed, 56 insertions(+), 78 deletions(-) diff --git a/jwst/cube_build/cube_build_step.py b/jwst/cube_build/cube_build_step.py index 0e476283913..88d620bc95a 100644 --- a/jwst/cube_build/cube_build_step.py +++ b/jwst/cube_build/cube_build_step.py @@ -4,6 +4,7 @@ import asdf from astropy import units +from stdatamodels.jwst import datamodels from jwst.assign_wcs.util import update_s_region_keyword from jwst.cube_build import cube_build, data_types, ifu_cube @@ -22,13 +23,12 @@ class CubeBuildStep(Step): class_alias = "cube_build" spec = """ - pipeline = integer(2,3, default=3) # Set calwebb_spec2 or calwebb_spec3 channel = option('1','2','3','4','all',default='all') # Channel band = option('short','medium','long','short-medium','short-long','medium-short', \ 'medium-long', 'long-short', 'long-medium','all',default='all') # Band grating = option('prism','g140m','g140h','g235m','g235h','g395m','g395h','all',default='all') # Grating filter = option('clear','f100lp','f070lp','f170lp','f290lp','all',default='all') # Filter - output_type = option('band','channel','grating','multi',default=None) # Type IFUcube to create. + output_type = option('band','channel','grating','multi',default='band') # Type IFUcube to create. linear_wave = boolean(default=True) # Toggle between linear (True) and nonlinear (False) wavelength dimensions scalexy = float(default=0.0) # cube sample size to use for axis 1 and axis2, arc seconds scalew = float(default=0.0) # cube sample size to use for axis 3, microns @@ -216,34 +216,31 @@ def process(self, input_data): # Read in the input data and make a copy as needed. read_in_models = self.prepare_output(input_data) - # ________________________________________________________________________________ - # DataTypes - # Read in the input data - 2 formats are expected: - # 1. single model - # 2. model container - # figure out what type of data we have. Fill in the input_table.input_models. - # input_table.input_models is used in the rest of IFU Cube Building - # We need to do this in cube_build_step because we need to pass the data_model - # to CRDS to figure out what type of reference files to grab (MIRI or NIRSPEC) - # if the user has provided the filename - strip out .fits and pull out the - # base name. The cube_build software will attached the needed information: - # channel, sub-channel grating or filter to filename - # ________________________________________________________________________________ - input_table = data_types.DataTypes( - read_in_models, self.single, self.output_file, self.output_dir - ) - input_models = input_table.input_models - self.output_name_base = input_table.output_name + input_models = [] # Hold the input models to build cubes from + + if self.parent is None: # cube build is run stand alone + print("***** running cube build stand alone") + # Determine if input data was an association, a model, or a file + input_table = data_types.DataTypes( + read_in_models, self.single, self.output_file, self.output_dir + ) + input_models = input_table.input_models + self.output_name_base = input_table.output_name + + else: + self.output_name_base = self.parent.output_file + print("******************", self.parent.output_file) + if isinstance(read_in_models, datamodels.IFUImageModel): + # We have spec2 data. + input_models.append(read_in_models) + elif isinstance(read_in_models, ModelContainer): + # We have spec3 data. + input_models = read_in_models # Read in the first input model to determine with instrument we have # output type is by default 'Channel' for MIRI and 'Band' for NIRSpec instrument = input_models[0].meta.instrument.name.upper() - # The calspec2 pipeline sets up output_type for each instrument. When running cube_build - # stand alone we to set output_type. - - # Running cube build stand-alone without setting self.pipeline will default to pipeline=3 - # for NIRSPEC the type of cubes to make are based on self.linear_wave if instrument == "NIRSPEC": if self.output_type == "multi": # keep this for now @@ -254,17 +251,6 @@ def process(self, input_data): else: self.output_type = "multi" - # set up default pipeline 2 - if self.pipeline == 2 and self.output_type is None and instrument == "MIRI": - self.output_type = "multi" - - # Set up output_type for pipeline 3 type cubes. - # In calspec3 the output_type default type is grating for NIRSpec and band for MIRI. - # MIRI sets output_type in the calspec3 parameter reference file. - - if self.pipeline == 3 and self.output_type is None and instrument == "MIRI": - self.output_type = "band" - self.pars_input["output_type"] = self.output_type self.pars_input["linear_wave"] = self.linear_wave log.info(f"Setting output type to: {self.output_type}") @@ -354,10 +340,6 @@ def process(self, input_data): log.error("use coord_system = ifualign instead") raise ValueError("The 'internal_cal' coordinate system is not supported for MIRI") - # filenames = master_table.FileMap["filename"] # This was used to determine if we have - # pipeline 2 or 3. I don't think we need it - but saving it just in case I need know if - # there is only 1 filename - # ________________________________________________________________________________ # How many and what type of cubes will be made. # send self.pars_input['output_type'], all_channel, all_subchannel, all_grating, all_filter @@ -380,7 +362,6 @@ def process(self, input_data): list_par1 = cube_pars[icube]["par1"] list_par2 = cube_pars[icube]["par2"] thiscube = ifu_cube.IFUCubeData( - self.pipeline, input_models, self.output_name_base, self.pars_input["output_type"], diff --git a/jwst/cube_build/ifu_cube.py b/jwst/cube_build/ifu_cube.py index 4b7a10853b8..197d699b5ba 100644 --- a/jwst/cube_build/ifu_cube.py +++ b/jwst/cube_build/ifu_cube.py @@ -32,11 +32,6 @@ class IFUCubeData: Parameters ---------- - pipeline : int - Integer that indicates which pipeline is being run: - - * 2 = :ref:`calwebb_spec2 ` - * 3 = :ref:`calwebb_spec3 ` input_models : `~jwst.datamodels.container.ModelContainer` Container of multiple `~stdatamodels.jwst.datamodels.IFUImageModel` @@ -92,7 +87,6 @@ class IFUCubeData: def __init__( self, - pipeline, input_models, output_name_base, output_type, @@ -106,8 +100,6 @@ def __init__( ): self.input_models_this_cube = [] # list of files use to make cube working on - self.pipeline = pipeline - self.input_models = input_models # needed when building single mode IFU cubes self.output_name_base = output_name_base @@ -229,18 +221,16 @@ def check_ifucube(self): # ________________________________________________________________________________ def define_cubename(self): """ - Define the base output name. + Determine the suffix name consisting of channels/sub channels or gratings/filters - Usually the output name is defined by the association table. However in the case - of ``cube_build``, several cubes can be created from a single call. The - user can override the type of data to combine to make a cube. It is left to ``cube_build`` - to determine which channels, bands, gratings, or filters are used to make the IFU cube. - The final name includes the channel/band (MIRI) or grating/filter (NIRSpec). + The base name is defined by the pipeline. + Cube_build determines which channels, bands, gratings, or filters are used to make + the IFU cube. Returns ------- - newname : str - Output name of the IFU cube. + suffix : str + Output suffix of the IFU cube. """ if self.instrument == "MIRI": # Check to see if the output base name already contains the @@ -248,10 +238,13 @@ def define_cubename(self): # names created by the ASN rules. If so, strip it off, so # that the remaining suffixes created below form the entire # list of optical elements in the final output name. - basename = self.output_name_base - suffix = basename[basename.rfind("_") + 1 :] - if suffix in ["clear"]: - self.output_name_base = basename[: basename.rfind("_")] + + # JEM 4/7/2026 - NOT SURE IF THIS IS TRUE - DON'T THINK + # we need it anymore Commenting out for now. + # basename = self.output_name_base + # suffix = basename[basename.rfind("_") + 1 :] + #if suffix in ["clear"]: + # self.output_name_base = basename[: basename.rfind("_")] # Now compose the appropriate list of optical element suffix names # based on MRS channel and sub-channel @@ -274,22 +267,22 @@ def define_cubename(self): for i in range(number_subchannels): b_name = b_name + subchannels[i] b_name = b_name.lower() - newname = self.output_name_base + ch_name + "-" + b_name + cb_suffix = ch_name + "-" + b_name + if self.coord_system == "internal_cal": - newname = self.output_name_base + ch_name + "-" + b_name + "_internal" + cb_suffix += "_internal" elif self.output_type == "single": - newname = self.output_name_base + ch_name + "-" + b_name + "_single" - elif self.pipeline == 2: - newname = self.output_name_base + "_" + self.suffix + ".fits" + cb_suffix += "_single" elif self.instrument == "NIRSPEC": # Check to see if the output base name already has a grating/prism # suffix attached. If so, strip it off, and let the following logic # add all necessary grating and filter suffixes. - basename = self.output_name_base - suffix = basename[basename.rfind("_") + 1 :] - if suffix in ["g140m", "g235m", "g395m", "g140h", "g235h", "g395h", "prism"]: - self.output_name_base = basename[: basename.rfind("_")] + # JEM 4/7/206 DO WE WANT THIS ? Commenting out for now + #basename = self.output_name_base + #suffix = basename[basename.rfind("_") + 1 :] + #if suffix in ["g140m", "g235m", "g395m", "g140h", "g235h", "g395h", "prism"]: + # self.output_name_base = basename[: basename.rfind("_")] fg_name = "_" for i in range(len(self.list_par1)): @@ -297,18 +290,17 @@ def define_cubename(self): if i < self.num_bands - 1: fg_name = fg_name + "-" fg_name = fg_name.lower() - newname = self.output_name_base + fg_name + cb_suffix = fg_name + if self.output_type == "single": - newname = self.output_name_base + fg_name + "_single" + cb_suffix += "_single" elif self.coord_system == "internal_cal": - newname = self.output_name_base + fg_name + "_internal" - elif self.pipeline == 2: - newname = self.output_name_base + "_" + self.suffix + ".fits" + cb_suffix += "_internal" if self.output_type != "single": - log.info(f"Output Name: {newname}") + log.info(f"Output Suffix Name: {cb_suffix}") - return newname + return cb_suffix # _______________________________________________________________________ def set_geometry(self, corner_a, corner_b, lambda_min, lambda_max): @@ -674,7 +666,12 @@ def build_ifucube(self): result : `~stdatamodels.jwst.datamodels.IFUCubeModel` An IFU cube of combined IFU image data. """ - self.output_name = self.define_cubename() + #self.output_name = self.define_cubename() + cb_suffix = self.define_cubename() + self.output_name = self.output_name_base + cb_suffix + print('OUTPUT name', self.output_name) + + total_num = self.naxis1 * self.naxis2 * self.naxis3 self.spaxel_flux = np.zeros(total_num, dtype=np.float64) From 32530bd8403e777d8c1fe1a20aac4e6886a69d6d Mon Sep 17 00:00:00 2001 From: jemorrison Date: Tue, 7 Apr 2026 10:58:17 -0700 Subject: [PATCH 04/13] Loaded for comments --- jwst/cube_build/ifu_cube.py | 39 +++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/jwst/cube_build/ifu_cube.py b/jwst/cube_build/ifu_cube.py index 197d699b5ba..ba7023f56b4 100644 --- a/jwst/cube_build/ifu_cube.py +++ b/jwst/cube_build/ifu_cube.py @@ -32,7 +32,6 @@ class IFUCubeData: Parameters ---------- - input_models : `~jwst.datamodels.container.ModelContainer` Container of multiple `~stdatamodels.jwst.datamodels.IFUImageModel` @@ -223,7 +222,7 @@ def define_cubename(self): """ Determine the suffix name consisting of channels/sub channels or gratings/filters - The base name is defined by the pipeline. + The base name is defined by the pipeline. Cube_build determines which channels, bands, gratings, or filters are used to make the IFU cube. @@ -240,10 +239,10 @@ def define_cubename(self): # list of optical elements in the final output name. # JEM 4/7/2026 - NOT SURE IF THIS IS TRUE - DON'T THINK - # we need it anymore Commenting out for now. + # we need it anymore Commenting out for now. # basename = self.output_name_base # suffix = basename[basename.rfind("_") + 1 :] - #if suffix in ["clear"]: + # if suffix in ["clear"]: # self.output_name_base = basename[: basename.rfind("_")] # Now compose the appropriate list of optical element suffix names @@ -268,20 +267,20 @@ def define_cubename(self): b_name = b_name + subchannels[i] b_name = b_name.lower() cb_suffix = ch_name + "-" + b_name - + if self.coord_system == "internal_cal": - cb_suffix += "_internal" + cb_suffix += "_internal" elif self.output_type == "single": - cb_suffix += "_single" + cb_suffix += "_single" elif self.instrument == "NIRSPEC": # Check to see if the output base name already has a grating/prism # suffix attached. If so, strip it off, and let the following logic # add all necessary grating and filter suffixes. # JEM 4/7/206 DO WE WANT THIS ? Commenting out for now - #basename = self.output_name_base - #suffix = basename[basename.rfind("_") + 1 :] - #if suffix in ["g140m", "g235m", "g395m", "g140h", "g235h", "g395h", "prism"]: + # basename = self.output_name_base + # suffix = basename[basename.rfind("_") + 1 :] + # if suffix in ["g140m", "g235m", "g395m", "g140h", "g235h", "g395h", "prism"]: # self.output_name_base = basename[: basename.rfind("_")] fg_name = "_" @@ -293,14 +292,14 @@ def define_cubename(self): cb_suffix = fg_name if self.output_type == "single": - cb_suffix += "_single" + cb_suffix += "_single" elif self.coord_system == "internal_cal": - cb_suffix += "_internal" + cb_suffix += "_internal" if self.output_type != "single": log.info(f"Output Suffix Name: {cb_suffix}") - - return cb_suffix + + return cb_suffix # _______________________________________________________________________ def set_geometry(self, corner_a, corner_b, lambda_min, lambda_max): @@ -666,12 +665,11 @@ def build_ifucube(self): result : `~stdatamodels.jwst.datamodels.IFUCubeModel` An IFU cube of combined IFU image data. """ - #self.output_name = self.define_cubename() + # self.output_name = self.define_cubename() cb_suffix = self.define_cubename() self.output_name = self.output_name_base + cb_suffix - print('OUTPUT name', self.output_name) - - + print("OUTPUT name", self.output_name) + total_num = self.naxis1 * self.naxis2 * self.naxis3 self.spaxel_flux = np.zeros(total_num, dtype=np.float64) @@ -2662,8 +2660,8 @@ def setup_final_ifucube_model(self, model_ref): ifucube_model.update(model_ref) ifucube_model.meta.filename = self.output_name - print('before blending', ifucube_model.meta.filename) - + print("before blending", ifucube_model.meta.filename) + # Call model_blender if there are multiple inputs if len(self.input_models) > 1: saved_model_type = ifucube_model.meta.model_type @@ -2840,7 +2838,6 @@ def blend_output_metadata(self, ifu_cube): ifu_cube : `~stdatamodels.jwst.datamodels.IFUCubeModel` IFU cube data model """ - print(ifu_cube) print(self.input_models_this_cube) blendmeta.blendmodels( From 825a27e7f31b7c30d04bf7b41e1cdd22be55dc69 Mon Sep 17 00:00:00 2001 From: jemorrison Date: Mon, 20 Apr 2026 10:12:52 -0700 Subject: [PATCH 05/13] clean up interface and remove single type cubes used in old outlier detection method --- jwst/cube_build/cube_build.py | 27 +-- jwst/cube_build/cube_build_step.py | 91 ++------ jwst/cube_build/data_types.py | 12 +- jwst/cube_build/ifu_cube.py | 247 ++------------------ jwst/cube_build/tests/test_configuration.py | 16 -- jwst/cube_build/tests/test_offset.py | 2 - jwst/pipeline/calwebb_spec2.py | 1 - jwst/pipeline/calwebb_spec3.py | 1 - jwst/regtest/test_miri_mrs_spec2.py | 1 + 9 files changed, 39 insertions(+), 359 deletions(-) diff --git a/jwst/cube_build/cube_build.py b/jwst/cube_build/cube_build.py index 19d0d46b545..4caf33276aa 100644 --- a/jwst/cube_build/cube_build.py +++ b/jwst/cube_build/cube_build.py @@ -41,7 +41,6 @@ class CubeData: def __init__(self, input_models, par_filename, **pars): self.input_models = input_models self.par_filename = par_filename - self.single = pars.get("single") self.channel = pars.get("channel") self.subchannel = pars.get("subchannel") self.grating = pars.get("grating") @@ -338,8 +337,8 @@ def number_cubes(self): 1 cube is created. If the data is MIRI MRS, then 2 channels worth of data is combined into one IFU cube. For :ref:`calwebb_spec3 ` data, the default is to - create multiple cubes containing - single band data. However, the user can override this option and combine + create multiple cubes containing single band data. + However, the user can override this option and combine multiple bands in a single IFU cube. Returns @@ -361,18 +360,9 @@ def number_cubes(self): band_subchannel = self.all_subchannel # user, single, or multi - if ( - self.output_type == "user" - or self.output_type == "single" - or self.output_type == "multi" - ): + if self.output_type == "user" or self.output_type == "multi": if self.output_type == "multi": log.info("Output IFUcube are constructed from all the data ") - if self.single: - log.info( - "Single = true, creating a set of single exposures mapped" - " to output IFUCube coordinate system" - ) if self.output_type == "user": log.info("The user has selected the type of IFU cube to make") @@ -422,18 +412,9 @@ def number_cubes(self): band_grating = self.all_grating band_filter = self.all_filter - if ( - self.output_type == "user" - or self.output_type == "single" - or self.output_type == "multi" - ): + if self.output_type == "user" or self.output_type == "multi": if self.output_type == "multi": log.info("Output IFUcube are constructed from all the data ") - if self.single: - log.info( - "Single = true, creating a set of single exposures" - " mapped to output IFUCube coordinate system" - ) if self.output_type == "user": log.info("The user has selected the type of IFU cube to make") diff --git a/jwst/cube_build/cube_build_step.py b/jwst/cube_build/cube_build_step.py index 88d620bc95a..5b69f1a2752 100644 --- a/jwst/cube_build/cube_build_step.py +++ b/jwst/cube_build/cube_build_step.py @@ -4,7 +4,6 @@ import asdf from astropy import units -from stdatamodels.jwst import datamodels from jwst.assign_wcs.util import update_s_region_keyword from jwst.cube_build import cube_build, data_types, ifu_cube @@ -44,9 +43,7 @@ class CubeBuildStep(Step): weight_power = float(default=2.0) # Weighting option to use for Modified Shepard Method wavemin = float(default=None) # Minimum wavelength to be used in the IFUCube wavemax = float(default=None) # Maximum wavelength to be used in the IFUCube - single = boolean(default=false) # Internal pipeline option used by outlier detection skip_dqflagging = boolean(default=false) # skip setting the DQ plane of the IFU - search_output_file = boolean(default=false) output_use_model = boolean(default=true) # Use filenames in the output models suffix = string(default='s3d') offset_file = string(default=None) # Filename containing a list of Ra and Dec offsets to apply to files. @@ -190,25 +187,6 @@ def process(self, input_data): self.pars_input["coord_system"] = self.coord_system - if self.single: - self.pars_input["output_type"] = "single" - log.info("Cube Type: Single cubes") - self.pars_input["coord_system"] = "skyalign" - - # Don't allow anything but drizzle, msm, or emsm weightings - if self.weighting not in ["msm", "emsm", "drizzle"]: - self.weighting = "drizzle" - - if self.weighting == "drizzle": - self.interpolation = "drizzle" - - if self.weighting == "msm": - self.interpolation = "pointcloud" - - if self.weighting == "emsm": - self.interpolation = "pointcloud" - - # read_user_input: # if options channel, band, grating, or filter are set on the command lines # then set self.pars_input['output_type'] = 'user' and fill in par_input with values self.read_user_input() @@ -216,26 +194,12 @@ def process(self, input_data): # Read in the input data and make a copy as needed. read_in_models = self.prepare_output(input_data) - input_models = [] # Hold the input models to build cubes from - - if self.parent is None: # cube build is run stand alone - print("***** running cube build stand alone") - # Determine if input data was an association, a model, or a file - input_table = data_types.DataTypes( - read_in_models, self.single, self.output_file, self.output_dir - ) - input_models = input_table.input_models - self.output_name_base = input_table.output_name - - else: - self.output_name_base = self.parent.output_file - print("******************", self.parent.output_file) - if isinstance(read_in_models, datamodels.IFUImageModel): - # We have spec2 data. - input_models.append(read_in_models) - elif isinstance(read_in_models, ModelContainer): - # We have spec3 data. - input_models = read_in_models + # set the output base name + input_table = data_types.DataTypes( + read_in_models, self.search_attr("output_file"), self.output_dir + ) + input_models = input_table.input_models + self.output_name_base = input_table.output_name # Read in the first input model to determine with instrument we have # output type is by default 'Channel' for MIRI and 'Band' for NIRSpec @@ -282,7 +246,6 @@ def process(self, input_data): "grating": self.pars_input["grating"], "filter": self.pars_input["filter"], "weighting": self.weighting, - "single": self.single, "output_type": self.pars_input["output_type"], } @@ -348,15 +311,12 @@ def process(self, input_data): # list_pars2 (value subchannel or filter) num_cubes, cube_pars = cubeinfo.number_cubes() - if not self.single: - log.info(f"Number of IFU cubes produced by this run = {num_cubes}") + log.info(f"Number of IFU cubes produced by this run = {num_cubes}") # ModelContainer of ifucubes cube_container = ModelContainer() status_cube = 0 - # for single type cubes num_cubes always = 1, Looping over - # bands is done in outlier detection. for i in range(num_cubes): icube = str(i + 1) list_par1 = cube_pars[icube]["par1"] @@ -394,27 +354,14 @@ def process(self, input_data): # build the IFU Cube status = 0 + result, status = thiscube.build_ifucube() - # If single = True: map each file to output grid and return single mapped file - # to output grid. # This option is used for background matching and outlier rejection - - if self.single: - self.output_file = None - cube_container = thiscube.build_ifucube_single() - log.info("Number of Single IFUCube models returned %i ", len(cube_container)) - - # Else standard IFU cube building - # the result returned from build_ifucube will be 1 IFU CUBE - else: - result, status = thiscube.build_ifucube() - - # check if cube_build failed - # ************************** - if status == 1: - status_cube = 1 + # check if cube_build failed + if status == 1: + status_cube = 1 - cube_container.append(result) - del result + cube_container.append(result) + del result del thiscube # irrelevant WCS keywords we will remove from final product @@ -479,8 +426,7 @@ def read_user_input(self): if self.channel == "all": self.pars_input["channel"].append("all") else: # user has set value - if not self.single: - self.pars_input["output_type"] = "user" + self.pars_input["output_type"] = "user" channellist = self.channel.split(",") user_clen = len(channellist) for j in range(user_clen): @@ -504,8 +450,7 @@ def read_user_input(self): if self.subchannel == "all": self.pars_input["subchannel"].append("all") else: # user has set value - if not self.single: - self.pars_input["output_type"] = "user" + self.pars_input["output_type"] = "user" subchannellist = self.subchannel.split(",") user_blen = len(subchannellist) for j in range(user_blen): @@ -528,8 +473,7 @@ def read_user_input(self): if self.filter == "all": self.pars_input["filter"].append("all") else: # User has set value - if not self.single: - self.pars_input["output_type"] = "user" + self.pars_input["output_type"] = "user" filterlist = self.filter.split(",") user_flen = len(filterlist) for j in range(user_flen): @@ -551,8 +495,7 @@ def read_user_input(self): if self.grating == "all": self.pars_input["grating"].append("all") else: # user has set value - if not self.single: - self.pars_input["output_type"] = "user" + self.pars_input["output_type"] = "user" gratinglist = self.grating.split(",") user_glen = len(gratinglist) for j in range(user_glen): diff --git a/jwst/cube_build/data_types.py b/jwst/cube_build/data_types.py index 3619c0d8ff4..62de3b701f6 100644 --- a/jwst/cube_build/data_types.py +++ b/jwst/cube_build/data_types.py @@ -25,9 +25,7 @@ class DataTypes: input_models : `~stdatamodels.jwst.datamodels.IFUImageModel` or \ `~jwst.datamodels.container.ModelContainer` Input data to ``cube_build``, either a single model or a model container. - single : bool - If `True`, then create single mode IFU cubes for outlier detection. - If `False`, then create standard IFU cubes. + output_file : str Optional user-provided output file name. output_dir : str @@ -47,7 +45,7 @@ class DataTypes: "products": [{"name": "", "members": [{"exptype": "", "expname": ""}]}], } - def __init__(self, input_models, single, output_file, output_dir): + def __init__(self, input_models, output_file, output_dir): self.input_models = [] self.output_name = None @@ -59,10 +57,8 @@ def __init__(self, input_models, single, output_file, output_dir): self.output_name = self.build_product_name(filename) elif isinstance(input_models, ModelContainer): - self.output_name = "Temp" self.input_models = input_models - if not single: # find the name of the output file from the association - self.output_name = input_models.asn_table["products"][0]["name"] + self.output_name = input_models.asn_table["products"][0]["name"] else: raise TypeError(f"Failed to process file type {type(input_models)}") @@ -73,7 +69,7 @@ def __init__(self, input_models, single, output_file, output_dir): if output_file is not None: self.output_name = Path(output_file).stem - if output_dir is not None: + if output_dir is not None and self.output_name is not None: self.output_name = output_dir + "/" + self.output_name def build_product_name(self, filename): diff --git a/jwst/cube_build/ifu_cube.py b/jwst/cube_build/ifu_cube.py index ba7023f56b4..2f288231764 100644 --- a/jwst/cube_build/ifu_cube.py +++ b/jwst/cube_build/ifu_cube.py @@ -18,7 +18,6 @@ from jwst.cube_build import coord, cube_build_wcs_util, cube_internal_cal from jwst.cube_build.cube_match_sky_driz import cube_wrapper_driz # c extension from jwst.cube_build.cube_match_sky_pointcloud import cube_wrapper # c extension -from jwst.datamodels import ModelContainer from jwst.model_blender import blendmeta log = logging.getLogger(__name__) @@ -138,7 +137,7 @@ def __init__( self.skip_dqflagging = pars_cube.get("skip_dqflagging") self.suffix = pars_cube.get("suffix") self.num_bands = 0 - self.output_name = "" + self.output_name = None self.wavemin_user = False # Check for NIRSpec if user has set wavelength limits self.wavemax_user = False @@ -220,7 +219,7 @@ def check_ifucube(self): # ________________________________________________________________________________ def define_cubename(self): """ - Determine the suffix name consisting of channels/sub channels or gratings/filters + Determine the suffix name consisting of channels/sub channels or gratings/filters. The base name is defined by the pipeline. Cube_build determines which channels, bands, gratings, or filters are used to make @@ -231,6 +230,7 @@ def define_cubename(self): suffix : str Output suffix of the IFU cube. """ + cb_suffix = "" if self.instrument == "MIRI": # Check to see if the output base name already contains the # field "clear", which sometimes shows up in IFU product @@ -270,18 +270,16 @@ def define_cubename(self): if self.coord_system == "internal_cal": cb_suffix += "_internal" - elif self.output_type == "single": - cb_suffix += "_single" elif self.instrument == "NIRSPEC": # Check to see if the output base name already has a grating/prism # suffix attached. If so, strip it off, and let the following logic # add all necessary grating and filter suffixes. - # JEM 4/7/206 DO WE WANT THIS ? Commenting out for now - # basename = self.output_name_base - # suffix = basename[basename.rfind("_") + 1 :] - # if suffix in ["g140m", "g235m", "g395m", "g140h", "g235h", "g395h", "prism"]: - # self.output_name_base = basename[: basename.rfind("_")] + + basename = self.output_name_base + suffix = basename[basename.rfind("_") + 1 :] + if suffix in ["g140m", "g235m", "g395m", "g140h", "g235h", "g395h", "prism"]: + self.output_name_base = basename[: basename.rfind("_")] fg_name = "_" for i in range(len(self.list_par1)): @@ -291,14 +289,9 @@ def define_cubename(self): fg_name = fg_name.lower() cb_suffix = fg_name - if self.output_type == "single": - cb_suffix += "_single" - elif self.coord_system == "internal_cal": + if self.coord_system == "internal_cal": cb_suffix += "_internal" - if self.output_type != "single": - log.info(f"Output Suffix Name: {cb_suffix}") - return cb_suffix # _______________________________________________________________________ @@ -665,10 +658,8 @@ def build_ifucube(self): result : `~stdatamodels.jwst.datamodels.IFUCubeModel` An IFU cube of combined IFU image data. """ - # self.output_name = self.define_cubename() cb_suffix = self.define_cubename() self.output_name = self.output_name_base + cb_suffix - print("OUTPUT name", self.output_name) total_num = self.naxis1 * self.naxis2 * self.naxis3 @@ -975,189 +966,6 @@ def build_ifucube(self): result = self.setup_final_ifucube_model(input_model_ref) return result - # ________________________________________________________________________________ - def build_ifucube_single(self): - """ - Build a set of single mode IFU cubes. - - Loop over every band contained in the IFU cube and read in the data - associated with the band. Map each band to the output cube coordinate - system. - - Returns - ------- - single_ifucube_container : `~stdatamodels.jwst.datamodels.IFUCubeModel` - A single type IFU cube datamodel - """ - # loop over input models - single_ifucube_container = ModelContainer() - - weight_type = 0 # default to emsm instead of msm - if self.weighting == "msm": - weight_type = 1 - number_bands = len(self.list_par1) - this_par1 = self.list_par1[0] # single IFUcube only have a single channel - j = 0 - for i in range(number_bands): - this_par2 = self.list_par2[i] - nfiles = len(self.master_table.FileMap[self.instrument][this_par1][this_par2]) - - # loop over the files that cover the spectral range the cube is for - for k in range(nfiles): - input_model = self.master_table.FileMap[self.instrument][this_par1][this_par2][k] - self.input_models_this_cube.append(input_model) - log.debug(f"Working on next Single IFU Cube = {j + 1}") - - # for each new data model create a new spaxel - total_num = self.naxis1 * self.naxis2 * self.naxis3 - self.spaxel_flux = np.zeros(total_num, dtype=np.float64) - self.spaxel_weight = np.zeros(total_num, dtype=np.float64) - self.spaxel_iflux = np.zeros(total_num) - self.spaxel_dq = np.zeros(total_num, dtype=np.uint32) - self.spaxel_var = np.zeros(total_num, dtype=np.float64) - - pixelresult = self.map_detector_to_outputframe(this_par1, input_model) - - ( - coord1, - coord2, - corner_coord, - wave, - dwave, - flux, - err, - slice_no, - rois_pixel, - roiw_pixel, - weight_pixel, - softrad_pixel, - scalerad_pixel, - _, - _, - ) = pixelresult - - build_cube = True - # there is no valid data on the detector. Pixels are flagged as DO_NOT_USE. - if wave is None: - build_cube = False - # The following values are not needed in cube_wrapper because the DQ plane - # is not being filled in. - flag_dq_plane = 0 - start_region = 0 - end_region = 0 - roiw_ave = 0 - - if self.instrument == "MIRI": - instrument = 0 - else: # NIRSPEC - instrument = 1 - - result = None - - if self.interpolation == "pointcloud" and build_cube: - roiw_ave = np.mean(roiw_pixel) - result = cube_wrapper( - instrument, - flag_dq_plane, - weight_type, - start_region, - end_region, - self.overlap_partial, - self.overlap_full, - self.xcoord, - self.ycoord, - self.zcoord, - coord1, - coord2, - wave, - flux, - err, - slice_no, - rois_pixel, - roiw_pixel, - scalerad_pixel, - weight_pixel, - softrad_pixel, - self.cdelt3_normal, - roiw_ave, - self.cdelt1, - self.cdelt2, - ) - spaxel_flux, spaxel_weight, spaxel_var, spaxel_iflux, _ = result - - self.spaxel_flux = self.spaxel_flux + np.asarray(spaxel_flux, np.float64) - self.spaxel_weight = self.spaxel_weight + np.asarray(spaxel_weight, np.float64) - self.spaxel_var = self.spaxel_var + np.asarray(spaxel_var, np.float64) - self.spaxel_iflux = self.spaxel_iflux + np.asarray(spaxel_iflux, np.float64) - result = None - del result, spaxel_flux, spaxel_var, spaxel_iflux - - if self.weighting == "drizzle" and build_cube: - cdelt3_mean = np.nanmean(self.cdelt3_normal) - xi1, eta1, xi2, eta2, xi3, eta3, xi4, eta4 = corner_coord - linear = 0 - if self.linear_wavelength: - linear = 1 - x_det = None - y_det = None - debug_cube = -1 - result = cube_wrapper_driz( - instrument, - flag_dq_plane, - start_region, - end_region, - self.overlap_partial, - self.overlap_full, - self.xcoord, - self.ycoord, - self.zcoord, - coord1, - coord2, - wave, - flux, - err, - slice_no, - xi1, - eta1, - xi2, - eta2, - xi3, - eta3, - xi4, - eta4, - dwave, - self.cdelt3_normal, - self.cdelt1, - self.cdelt2, - cdelt3_mean, - linear, - x_det, - y_det, - debug_cube, - ) - - spaxel_flux, spaxel_weight, spaxel_var, spaxel_iflux, _ = result - self.spaxel_flux = self.spaxel_flux + np.asarray(spaxel_flux, np.float64) - self.spaxel_weight = self.spaxel_weight + np.asarray(spaxel_weight, np.float64) - self.spaxel_var = self.spaxel_var + np.asarray(spaxel_var, np.float64) - self.spaxel_iflux = self.spaxel_iflux + np.asarray(spaxel_iflux, np.float64) - result = None - del result, spaxel_flux, spaxel_var, spaxel_iflux - - # shove Flux and iflux in the final ifucube - self.find_spaxel_flux() - - # determine Cube Spaxel flux - status = 0 - result = self.setup_final_ifucube_model(input_model) - ifucube_model, status = result - - single_ifucube_container.append(ifucube_model) - if status != 0: - log.debug("Possible problem with single ifu cube, no valid data in cube") - j = j + 1 - return single_ifucube_container - # ________________________________________________________________________________ def determine_cube_parameters_internal(self): """Determine the spatial and spectral IFU size for ``coord_system=internal_cal``.""" @@ -1387,7 +1195,7 @@ def determine_cube_parameters(self): # default rois in tables is designed with a 4 dither pattern # increase rois if less than 4 file - if self.output_type == "single" or self.num_files < 4: + if self.num_files < 4: # We don't need to increase it if using 'emsm' weighting if self.weighting.lower() != "emsm": self.rois = self.rois * 1.5 @@ -1396,8 +1204,7 @@ def determine_cube_parameters(self): f"default value set for 4 dithers {self.rois}" ) - # set wave_roi and weight_power to same values if they are in list - + # set wave_roi and weight_power to same values if they are in list if self.roiw == 0: self.roiw = wave_roi if self.weight_power == 0: @@ -2659,8 +2466,8 @@ def setup_final_ifucube_model(self, model_ref): ) ifucube_model.update(model_ref) - ifucube_model.meta.filename = self.output_name - print("before blending", ifucube_model.meta.filename) + if self.output_name is not None: + ifucube_model.meta.filename = self.output_name # Call model_blender if there are multiple inputs if len(self.input_models) > 1: @@ -2681,26 +2488,6 @@ def setup_final_ifucube_model(self, model_ref): outchannel = "".join(sorted(outchannel)) ifucube_model.meta.instrument.channel = outchannel - # single files are created for a single band, - if self.output_type == "single": - with datamodels.open(model_ref) as input_ref: - # define the cubename for each single - filename = input_ref.meta.filename - indx = filename.rfind(".fits") - self.output_name_base = filename[:indx] - self.output_file = None - newname = self.define_cubename() - ifucube_model.meta.filename = newname - - if self.instrument == "MIRI": - outchannel = self.list_par1[0] - outband = self.list_par2[0] - ifucube_model.meta.instrument.channel = outchannel - ifucube_model.meta.instrument.band = outband.upper() - else: - outgrating = self.list_par1[0] - ifucube_model.meta.instrument.grating = outgrating.upper() - ifucube_model.meta.wcsinfo.crval1 = self.crval1 ifucube_model.meta.wcsinfo.crval2 = self.crval2 ifucube_model.meta.wcsinfo.crpix1 = self.crpix1 @@ -2767,14 +2554,8 @@ def setup_final_ifucube_model(self, model_ref): ifucube_model.meta.ifu.error_type = "ERR" ifucube_model.meta.ifu.dq_extension = "DQ" ifucube_model.meta.ifu.weighting = str(self.weighting) - # weight_power is needed for single cubes. Linear Wavelengths - # if non-linear wavelengths then this will be None ifucube_model.meta.ifu.weight_power = self.weight_power - with datamodels.open(model_ref) as input_ref: - ifucube_model.meta.bunit_data = input_ref.meta.bunit_data - ifucube_model.meta.bunit_err = input_ref.meta.bunit_err - if self.interpolation == "drizzle": # stick in values of 0, otherwise it is NaN and # fits file can not be written because these @@ -2838,8 +2619,6 @@ def blend_output_metadata(self, ifu_cube): ifu_cube : `~stdatamodels.jwst.datamodels.IFUCubeModel` IFU cube data model """ - print(ifu_cube) - print(self.input_models_this_cube) blendmeta.blendmodels( ifu_cube, self.input_models_this_cube, diff --git a/jwst/cube_build/tests/test_configuration.py b/jwst/cube_build/tests/test_configuration.py index cd8e948a0d3..6a02ee85f98 100644 --- a/jwst/cube_build/tests/test_configuration.py +++ b/jwst/cube_build/tests/test_configuration.py @@ -169,7 +169,6 @@ def test_calspec2_config(tmp_cwd, miri_ifushort_short): pars_input["grating"] = [] weighting = "drizzle" output_type = "multi" # calspec 2 setup. Only 1 cube create from 2 channels - single = False par_filename = "None" input_models = [] @@ -181,7 +180,6 @@ def test_calspec2_config(tmp_cwd, miri_ifushort_short): "grating": pars_input["grating"], "filter": pars_input["filter"], "weighting": weighting, - "single": single, "output_type": output_type, } @@ -213,7 +211,6 @@ def test_calspec3_config_miri(tmp_cwd, miri_full_coverage): pars_input["grating"] = [] weighting = "drizzle" output_type = "band" - single = False par_filename = "None" pars = { @@ -222,7 +219,6 @@ def test_calspec3_config_miri(tmp_cwd, miri_full_coverage): "grating": pars_input["grating"], "filter": pars_input["filter"], "weighting": weighting, - "single": single, "output_type": output_type, } @@ -292,7 +288,6 @@ def test_calspec3_config_miri_multi(tmp_cwd, miri_full_coverage): pars_input["grating"] = [] weighting = "drizzle" output_type = "multi" - single = False par_filename = "None" pars = { @@ -301,7 +296,6 @@ def test_calspec3_config_miri_multi(tmp_cwd, miri_full_coverage): "grating": pars_input["grating"], "filter": pars_input["filter"], "weighting": weighting, - "single": single, "output_type": output_type, } @@ -361,7 +355,6 @@ def test_calspec3_config_miri_multi_ch1(tmp_cwd, miri_full_coverage): weighting = "drizzle" output_type = "multi" channel = "1" - single = False par_filename = "None" pars = { @@ -369,7 +362,6 @@ def test_calspec3_config_miri_multi_ch1(tmp_cwd, miri_full_coverage): "grating": pars_input["grating"], "filter": pars_input["filter"], "weighting": weighting, - "single": single, "channel": channel, "output_type": output_type, } @@ -402,7 +394,6 @@ def test_calspec3_config_nirspec(tmp_cwd, nirspec_medium_coverage): pars_input["grating"] = [] weighting = "drizzle" output_type = "band" - single = False par_filename = "None" pars = { @@ -411,7 +402,6 @@ def test_calspec3_config_nirspec(tmp_cwd, nirspec_medium_coverage): "grating": pars_input["grating"], "filter": pars_input["filter"], "weighting": weighting, - "single": single, "output_type": output_type, } @@ -447,7 +437,6 @@ def test_calspec3_config_nirspec_multi(tmp_cwd, nirspec_medium_coverage): pars_input["grating"] = [] weighting = "msm" output_type = "multi" - single = False par_filename = "None" pars = { @@ -456,7 +445,6 @@ def test_calspec3_config_nirspec_multi(tmp_cwd, nirspec_medium_coverage): "grating": pars_input["grating"], "filter": pars_input["filter"], "weighting": weighting, - "single": single, "output_type": output_type, } @@ -491,7 +479,6 @@ def test_calspec3_config_nirspec_grating(tmp_cwd, nirspec_medium_coverage): grating = "g140m" filter = "f100lp" output_type = "multi" - single = False par_filename = "None" pars = { @@ -500,7 +487,6 @@ def test_calspec3_config_nirspec_grating(tmp_cwd, nirspec_medium_coverage): "weighting": weighting, "grating": grating, "filter": filter, - "single": single, "output_type": output_type, } @@ -535,7 +521,6 @@ def test_calspec3_config_nirspec_no_grating(tmp_cwd, nirspec_medium_coverage): grating = "g140h" filter = "f100lp" output_type = "multi" - single = False par_filename = "None" pars = { @@ -544,7 +529,6 @@ def test_calspec3_config_nirspec_no_grating(tmp_cwd, nirspec_medium_coverage): "weighting": weighting, "grating": grating, "filter": filter, - "single": single, "output_type": output_type, } diff --git a/jwst/cube_build/tests/test_offset.py b/jwst/cube_build/tests/test_offset.py index 106a1cb9a91..112b3ccd904 100644 --- a/jwst/cube_build/tests/test_offset.py +++ b/jwst/cube_build/tests/test_offset.py @@ -189,7 +189,6 @@ def test_read_offset_file(miri_ifushort_short_2files, offset_file): pars_input["grating"] = [] weighting = "drizzle" output_type = "multi" - single = False par_filename = "None" # set up pars needed for CubeData class @@ -199,7 +198,6 @@ def test_read_offset_file(miri_ifushort_short_2files, offset_file): "grating": pars_input["grating"], "filter": pars_input["filter"], "weighting": weighting, - "single": single, "output_type": output_type, "offset_file": offset_file, } diff --git a/jwst/pipeline/calwebb_spec2.py b/jwst/pipeline/calwebb_spec2.py index 506aa08dc1d..fb39de9abd4 100644 --- a/jwst/pipeline/calwebb_spec2.py +++ b/jwst/pipeline/calwebb_spec2.py @@ -136,7 +136,6 @@ def process(self, data): self.resample_spec.suffix = "s2d" self.cube_build.save_results = False self.cube_build.skip_dqflagging = True - self.cube_build.pipeline = 2 self.extract_1d.save_results = self.save_results # Retrieve the input(s) asn = self.load_as_level2_asn(data) diff --git a/jwst/pipeline/calwebb_spec3.py b/jwst/pipeline/calwebb_spec3.py index 60c0c97a778..9e627dbfaf2 100644 --- a/jwst/pipeline/calwebb_spec3.py +++ b/jwst/pipeline/calwebb_spec3.py @@ -88,7 +88,6 @@ def process(self, input_data): self.resample_spec.suffix = "s2d" self.resample_spec.save_results = self.save_results self.cube_build.suffix = "s3d" - self.cube_build.pipeline = 3 self.cube_build.save_results = self.save_results self.extract_1d.suffix = "x1d" self.extract_1d.save_results = self.save_results diff --git a/jwst/regtest/test_miri_mrs_spec2.py b/jwst/regtest/test_miri_mrs_spec2.py index bc273fc79a7..e2e30ddc42e 100644 --- a/jwst/regtest/test_miri_mrs_spec2.py +++ b/jwst/regtest/test_miri_mrs_spec2.py @@ -42,6 +42,7 @@ def run_spec2(rtdata_module): "--steps.photom.save_results=true", "--steps.resample_spec.save_results=true", "--steps.cube_build.save_results=true", + "--steps.cube_build.output_type='multi", "--steps.extract_1d.save_results=true", ] Step.from_cmdline(args) From 56967ee2efc5f1ef01829b90419c6908e29382dd Mon Sep 17 00:00:00 2001 From: jemorrison Date: Mon, 20 Apr 2026 10:36:44 -0700 Subject: [PATCH 06/13] add change log --- changes/10433.cube_build.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/10433.cube_build.rst diff --git a/changes/10433.cube_build.rst b/changes/10433.cube_build.rst new file mode 100644 index 00000000000..be92254659f --- /dev/null +++ b/changes/10433.cube_build.rst @@ -0,0 +1 @@ +Cleanup cube build interface removing the parameters pipeline and single From 7144ced47473c2598c56b11eaddc39b542ba4d39 Mon Sep 17 00:00:00 2001 From: jemorrison Date: Tue, 21 Apr 2026 08:55:31 -0700 Subject: [PATCH 07/13] cleaned up tests --- jwst/cube_build/tests/test_miri_cubepars.py | 8 -------- jwst/cube_build/tests/test_nirspec_cubepars.py | 7 ------- jwst/cube_build/tests/test_offset.py | 2 -- jwst/cube_build/tests/test_wcs.py | 2 -- 4 files changed, 19 deletions(-) diff --git a/jwst/cube_build/tests/test_miri_cubepars.py b/jwst/cube_build/tests/test_miri_cubepars.py index 46b31dee0f7..ddf603629ea 100644 --- a/jwst/cube_build/tests/test_miri_cubepars.py +++ b/jwst/cube_build/tests/test_miri_cubepars.py @@ -164,8 +164,6 @@ def test_miri_use_cubepars(tmp_cwd, miri_cube_pars): "debug_spaxel": "0 0 0", } - pipeline = 3 - input_model = None output_name_base = None output_type = "band" @@ -176,7 +174,6 @@ def test_miri_use_cubepars(tmp_cwd, miri_cube_pars): master_table = None instrument_info = instrument_info this_cube = ifu_cube.IFUCubeData( - pipeline, input_model, output_name_base, output_type, @@ -268,7 +265,6 @@ def test_miri_cubepars_user_defaults(tmp_cwd, miri_cube_pars): "debug_spaxel": "0 0 0", } - pipeline = 3 input_model = None output_name_base = None output_type = "band" @@ -279,7 +275,6 @@ def test_miri_cubepars_user_defaults(tmp_cwd, miri_cube_pars): master_table = None instrument_info = instrument_info this_cube = ifu_cube.IFUCubeData( - pipeline, input_model, output_name_base, output_type, @@ -332,7 +327,6 @@ def test_miri_cubepars_user_defaults(tmp_cwd, miri_cube_pars): } this_cube = ifu_cube.IFUCubeData( - pipeline, input_model, output_name_base, output_type, @@ -440,7 +434,6 @@ def test_miri_cubepars_multiple_bands(tmp_cwd, miri_cube_pars): "debug_spaxel": "0 0 0", } - pipeline = 3 input_model = None output_name_base = None output_type = "multi" @@ -451,7 +444,6 @@ def test_miri_cubepars_multiple_bands(tmp_cwd, miri_cube_pars): master_table = None instrument_info = instrument_info this_cube = ifu_cube.IFUCubeData( - pipeline, input_model, output_name_base, output_type, diff --git a/jwst/cube_build/tests/test_nirspec_cubepars.py b/jwst/cube_build/tests/test_nirspec_cubepars.py index 960e13c4015..87a3b396e8c 100644 --- a/jwst/cube_build/tests/test_nirspec_cubepars.py +++ b/jwst/cube_build/tests/test_nirspec_cubepars.py @@ -177,7 +177,6 @@ def test_nirspec_cubepars_test1(tmp_cwd, nirspec_cube_pars): # By default Prism/Clear will produce a linear wavelength plane. - pipeline = 3 input_model = None output_name_base = None output_type = "band" @@ -188,7 +187,6 @@ def test_nirspec_cubepars_test1(tmp_cwd, nirspec_cube_pars): master_table = None instrument_info = instrument_info this_cube = ifu_cube.IFUCubeData( - pipeline, input_model, output_name_base, output_type, @@ -211,7 +209,6 @@ def test_nirspec_cubepars_test1(tmp_cwd, nirspec_cube_pars): # Add output_type = 'multi' and Prism/Clear will produce a non-linear wavelength plane. - pipeline = 3 input_model = None output_name_base = None output_type = "multi" @@ -222,7 +219,6 @@ def test_nirspec_cubepars_test1(tmp_cwd, nirspec_cube_pars): master_table = None instrument_info = instrument_info this_cube = ifu_cube.IFUCubeData( - pipeline, input_model, output_name_base, output_type, @@ -302,7 +298,6 @@ def test_nirspec_cubepars_test2(tmp_cwd, nirspec_cube_pars): "debug_spaxel": "0 0 0", } - pipeline = 3 input_model = None output_name_base = None output_type = "band" @@ -313,7 +308,6 @@ def test_nirspec_cubepars_test2(tmp_cwd, nirspec_cube_pars): master_table = None instrument_info = instrument_info this_cube = ifu_cube.IFUCubeData( - pipeline, input_model, output_name_base, output_type, @@ -367,7 +361,6 @@ def test_nirspec_cubepars_test2(tmp_cwd, nirspec_cube_pars): } this_cube = ifu_cube.IFUCubeData( - pipeline, input_model, output_name_base, output_type, diff --git a/jwst/cube_build/tests/test_offset.py b/jwst/cube_build/tests/test_offset.py index 112b3ccd904..6cf1a19d1dc 100644 --- a/jwst/cube_build/tests/test_offset.py +++ b/jwst/cube_build/tests/test_offset.py @@ -241,7 +241,6 @@ def test_read_offset_file(miri_ifushort_short_2files, offset_file): "debug_spaxel": "0 0 0", } - pipeline = 3 list_par1 = ["1", "2"] list_par2 = ["short", "short"] output_name_base = "TEMP" @@ -249,7 +248,6 @@ def test_read_offset_file(miri_ifushort_short_2files, offset_file): instrument_info = instrument_defaults.InstrumentInfo() thiscube = ifu_cube.IFUCubeData( - pipeline, miri_ifushort_short_2files, output_name_base, output_type, diff --git a/jwst/cube_build/tests/test_wcs.py b/jwst/cube_build/tests/test_wcs.py index 0ae00b704dd..84c4a260bc6 100644 --- a/jwst/cube_build/tests/test_wcs.py +++ b/jwst/cube_build/tests/test_wcs.py @@ -201,7 +201,6 @@ def test_setup_wcs(): "debug_spaxel": "0 0 0", } - pipeline = 3 input_model = None output_name_base = None output_type = None @@ -212,7 +211,6 @@ def test_setup_wcs(): master_table = None instrument_info = None thiscube = ifu_cube.IFUCubeData( - pipeline, input_model, output_name_base, output_type, From 464b50114acb238c303c8d98f281b6fe18ef9ef4 Mon Sep 17 00:00:00 2001 From: jemorrison Date: Tue, 21 Apr 2026 10:44:02 -0700 Subject: [PATCH 08/13] fix syntax error in test and update docs --- docs/jwst/cube_build/arguments.rst | 10 ---------- jwst/regtest/test_miri_mrs_spec2.py | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/docs/jwst/cube_build/arguments.rst b/docs/jwst/cube_build/arguments.rst index 09d5bff1102..a3f977cc95b 100644 --- a/docs/jwst/cube_build/arguments.rst +++ b/docs/jwst/cube_build/arguments.rst @@ -17,16 +17,6 @@ each band will be created. ``cube_build``, it sets ``pipeline=2``. When running stand-alone or in :ref:`calwebb_spec3 ` pipeline, the parameter ``pipeline=3`` is set for ``cube_build``. - - ``pipeline=2`` sets up the rules for making :ref:`calwebb_spec2 ` pipeline type cubes. For NIRSpec data the default - rules produce cubes with a single grating and filter and with a linear wavelength dimension. For MIRI data - the default rules produce a single IFU cube containing the two channels in the input data with a non-linear - wavelength dimension. In both cases, the input filename suffix ``cal`` is replaced with ``s3d``. - - - ``pipeline=3`` setups up the results for making :ref:`calwebb_spec3 ` pipeline type cubes. For NIRSpec data - the default rules will produce a single IFU cube from the same grating and filter, while for MIRI data a single - IFU cube is created for each channel and band. In both cases, the output IFU cube will contain the grating and - filter name (NIRSpec) or channel and band (MIRI). - ``channel [string]`` This is a MIRI only option and the valid values are 1, 2, 3, 4, and ALL. If the ``channel`` argument is given, then only data corresponding to that channel will be used in diff --git a/jwst/regtest/test_miri_mrs_spec2.py b/jwst/regtest/test_miri_mrs_spec2.py index e2e30ddc42e..f809f05fba3 100644 --- a/jwst/regtest/test_miri_mrs_spec2.py +++ b/jwst/regtest/test_miri_mrs_spec2.py @@ -42,7 +42,7 @@ def run_spec2(rtdata_module): "--steps.photom.save_results=true", "--steps.resample_spec.save_results=true", "--steps.cube_build.save_results=true", - "--steps.cube_build.output_type='multi", + "--steps.cube_build.output_type='multi'", "--steps.extract_1d.save_results=true", ] Step.from_cmdline(args) From 4145b0c18082153d7631ac66ad21e68acec724e9 Mon Sep 17 00:00:00 2001 From: jemorrison Date: Tue, 21 Apr 2026 12:39:12 -0700 Subject: [PATCH 09/13] update calspec2 tests in regression tests --- jwst/regtest/test_miri_mrs_spec2.py | 1 + jwst/regtest/test_miri_mrs_trace_model.py | 1 + 2 files changed, 2 insertions(+) diff --git a/jwst/regtest/test_miri_mrs_spec2.py b/jwst/regtest/test_miri_mrs_spec2.py index f809f05fba3..c0f1d50cba6 100644 --- a/jwst/regtest/test_miri_mrs_spec2.py +++ b/jwst/regtest/test_miri_mrs_spec2.py @@ -63,6 +63,7 @@ def run_spec2_with_residual_fringe(rtdata_module, resource_tracker): "--output_file=jw01024001001_04101_00001_mirifulong_rf", "--steps.residual_fringe.skip=false", "--steps.residual_fringe.save_results=true", + "--steps.cube_build.output_type='multi'", ] with resource_tracker.track(): diff --git a/jwst/regtest/test_miri_mrs_trace_model.py b/jwst/regtest/test_miri_mrs_trace_model.py index b610851631a..33db51b020a 100644 --- a/jwst/regtest/test_miri_mrs_trace_model.py +++ b/jwst/regtest/test_miri_mrs_trace_model.py @@ -29,6 +29,7 @@ def run_spec2_trace_model(rtdata_module): "--steps.adaptive_trace_model.oversample=1.0", "--steps.adaptive_trace_model.skip=false", "--steps.adaptive_trace_model.save_results=true", + "--steps.cube_build.output_type='multi'", ] Step.from_cmdline(args) return rtdata From 9e5af964711d1c2d0d786d9edd84da2a1ae3aa64 Mon Sep 17 00:00:00 2001 From: jemorrison Date: Mon, 4 May 2026 07:26:44 -0700 Subject: [PATCH 10/13] updates from review --- changes/10433.cube_build.rst | 2 +- jwst/cube_build/cube_build_step.py | 3 +-- jwst/cube_build/ifu_cube.py | 23 +++++------------------ 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/changes/10433.cube_build.rst b/changes/10433.cube_build.rst index be92254659f..dd03c9b972c 100644 --- a/changes/10433.cube_build.rst +++ b/changes/10433.cube_build.rst @@ -1 +1 @@ -Cleanup cube build interface removing the parameters pipeline and single +Clean up cube build interface, removing the parameters ``pipeline`` and ``single``, which were intended for internal use only. diff --git a/jwst/cube_build/cube_build_step.py b/jwst/cube_build/cube_build_step.py index 5b69f1a2752..034e64e3e46 100644 --- a/jwst/cube_build/cube_build_step.py +++ b/jwst/cube_build/cube_build_step.py @@ -282,8 +282,7 @@ def process(self, input_data): match_nans_and_flags(model) # We need to know what type of cubes we are building for cube_build.py to correctly - # group the data. This information is controlled by the self.output_type parameter, which - # is also dependent on the self.pipeline parameter. + # group the data. This information is controlled by the self.output_type parameter. cubeinfo = cube_build.CubeData(input_models, par_filename, **pars) # ________________________________________________________________________________ diff --git a/jwst/cube_build/ifu_cube.py b/jwst/cube_build/ifu_cube.py index 2f288231764..21587cac414 100644 --- a/jwst/cube_build/ifu_cube.py +++ b/jwst/cube_build/ifu_cube.py @@ -217,34 +217,21 @@ def check_ifucube(self): ) # ________________________________________________________________________________ - def define_cubename(self): + def define_cubename_suffix(self): """ - Determine the suffix name consisting of channels/sub channels or gratings/filters. + Determine the filename suffix name consisting of channels/sub channels or gratings/filters. The base name is defined by the pipeline. Cube_build determines which channels, bands, gratings, or filters are used to make - the IFU cube. + the IFU cube, so it appends the appropriate suffix to the base name. Returns ------- suffix : str - Output suffix of the IFU cube. + Output suffix for the IFU cube. """ cb_suffix = "" if self.instrument == "MIRI": - # Check to see if the output base name already contains the - # field "clear", which sometimes shows up in IFU product - # names created by the ASN rules. If so, strip it off, so - # that the remaining suffixes created below form the entire - # list of optical elements in the final output name. - - # JEM 4/7/2026 - NOT SURE IF THIS IS TRUE - DON'T THINK - # we need it anymore Commenting out for now. - # basename = self.output_name_base - # suffix = basename[basename.rfind("_") + 1 :] - # if suffix in ["clear"]: - # self.output_name_base = basename[: basename.rfind("_")] - # Now compose the appropriate list of optical element suffix names # based on MRS channel and sub-channel channels = [] @@ -658,7 +645,7 @@ def build_ifucube(self): result : `~stdatamodels.jwst.datamodels.IFUCubeModel` An IFU cube of combined IFU image data. """ - cb_suffix = self.define_cubename() + cb_suffix = self.define_cubename_suffix() self.output_name = self.output_name_base + cb_suffix total_num = self.naxis1 * self.naxis2 * self.naxis3 From 92bee69f7ead9a54b6ec5c3d9365abe9541c17f5 Mon Sep 17 00:00:00 2001 From: jemorrison Date: Mon, 4 May 2026 08:43:58 -0700 Subject: [PATCH 11/13] add unit test for suffix --- jwst/cube_build/ifu_cube.py | 2 + jwst/cube_build/tests/test_cube_suffix.py | 73 +++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 jwst/cube_build/tests/test_cube_suffix.py diff --git a/jwst/cube_build/ifu_cube.py b/jwst/cube_build/ifu_cube.py index 21587cac414..e18f45786a2 100644 --- a/jwst/cube_build/ifu_cube.py +++ b/jwst/cube_build/ifu_cube.py @@ -224,6 +224,8 @@ def define_cubename_suffix(self): The base name is defined by the pipeline. Cube_build determines which channels, bands, gratings, or filters are used to make the IFU cube, so it appends the appropriate suffix to the base name. + This function not used to determine the suffix when cube_build is called from calwebb_spec2. + In that case, the pipeline attaches 's3d' onto the filename. Returns ------- diff --git a/jwst/cube_build/tests/test_cube_suffix.py b/jwst/cube_build/tests/test_cube_suffix.py new file mode 100644 index 00000000000..24348b2e020 --- /dev/null +++ b/jwst/cube_build/tests/test_cube_suffix.py @@ -0,0 +1,73 @@ +""" +Unit test for Cube Build testing testing determining the correct suffix for the filename. +""" + +from unittest.mock import MagicMock + +import pytest + +from jwst.cube_build.ifu_cube import IFUCubeData + + +@pytest.fixture +def mock_ifu_cube(): + """Fixture to create an IFUCubeData instance with mocked dependencies.""" + # Mock the inputs to __init__ to avoid needing real JWST data models + with MagicMock() as mock_models, MagicMock() as mock_inst_info: + cube = IFUCubeData( + input_models=mock_models, + output_name_base="test_output", + output_type="band", + linear_wave=True, + instrument="MIRI", + list_par1=["1"], + list_par2=["SHORT"], + instrument_info=mock_inst_info, + master_table={}, + debug_spaxel="1 1 1", + ) + return cube + + +def test_define_cubename_suffix_miri_single(mock_ifu_cube): + """Test MIRI suffix with a single channel/subchannel.""" + mock_ifu_cube.instrument = "MIRI" + mock_ifu_cube.list_par1 = ["1"] + mock_ifu_cube.list_par2 = ["SHORT"] + + suffix = mock_ifu_cube.define_cubename_suffix() + assert suffix == "_ch1-short" + + +def test_define_cubename_suffix_miri_multi(mock_ifu_cube): + """Test MIRI suffix with multiple channels and sorting logic.""" + mock_ifu_cube.instrument = "MIRI" + mock_ifu_cube.list_par1 = ["1", "2"] + mock_ifu_cube.list_par2 = ["SHORT", "MEDIUM"] + + suffix = mock_ifu_cube.define_cubename_suffix() + # Note: Logic sorts subchannels descending (short -> medium -> long check) + assert "_ch1-2" in suffix + assert "shortmedium" in suffix # depends on alphabetical sort + + +def test_define_cubename_suffix_nirspec(mock_ifu_cube): + """Test NIRSpec suffix generation.""" + mock_ifu_cube.instrument = "NIRSPEC" + mock_ifu_cube.list_par1 = ["G140M"] + mock_ifu_cube.list_par2 = ["F100LP"] + mock_ifu_cube.num_bands = 1 + + suffix = mock_ifu_cube.define_cubename_suffix() + assert suffix == "_g140m-f100lp" + + +def test_define_cubename_suffix_internal_cal(mock_ifu_cube): + """Test the addition of the _internal flag.""" + mock_ifu_cube.instrument = "MIRI" + mock_ifu_cube.list_par1 = ["1"] + mock_ifu_cube.list_par2 = ["LONG"] + mock_ifu_cube.coord_system = "internal_cal" + + suffix = mock_ifu_cube.define_cubename_suffix() + assert suffix.endswith("_internal") From 36df3c366ae24df3500bce46bb4cf342847cdf96 Mon Sep 17 00:00:00 2001 From: jemorrison Date: Mon, 4 May 2026 09:45:34 -0700 Subject: [PATCH 12/13] change test to monkey patch --- jwst/cube_build/tests/test_cube_suffix.py | 127 ++++++++++++---------- 1 file changed, 68 insertions(+), 59 deletions(-) diff --git a/jwst/cube_build/tests/test_cube_suffix.py b/jwst/cube_build/tests/test_cube_suffix.py index 24348b2e020..f81dcb294fe 100644 --- a/jwst/cube_build/tests/test_cube_suffix.py +++ b/jwst/cube_build/tests/test_cube_suffix.py @@ -2,72 +2,81 @@ Unit test for Cube Build testing testing determining the correct suffix for the filename. """ -from unittest.mock import MagicMock - import pytest from jwst.cube_build.ifu_cube import IFUCubeData @pytest.fixture -def mock_ifu_cube(): - """Fixture to create an IFUCubeData instance with mocked dependencies.""" - # Mock the inputs to __init__ to avoid needing real JWST data models - with MagicMock() as mock_models, MagicMock() as mock_inst_info: - cube = IFUCubeData( - input_models=mock_models, - output_name_base="test_output", - output_type="band", - linear_wave=True, - instrument="MIRI", - list_par1=["1"], - list_par2=["SHORT"], - instrument_info=mock_inst_info, - master_table={}, - debug_spaxel="1 1 1", - ) - return cube - - -def test_define_cubename_suffix_miri_single(mock_ifu_cube): - """Test MIRI suffix with a single channel/subchannel.""" - mock_ifu_cube.instrument = "MIRI" - mock_ifu_cube.list_par1 = ["1"] - mock_ifu_cube.list_par2 = ["SHORT"] - - suffix = mock_ifu_cube.define_cubename_suffix() - assert suffix == "_ch1-short" - - -def test_define_cubename_suffix_miri_multi(mock_ifu_cube): - """Test MIRI suffix with multiple channels and sorting logic.""" - mock_ifu_cube.instrument = "MIRI" - mock_ifu_cube.list_par1 = ["1", "2"] - mock_ifu_cube.list_par2 = ["SHORT", "MEDIUM"] - - suffix = mock_ifu_cube.define_cubename_suffix() - # Note: Logic sorts subchannels descending (short -> medium -> long check) - assert "_ch1-2" in suffix - assert "shortmedium" in suffix # depends on alphabetical sort - - -def test_define_cubename_suffix_nirspec(mock_ifu_cube): - """Test NIRSpec suffix generation.""" - mock_ifu_cube.instrument = "NIRSPEC" - mock_ifu_cube.list_par1 = ["G140M"] - mock_ifu_cube.list_par2 = ["F100LP"] - mock_ifu_cube.num_bands = 1 - - suffix = mock_ifu_cube.define_cubename_suffix() +def cube_instance(): + """ + Initialize IFUCubeData with the bare minimum to survive __init__. + Use standard strings and lists + """ + return IFUCubeData( + input_models=[], # Empty list + output_name_base="test_base", + output_type="band", + linear_wave=True, + instrument="MIRI", # Default + list_par1=[], # Empty list + list_par2=[], # Empty list + instrument_info={}, # Empty dict + master_table={}, # Empty dict + debug_spaxel="0 0 0", # Must be 3 integers in a string + ) + + +def test_define_cubename_suffix_miri_1band(monkeypatch, cube_instance): + """Test MIRI suffix logic by monkeypatching instance attributes.""" + # 1. Set the state for a MIRI Ch1 Long cube + monkeypatch.setattr(cube_instance, "instrument", "MIRI") + monkeypatch.setattr(cube_instance, "list_par1", ["1"]) + monkeypatch.setattr(cube_instance, "list_par2", ["LONG"]) + + # 2. Run the function + suffix = cube_instance.define_cubename_suffix() + + # 3. Assert correct suffix + assert "_ch1-long" in suffix + + +def test_define_cubename_suffix_miri(monkeypatch, cube_instance): + """Test MIRI suffix logic by monkeypatching instance attributes.""" + # 1. Set the state for a MIRI Ch1 Short & medium, Long Ch 2 + monkeypatch.setattr(cube_instance, "instrument", "MIRI") + monkeypatch.setattr(cube_instance, "list_par1", ["1", "2", "1"]) + monkeypatch.setattr(cube_instance, "list_par2", ["SHORT", "LONG", "MEDIUM"]) + + # 2. Run the function + suffix = cube_instance.define_cubename_suffix() + + # 3. Assert (MIRI logic sorts subchannels according to increasing wavelength) + assert "_ch1-2-shortmediumlong" in suffix + + +def test_define_cubename_suffix_nirspec(monkeypatch, cube_instance): + """Test NIRSpec logic and the base name stripping feature.""" + # 1. Setup NIRSpec state for G140M and F100LP + monkeypatch.setattr(cube_instance, "instrument", "NIRSPEC") + monkeypatch.setattr(cube_instance, "list_par1", ["G140M"]) + monkeypatch.setattr(cube_instance, "list_par2", ["F100LP"]) + monkeypatch.setattr(cube_instance, "num_bands", 1) + + # 2. Run the function + suffix = cube_instance.define_cubename_suffix() + + # 3. Assertions: grating first then filter assert suffix == "_g140m-f100lp" -def test_define_cubename_suffix_internal_cal(mock_ifu_cube): - """Test the addition of the _internal flag.""" - mock_ifu_cube.instrument = "MIRI" - mock_ifu_cube.list_par1 = ["1"] - mock_ifu_cube.list_par2 = ["LONG"] - mock_ifu_cube.coord_system = "internal_cal" +def test_define_cubename_suffix_internal(monkeypatch, cube_instance): + """Test the internal_cal coordinate system suffix.""" + monkeypatch.setattr(cube_instance, "instrument", "MIRI") + monkeypatch.setattr(cube_instance, "list_par1", ["1"]) + monkeypatch.setattr(cube_instance, "list_par2", ["LONG"]) + monkeypatch.setattr(cube_instance, "coord_system", "internal_cal") + + suffix = cube_instance.define_cubename_suffix() - suffix = mock_ifu_cube.define_cubename_suffix() - assert suffix.endswith("_internal") + assert suffix == "_ch1-long_internal" From 0af9fe3f43ac38f87c772a3ef3c174705c36a28a Mon Sep 17 00:00:00 2001 From: jemorrison Date: Tue, 5 May 2026 09:00:25 -0700 Subject: [PATCH 13/13] update unit test --- jwst/cube_build/tests/test_cube_suffix.py | 54 +++++++++++------------ 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/jwst/cube_build/tests/test_cube_suffix.py b/jwst/cube_build/tests/test_cube_suffix.py index f81dcb294fe..4c781cd7ebc 100644 --- a/jwst/cube_build/tests/test_cube_suffix.py +++ b/jwst/cube_build/tests/test_cube_suffix.py @@ -27,12 +27,12 @@ def cube_instance(): ) -def test_define_cubename_suffix_miri_1band(monkeypatch, cube_instance): - """Test MIRI suffix logic by monkeypatching instance attributes.""" +def test_define_cubename_suffix_miri_1band(cube_instance): + """Test MIRI suffix logic by cube_instance attributes.""" # 1. Set the state for a MIRI Ch1 Long cube - monkeypatch.setattr(cube_instance, "instrument", "MIRI") - monkeypatch.setattr(cube_instance, "list_par1", ["1"]) - monkeypatch.setattr(cube_instance, "list_par2", ["LONG"]) + cube_instance.instrument = "MIRI" + cube_instance.list_par1 = ["1"] + cube_instance.list_par2 = ["LONG"] # 2. Run the function suffix = cube_instance.define_cubename_suffix() @@ -41,42 +41,40 @@ def test_define_cubename_suffix_miri_1band(monkeypatch, cube_instance): assert "_ch1-long" in suffix -def test_define_cubename_suffix_miri(monkeypatch, cube_instance): - """Test MIRI suffix logic by monkeypatching instance attributes.""" - # 1. Set the state for a MIRI Ch1 Short & medium, Long Ch 2 - monkeypatch.setattr(cube_instance, "instrument", "MIRI") - monkeypatch.setattr(cube_instance, "list_par1", ["1", "2", "1"]) - monkeypatch.setattr(cube_instance, "list_par2", ["SHORT", "LONG", "MEDIUM"]) +def test_define_cubename_suffix_miri(cube_instance): + """Test MIRI suffix logic for multiple bands and channels""" + + cube_instance.instrument = "MIRI" + cube_instance.list_par1 = ["1", "2", "1"] + cube_instance.list_par2 = ["SHORT", "LONG", "MEDIUM"] - # 2. Run the function suffix = cube_instance.define_cubename_suffix() - # 3. Assert (MIRI logic sorts subchannels according to increasing wavelength) + # Assert (MIRI logic sorts subchannels according to increasing wavelength) assert "_ch1-2-shortmediumlong" in suffix -def test_define_cubename_suffix_nirspec(monkeypatch, cube_instance): - """Test NIRSpec logic and the base name stripping feature.""" - # 1. Setup NIRSpec state for G140M and F100LP - monkeypatch.setattr(cube_instance, "instrument", "NIRSPEC") - monkeypatch.setattr(cube_instance, "list_par1", ["G140M"]) - monkeypatch.setattr(cube_instance, "list_par2", ["F100LP"]) - monkeypatch.setattr(cube_instance, "num_bands", 1) +def test_define_cubename_suffix_nirspec(cube_instance): + """Test NIRSpec logic fir 1 grating and 1 band .""" + + cube_instance.instrument = "NIRSPEC" + cube_instance.list_par1 = ["G140M"] + cube_instance.list_par2 = ["F100LP"] - # 2. Run the function suffix = cube_instance.define_cubename_suffix() - # 3. Assertions: grating first then filter + # Assert grating first then filter assert suffix == "_g140m-f100lp" -def test_define_cubename_suffix_internal(monkeypatch, cube_instance): +def test_define_cubename_suffix_internal(cube_instance): """Test the internal_cal coordinate system suffix.""" - monkeypatch.setattr(cube_instance, "instrument", "MIRI") - monkeypatch.setattr(cube_instance, "list_par1", ["1"]) - monkeypatch.setattr(cube_instance, "list_par2", ["LONG"]) - monkeypatch.setattr(cube_instance, "coord_system", "internal_cal") + + cube_instance.instrument = "MIRI" + cube_instance.list_par1 = ["4"] + cube_instance.list_par2 = ["LONG"] + cube_instance.coord_system = "internal_cal" suffix = cube_instance.define_cubename_suffix() - assert suffix == "_ch1-long_internal" + assert suffix == "_ch4-long_internal"