Skip to content
1 change: 1 addition & 0 deletions changes/10433.cube_build.rst
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please also add a breaking change note, since we're removing step parameters?

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Clean up cube build interface, removing the parameters ``pipeline`` and ``single``, which were intended for internal use only.
10 changes: 0 additions & 10 deletions docs/jwst/cube_build/arguments.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,6 @@ each band will be created.
``cube_build``, it sets ``pipeline=2``. When running stand-alone or in
:ref:`calwebb_spec3 <calwebb_spec3>` pipeline, the parameter ``pipeline=3`` is set for ``cube_build``.

- ``pipeline=2`` sets up the rules for making :ref:`calwebb_spec2 <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 <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
Expand Down
27 changes: 4 additions & 23 deletions jwst/cube_build/cube_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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 <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
Expand All @@ -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")

Expand Down Expand Up @@ -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")

Expand Down
107 changes: 15 additions & 92 deletions jwst/cube_build/cube_build_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,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
Expand All @@ -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.
Expand Down Expand Up @@ -190,47 +187,16 @@ 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()

# 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
# ________________________________________________________________________________
# set the output base name
input_table = data_types.DataTypes(
read_in_models, self.single, self.output_file, self.output_dir
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
Expand All @@ -239,11 +205,6 @@ def process(self, input_data):
# 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
Expand All @@ -254,17 +215,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}")
Expand Down Expand Up @@ -296,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"],
}

Expand Down Expand Up @@ -333,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)
# ________________________________________________________________________________
Expand All @@ -354,10 +302,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
Expand All @@ -366,21 +310,17 @@ 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"]
list_par2 = cube_pars[icube]["par2"]
thiscube = ifu_cube.IFUCubeData(
self.pipeline,
input_models,
self.output_name_base,
self.pars_input["output_type"],
Expand Down Expand Up @@ -413,27 +353,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
Expand Down Expand Up @@ -498,8 +425,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):
Expand All @@ -523,8 +449,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):
Expand All @@ -547,8 +472,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):
Expand All @@ -570,8 +494,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):
Expand Down
12 changes: 4 additions & 8 deletions jwst/cube_build/data_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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)}")

Expand All @@ -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):
Expand Down
Loading
Loading