diff --git a/CHANGES.rst b/CHANGES.rst
index 888253bea..f2eed05da 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,6 +1,26 @@
 0.13.3 (Unreleased)
 ===================
 
+
+combine_1d
+----------
+
+- Unit tests were added to combine_1d.  [#3490]
+
+ami
+---
+
+- Fixed indentation bug in ami_analyze, so now all results are sufficiently 
+  close to the results of the stand-alone prototype. Other modifications include 
+  minor tweaks to more closely match those in the prototype code: changed some of 
+  initial values of the estimation parameters, and the filtering routine 
+  arguments.  [#3487]
+
+assign_wcs
+------------
+
+- Fix a one pixel off problem with the Nirspec NRS2 WCS transforms. [#3473]
+
 datamodels
 ----------
 
@@ -26,7 +46,7 @@ photom
 ------
 
 - Updated to zero-out pixels outside the wavelength range of flux calibration
-  and set DQ=DO_NOT_USE. [#3475]
+  and set DQ=DO_NOT_USE. [#3475, #3489]
 
 refpix
 ------
diff --git a/docs/jwst/combine_1d/arguments.rst b/docs/jwst/combine_1d/arguments.rst
index 1c6ef95bc..3d45398af 100644
--- a/docs/jwst/combine_1d/arguments.rst
+++ b/docs/jwst/combine_1d/arguments.rst
@@ -14,9 +14,3 @@ the exposure time (FITS keyword EFFEXPTM) is used as the weight.  If
 the string is "unit_weight" or "unit weight", the same weight (1) will
 be used for all input spectra.  If the string is anything else, a warning
 will be logged and unit weight will be used.
-
-*  ``--background``
-
-Set this boolean parameter to True if the input spectra are all just
-background.  The default is False, meaning that the input are science
-spectra.
diff --git a/docs/jwst/combine_1d/description.rst b/docs/jwst/combine_1d/description.rst
index 58d815401..23e51136c 100644
--- a/docs/jwst/combine_1d/description.rst
+++ b/docs/jwst/combine_1d/description.rst
@@ -12,15 +12,7 @@ spectra have been included, the output is normalized by dividing by
 the sum of the weights.
 
 The weight will typically be the integration time or the exposure time,
-but uniform (unit) weighting can be specified instead.  It is the net
-count rate that uses this weight; that is, the net count rate is multiplied
-by the integration time to get net counts, and it is the net counts that
-are added together and finally divided by the sum of the integration
-times.  The flux weighted by an additional factor of the instrumental
-sensitivity, count rate per unit flux.  The idea is that the quantity
-that is added up should be in units of counts.  If unit weight was
-specified, however, unit weight will be used for both flux and net.
-The data quality (DQ) columns will be combined using bitwise OR.
+but uniform (unit) weighting can be specified instead.
 
 The only part of this step that is not completely straightforward is the
 determination of wavelengths for the output spectrum.  The output
@@ -31,11 +23,9 @@ wavelength would be computed as the average of the wavelengths at the same
 pixel in all the input files.  The combine_1d step is intended to handle a
 more general case where the input wavelength arrays may be offset with
 respect to each other, or they might not align well due to different
-distortions.
-
-All the input wavelength arrays will be concatenated and then sorted.
-The code then looks for "clumps" in wavelength, based on the standard
-deviation of a slice of the concatenated and sorted array of input
+distortions.  All the input wavelength arrays will be concatenated and then
+sorted.  The code then looks for "clumps" in wavelength, based on the
+standard deviation of a slice of the concatenated and sorted array of input
 wavelengths; a small standard deviation implies a clump.  In regions of
 the spectrum where the input wavelengths overlap with somewhat random
 offsets and don't form any clumps, the output wavelengths are computed
@@ -46,9 +36,9 @@ Input
 =====
 An association file specifies which file or files to read for the input
 data.  Each input data file contains one or more 1-D spectra in table
-format, e.g. as written by the extract_1d step.  An input data file can
-be either SpecModel (for one spectrum) or MultiSpecModel format (which
-can contain more than one spectrum).
+format, e.g. as written by the extract_1d step.  Each input data file will
+ordinarily be in MultiSpecModel format (which can contain more than one
+spectrum).
 
 The association file should have an object called "products", which is
 a one-element list containing a dictionary.  This dictionary contains two
@@ -60,8 +50,8 @@ of which contains one input file name, identified by key "expname".
 Output
 ======
 The output will be in CombinedSpecModel format, with a table extension
-having the name COMBINE1D.  This extension will have six columns, giving
-the wavelength, flux, error estimate for the flux, net countrate in
-counts/second, the sum of the weights that were used when combining the
-input spectra, and the number of input spectra that contributed to each
-output pixel.
+having the name COMBINE1D.  This extension will have eight columns, giving
+the wavelength, flux, error estimate for the flux, surface brightness,
+error estimate for the surface brightness, the combined data quality flags,
+the sum of the weights that were used when combining the input spectra,
+and the number of input spectra that contributed to each output pixel.
diff --git a/jwst/ami/ami_analyze.py b/jwst/ami/ami_analyze.py
index 55815ef54..61466e286 100644
--- a/jwst/ami/ami_analyze.py
+++ b/jwst/ami/ami_analyze.py
@@ -48,21 +48,22 @@ def apply_LG(input_model, filter_model, oversample, rotation):
     log.info('Filter: %s', input_model.meta.instrument.filter)
 
     # Load the filter throughput data from the reference file
-    bindown = 6
-    band = webb_psf.get_webbpsf_filter(filter_model, specbin=bindown)
+    bindown = 12
+    band = webb_psf.get_webbpsf_filter(filter_model, specbin=bindown, trim=(3.8e-6,0.05))
 
     # Set up some params that are needed as input to the LG algorithm:
     #  Search window for rotation fine-tuning
-    rots_deg = np.array((-0.5, -0.25, 0.0, 0.25, 0.5))
+    rots_deg = np.array(( -1.00, -0.5, 0.0, 0.5, 1.00))
+
     #  Search range for relative pixel scales
-    relpixscales = np.array((63.0, 63.25, 63.75, 64.0, 64.25)) / 63.75
+    relpixscales = np.array((64.2, 64.4, 64.6, 64.8, 65.0, 65.2, 65.4, 65.6, 65.8)) /65.0
 
     # Convert initial rotation guess from degrees to radians
     rotation = rotation * np.pi / 180.0
 
     # Instantiate the NRM model object
-    jwnrm = NrmModel(mask='JWST', holeshape='hex',
-                pixscale=leastsqnrm.mas2rad(63.52554816773347),
+    jwnrm = NrmModel(mask='jwst', holeshape='hex',
+                pixscale=leastsqnrm.mas2rad(65.),
                 rotate=rotation, rotlist_deg=rots_deg,
                 scallist=relpixscales)
 
diff --git a/jwst/ami/nrm_model.py b/jwst/ami/nrm_model.py
index ac8230879..bf42467da 100755
--- a/jwst/ami/nrm_model.py
+++ b/jwst/ami/nrm_model.py
@@ -329,11 +329,11 @@ class NrmModel:
             self.model_over = leastsqnrm.multiplyenv(self.model_beam, self.fringes)
 
             self.model = np.zeros((self.fov,self.fov, self.model_over.shape[2]))
+
             # loop over slices "sl" in the model
             for sl in range(self.model_over.shape[2]):
                 self.model[:,:,sl] = utils.rebin( self.model_over[:,:,sl],
-                                                (self.over, self.over))
-
+                                            (self.over, self.over))
             return self.model
 
         else:
@@ -371,7 +371,7 @@ class NrmModel:
                     model_binned[:,:,sl] = utils.rebin(
                         self.model_over[:,:,sl], (self.over, self.over))
 
-                    self.model += w*model_binned
+                self.model += w*model_binned
 
             return self.model
 
@@ -582,7 +582,7 @@ class NrmModel:
                 rotate=self.rot_measured, centering=centering)
 
         try:
-            self.gof = goodness_of_fit(img, self.refpsf)
+            self.gof = goodness_of_fit(img,self.refpsf)
         except Exception:
             self.gof = False
 
diff --git a/jwst/assign_wcs/nirspec.py b/jwst/assign_wcs/nirspec.py
index 420c821d9..5cead240e 100644
--- a/jwst/assign_wcs/nirspec.py
+++ b/jwst/assign_wcs/nirspec.py
@@ -1076,12 +1076,12 @@ def dms_to_sca(input_model):
         ystart = 1
     # The SCA coordinates are in full frame
     # The inputs are 1-based, remove -1 when'if they are 0-based
-    # The outputs must be 1-based becaause this is what the model expects.
+    # The outputs must be 1-based because this is what the model expects.
     # If xstart was 0-based and the inputs were 0-based ->
     # Shift(+1)
     subarray2full = models.Shift(xstart - 1) & models.Shift(ystart - 1)
     if detector == 'NRS2':
-        model = models.Shift(-2048) & models.Shift(-2048) | models.Scale(-1) & models.Scale(-1)
+        model = models.Shift(-2047) & models.Shift(-2047) | models.Scale(-1) & models.Scale(-1)
     elif detector == 'NRS1':
         model = models.Identity(2)
     return subarray2full | model
diff --git a/jwst/combine_1d/combine1d.py b/jwst/combine_1d/combine1d.py
index 8cc08c696..22cbf0c37 100644
--- a/jwst/combine_1d/combine1d.py
+++ b/jwst/combine_1d/combine1d.py
@@ -75,14 +75,9 @@ class InputSpectrumModel:
             raise RuntimeError("Don't understand exptime_key = '%s'" %
                                exptime_key)
 
-        got_wcs = True
         try:
-            wcs = spec.meta.wcs
+            self.right_ascension[:], self.declination[:], _ = spec.meta.wcs(0., 0.)
         except AttributeError:
-            got_wcs = False
-        if got_wcs:
-            self.right_ascension[:], self.declination[:], _ = wcs(0.)
-        else:
             self.right_ascension[:] = ms.meta.target.ra
             self.declination[:] = ms.meta.target.dec
             log.warning("There is no WCS in the input.")
@@ -112,9 +107,6 @@ class OutputSpectrumModel:
         weight
         count
         wcs
-        wavelength_dtype
-        flux_dtype
-        dq_dtype
         normalized
     """
 
@@ -129,17 +121,13 @@ class OutputSpectrumModel:
         self.weight = None
         self.count = None
         self.wcs = None
-        self.wavelength_dtype = None
-        self.flux_dtype = None
-        self.dq_dtype = None
         self.normalized = False
 
     def assign_wavelengths(self, input_spectra):
         """Create an array of wavelengths to use for the output spectrum.
 
-        Take the union of all input wavelengths, then call method
-        compute_output_wl to bin wavelengths in groups of the number of
-        overlapping spectra.
+        Take the union of all input wavelengths, then call compute_output_wl
+        to bin wavelengths in groups of the number of overlapping spectra.
 
         Parameters
         ----------
@@ -147,171 +135,14 @@ class OutputSpectrumModel:
             List of input spectra.
         """
 
-        # Save these, so we'll know what data type to use for the output.
-        # The types used for accumulating sums and taking averages may not
-        # be the same as these types.
-        self.wavelength_dtype = input_spectra[0].wavelength.dtype
-        self.flux_dtype = input_spectra[0].flux.dtype
-        self.dq_dtype = input_spectra[0].dq.dtype
-
-        nwl = 0
-        for in_spec in input_spectra:
-            nwl += in_spec.nelem
-
-        # Create an array with all the input wavelengths (i.e. the union
-        # of the input wavelengths).
-        wl = np.zeros(nwl, dtype=np.float64)
-        i = 0
-        for in_spec in input_spectra:
-            nelem = in_spec.nelem
-            # Concatenate current input wavelengths to wl array.
-            wl[i:i + nelem] = in_spec.wavelength.copy()
-            i += nelem
-        wl.sort()
-
-        # count_input will be the number of input spectra that cover the
-        # corresponding wavelength in wl.
-        count_input = np.zeros(nwl, dtype=np.int64)
-        for in_spec in input_spectra:
-            input_wl = in_spec.wavelength
-            # wl0 and wl1 will be about a half pixel wider on either side
-            # of the wavelength range for the current input spectrum.
-            if input_wl[1] > input_wl[0]:       # wavelengths are increasing
-                wl0 = input_wl[0] - 0.5 * (input_wl[1] - input_wl[0])
-                wl1 = input_wl[-1] + 0.5 * (input_wl[-1] - input_wl[-2])
-            elif input_wl[1] < input_wl[0]:     # wavelengths are decreasing
-                wl0 = input_wl[-1] - 0.5 * (input_wl[-2] - input_wl[-1])
-                wl1 = input_wl[0] + 0.5 * (input_wl[0] - input_wl[1])
-            else:
-                raise RuntimeError("Wavelength increment must not be zero.")
-            temp = np.where(wl >= wl0, 1, 0)
-            temp = np.where(wl >= wl1, 0, temp)
-            count_input += temp
-            del temp
-        # This shouldn't happen.
-        if np.any(count_input <= 0.):
-            raise RuntimeError("Problem with input wavelengths.")
+        (wl, n_input_spectra) = count_input(input_spectra)
 
-        self.wavelength = self.compute_output_wl(wl, count_input)
+        self.wavelength = compute_output_wl(wl, n_input_spectra)
 
         self.wcs = create_spectral_wcs(input_spectra[0].right_ascension[0],
                                        input_spectra[0].declination[0],
                                        self.wavelength)
 
-    def compute_output_wl(self, wl, count_input):
-        """Compute output wavelengths.
-
-        In summary, the output wavelengths are computed by binning the
-        input wavelengths in groups of the number of overlapping spectra.
-        The merged and sorted input wavelengths are in `wl`.
-
-        If the wavelength arrays of the input spectra are nearly all the
-        same, the values in wl will be in clumps of N nearly identical
-        values (for N input files).  We want to average the wavelengths
-        in those clumps to get the output wavelengths.  However, if the
-        wavelength scales of the input files differ slightly, the values
-        in wl may clump in some regions but not in others.  The algorithm
-        below tries to find clumps; a clump is identified by having a
-        small standard deviation of all the wavelengths in a slice of wl,
-        compared with neighboring slices.  It is intended that this should
-        not find clumps where they're not present, i.e. in regions where
-        the wavelengths of the input spectra are not well aligned with each
-        other.  These regions will be gaps in the initial determination
-        of output wavelengths based on clumps.  In such regions, output
-        wavelengths will then be computed by just averaging groups of
-        input wavelengths to fill in the gaps.
-
-        Parameters
-        ----------
-        wl : 1-D array
-            An array containing all the wavelengths from all the input
-            spectra, sorted in increasing order.
-
-        count_input : 1-D array
-            An integer array of the same length as `wl`.  For a given
-            array index k (for example), count_input[k] is the number of
-            input spectra that cover wavelength wl[k].
-
-        Returns
-        -------
-        wavelength :  1-D array
-            Array of wavelengths for the output spectrum.
-        """
-
-        nwl = len(wl)
-
-        # sigma is an array of the standard deviation at each element
-        # of wl, over count_input elements.  A small value implies that
-        # there's a clump, i.e. several elements of wl with nearly the
-        # same wavelength.
-        sigma = np.zeros(nwl, dtype=np.float64) + 9999.
-
-        # mean_wl is the mean wavelength over the same slice of wl that we
-        # used to compute sigma.  If sigma is small enough that it looks
-        # as if there's a clump, we'll copy the mean_wl value to temp_wl
-        # to be one element of the output wavelengths.
-        mean_wl = np.zeros(nwl, dtype=np.float64) - 99.
-
-        # temp_wl has the same number of elements as wl, but we expect the
-        # array of output wavelengths to be significantly smaller, so
-        # temp_wl is initialized to a negative value as a flag.  Positive
-        # elements will be copied to the array of output wavelengths.
-        temp_wl = np.zeros(nwl, dtype=np.float64) - 99.
-
-        for k in range(nwl):
-            n = count_input[k]
-            if n == 1:
-                sigma[k] = 0.
-                mean_wl[k] = wl[k]
-                temp_wl[k] = mean_wl[k]
-            else:
-                k1 = k + n // 2 + 1
-                k0 = k1 - n
-                if k0 >= 0 and k1 <= nwl:
-                    sigma[k] = wl[k0:k1].std()
-                    mean_wl[k] = wl[k0:k1].mean()
-                    if sigma[k] == 0.:
-                        temp_wl[k] = mean_wl[k]
-
-        cutoff = 0.8
-        for k in range(nwl):
-            # If sigma[k] equals 0, temp_wl has already been assigned.
-            if sigma[k] > 0.:
-                if k == 0:
-                    if sigma[k] < cutoff * sigma[1]:
-                        temp_wl[k] = mean_wl[k]
-                elif k == nwl - 1:
-                    if sigma[k] < cutoff * sigma[nwl - 2]:
-                        temp_wl[k] = mean_wl[k]
-                else:
-                    if sigma[k] < cutoff * (sigma[k - 1] + sigma[k + 1]) / 2.:
-                        temp_wl[k] = mean_wl[k]
-
-        # Fill gaps in the output wavelengths by taking averages of the
-        # input wavelengths.  If there are n overlapping input spectra,
-        # average a block of n elements of `wl`.
-        done = False
-        i = 0
-        while not done:
-            if i >= nwl:
-                done = True
-            else:
-                n = count_input[i]
-                middle = n // 2
-                if i + middle < nwl:
-                    n = count_input[i + middle]
-                    if i + n < nwl:
-                        # The nominal range is i - 1 to i + n (inclusive) for
-                        # checking whether an output wavelength has already
-                        # been assigned.
-                        low = max(i - 1, 0)
-                        high = min(i + n + 1, nwl - 1)
-                        if temp_wl[low:high].max() <= 0.:
-                            temp_wl[i] = wl[i:i + n].mean()
-                i += n
-
-        return temp_wl[np.where(temp_wl > 0.)].copy()
-
     def accumulate_sums(self, input_spectra):
         """Compute a weighted sum of all the input spectra.
 
@@ -337,13 +168,19 @@ class OutputSpectrumModel:
             List of input spectra.
         """
 
+        # This is the data type for the output spectrum.  We'll use double
+        # precision for accumulating sums for most columns, but for the DQ
+        # array, use the correct output data type.
+        cmb_dtype = datamodels.CombinedSpecModel().spec_table.dtype
+        dq_dtype = cmb_dtype.fields["DQ"][0]
+
         nelem = self.wavelength.shape[0]
 
         self.flux = np.zeros(nelem, dtype=np.float64)
         self.error = np.zeros(nelem, dtype=np.float64)
         self.surf_bright = np.zeros(nelem, dtype=np.float64)
         self.sb_error = np.zeros(nelem, dtype=np.float64)
-        self.dq = np.zeros(nelem, dtype=self.dq_dtype)
+        self.dq = np.zeros(nelem, dtype=dq_dtype)
         self.weight = np.zeros(nelem, dtype=np.float64)
         self.count = np.zeros(nelem, dtype=np.float64)
 
@@ -415,15 +252,9 @@ class OutputSpectrumModel:
             log.warning("Data have not been divided by"
                         " the sum of the weights.")
 
-        dtype = [('WAVELENGTH', self.wavelength_dtype),
-                 ('FLUX', self.flux_dtype),
-                 ('ERROR', self.flux_dtype),
-                 ('SURF_BRIGHT', self.flux_dtype),
-                 ('SB_ERROR', self.flux_dtype),
-                 ('DQ', self.dq_dtype),
-                 ('WEIGHT', self.wavelength_dtype),
-                 ('N_INPUT', np.float64)]
+        cmb_dtype = datamodels.CombinedSpecModel().spec_table.dtype
 
+        # Note that these arrays have to be in the right order.
         data = np.array(list(zip(self.wavelength,
                                  self.flux,
                                  self.error,
@@ -431,7 +262,7 @@ class OutputSpectrumModel:
                                  self.sb_error,
                                  self.dq,
                                  self.weight,
-                                 self.count)), dtype=dtype)
+                                 self.count)), dtype=cmb_dtype)
         output_model = datamodels.CombinedSpecModel(spec_table=data)
 
         return output_model
@@ -446,12 +277,183 @@ class OutputSpectrumModel:
         self.weight = None
         self.count = None
         self.wcs = None
-        self.wavelength_dtype = None
-        self.flux_dtype = None
-        self.dq_dtype = None
         self.normalized = False
 
 
+def count_input(input_spectra):
+    """Determine the number of input spectra that cover each wavelength.
+
+    For any given input spectrum, the array of wavelengths gives the
+    wavelengths at the centers of the pixels.  In this context, the
+    expression that an input spectrum "covers" some particular wavelength
+    (typically not one of the elements in the input spectrum's array of
+    wavelengths) means that this wavelength is within the interval between
+    the left edge of the first pixel and the right edge of the last pixel
+    of the input spectrum.
+
+    Parameters
+    ----------
+    input_spectra : list of InputSpectrumModel objects
+        List of input spectra.
+
+    Returns
+    -------
+    wl : ndarray
+        Sorted list of all the wavelengths in all the input spectra.
+
+    n_input_spectra : ndarray, 1-D
+        For each element of `wl`, the corresponding element of
+        `n_input_spectra` is the number of input spectra that cover the
+        wavelength in `wl`.
+    """
+
+    # Create an array with all the input wavelengths (i.e. the union
+    # of the input wavelengths).
+    wl = np.hstack([in_spec.wavelength for in_spec in input_spectra])
+    wl.sort()
+    nwl = len(wl)
+
+    # n_input_spectra will be the number of input spectra that cover the
+    # corresponding wavelength in wl.
+    n_input_spectra = np.zeros(nwl, dtype=np.int64)
+    for in_spec in input_spectra:
+        input_wl = in_spec.wavelength
+        # wl0 and wl1 will be about a half pixel wider on either side
+        # of the wavelength range for the current input spectrum.
+        if input_wl[1] > input_wl[0]:       # wavelengths are increasing
+            wl0 = input_wl[0] - 0.5 * (input_wl[1] - input_wl[0])
+            wl1 = input_wl[-1] + 0.5 * (input_wl[-1] - input_wl[-2])
+        elif input_wl[1] < input_wl[0]:     # wavelengths are decreasing
+            wl0 = input_wl[-1] - 0.5 * (input_wl[-2] - input_wl[-1])
+            wl1 = input_wl[0] + 0.5 * (input_wl[0] - input_wl[1])
+        else:
+            raise RuntimeError("Wavelength increment must not be zero.")
+        temp = np.where(wl >= wl0, 1, 0)
+        temp = np.where(wl >= wl1, 0, temp)
+        n_input_spectra += temp
+        del temp
+    # This shouldn't happen.
+    if np.any(n_input_spectra <= 0.):
+        raise RuntimeError("Problem with input wavelengths.")
+
+    return (wl, n_input_spectra)
+
+
+def compute_output_wl(wl, n_input_spectra):
+    """Compute output wavelengths.
+
+    In summary, the output wavelengths are computed by binning the
+    input wavelengths in groups of the number of overlapping spectra.
+    The merged and sorted input wavelengths are in `wl`.
+
+    If the wavelength arrays of the input spectra are nearly all the
+    same, the values in wl will be in clumps of N nearly identical
+    values (for N input files).  We want to average the wavelengths
+    in those clumps to get the output wavelengths.  However, if the
+    wavelength scales of the input files differ slightly, the values
+    in wl may clump in some regions but not in others.  The algorithm
+    below tries to find clumps; a clump is identified by having a
+    small standard deviation of all the wavelengths in a slice of wl,
+    compared with neighboring slices.  It is intended that this should
+    not find clumps where they're not present, i.e. in regions where
+    the wavelengths of the input spectra are not well aligned with each
+    other.  These regions will be gaps in the initial determination
+    of output wavelengths based on clumps.  In such regions, output
+    wavelengths will then be computed by just averaging groups of
+    input wavelengths to fill in the gaps.
+
+    Parameters
+    ----------
+    wl : 1-D array
+        An array containing all the wavelengths from all the input
+        spectra, sorted in increasing order.
+
+    n_input_spectra : 1-D array
+        An integer array of the same length as `wl`.  For a given
+        array index k (for example), n_input_spectra[k] is the number of
+        input spectra that cover wavelength wl[k].
+
+    Returns
+    -------
+    wavelength :  1-D array
+        Array of wavelengths for the output spectrum.
+    """
+
+    nwl = len(wl)
+
+    # sigma is an array of the standard deviation at each element
+    # of wl, over n_input_spectra elements.  A small value implies that
+    # there's a clump, i.e. several elements of wl with nearly the
+    # same wavelength.
+    sigma = np.zeros(nwl, dtype=np.float64) + 9999.
+
+    # mean_wl is the mean wavelength over the same slice of wl that we
+    # used to compute sigma.  If sigma is small enough that it looks
+    # as if there's a clump, we'll copy the mean_wl value to temp_wl
+    # to be one element of the output wavelengths.
+    mean_wl = np.zeros(nwl, dtype=np.float64) - 99.
+
+    # temp_wl has the same number of elements as wl, but we expect the
+    # array of output wavelengths to be significantly smaller, so
+    # temp_wl is initialized to a negative value as a flag.  Positive
+    # elements will be copied to the array of output wavelengths.
+    temp_wl = np.zeros(nwl, dtype=np.float64) - 99.
+
+    for k in range(nwl):
+        n = n_input_spectra[k]
+        if n == 1:
+            sigma[k] = 0.
+            mean_wl[k] = wl[k]
+            temp_wl[k] = mean_wl[k]
+        else:
+            k1 = k + n // 2 + 1
+            k0 = k1 - n
+            if k0 >= 0 and k1 <= nwl:
+                sigma[k] = wl[k0:k1].std()
+                mean_wl[k] = wl[k0:k1].mean()
+                if sigma[k] == 0.:
+                    temp_wl[k] = mean_wl[k]
+
+    cutoff = 0.8
+    for k in range(nwl):
+        # If sigma[k] equals 0, temp_wl has already been assigned.
+        if sigma[k] > 0.:
+            if k == 0:
+                if sigma[k] < cutoff * sigma[1]:
+                    temp_wl[k] = mean_wl[k]
+            elif k == nwl - 1:
+                if sigma[k] < cutoff * sigma[nwl - 2]:
+                    temp_wl[k] = mean_wl[k]
+            else:
+                if sigma[k] < cutoff * (sigma[k - 1] + sigma[k + 1]) / 2.:
+                    temp_wl[k] = mean_wl[k]
+
+    # Fill gaps in the output wavelengths by taking averages of the
+    # input wavelengths.  If there are n overlapping input spectra,
+    # average a block of n elements of `wl`.
+    done = False
+    i = 0
+    while not done:
+        if i >= nwl:
+            done = True
+        else:
+            n = n_input_spectra[i]
+            middle = n // 2
+            if i + middle < nwl:
+                n = n_input_spectra[i + middle]
+                if i + n < nwl:
+                    # The nominal range is i - 1 to i + n (inclusive) for
+                    # checking whether an output wavelength has already
+                    # been assigned.
+                    low = max(i - 1, 0)
+                    high = min(i + n + 1, nwl - 1)
+                    if temp_wl[low:high].max() <= 0.:
+                        temp_wl[i] = wl[i:i + n].mean()
+            i += n
+
+    return temp_wl[np.where(temp_wl > 0.)].copy()
+
+
 def check_exptime(exptime_key):
     """Check exptime_key for validity.
 
diff --git a/jwst/photom/photom.py b/jwst/photom/photom.py
index 578f3c638..baeb5dd64 100644
--- a/jwst/photom/photom.py
+++ b/jwst/photom/photom.py
@@ -650,8 +650,9 @@ class DataSet():
         # Get the length of the relative response arrays in this row
         nelem = tabdata['nelem']
 
-        # If the relative response arrays have length > 0, load and include them in
-        # the flux conversion
+        # If the relative response arrays have length > 0,
+        # load and include them in the flux conversion
+        no_cal = None
         if nelem > 0:
             waves = tabdata['wavelength'][:nelem]
             relresps = tabdata['relresponse'][:nelem]
@@ -700,7 +701,9 @@ class DataSet():
                 slit.var_rnoise *= conversion**2
             if slit.var_flat is not None and np.size(slit.var_flat) > 0:
                 slit.var_flat *= conversion**2
-            slit.dq[no_cal] = np.bitwise_or(slit.dq[no_cal], dqflags.pixel['DO_NOT_USE'])
+            if no_cal is not None:
+                slit.dq[..., no_cal] = np.bitwise_or(slit.dq[..., no_cal],
+                                                     dqflags.pixel['DO_NOT_USE'])
             slit.meta.bunit_data = 'MJy/sr'
             slit.meta.bunit_err = 'MJy/sr'
         else:
@@ -712,7 +715,9 @@ class DataSet():
                 self.input.var_rnoise *= conversion**2
             if self.input.var_flat is not None and np.size(self.input.var_flat) > 0:
                 self.input.var_flat *= conversion**2
-            self.input.dq[no_cal] = np.bitwise_or(self.input.dq[no_cal], dqflags.pixel['DO_NOT_USE'])
+            if no_cal is not None:
+                self.input.dq[..., no_cal] = np.bitwise_or(self.input.dq[..., no_cal],
+                                                           dqflags.pixel['DO_NOT_USE'])
             self.input.meta.bunit_data = 'MJy/sr'
             self.input.meta.bunit_err = 'MJy/sr'
 

NIRCAM spec2 error: No source catalog specified in association
Issue [JP-545](https://jira.stsci.edu/browse/JP-545) was created by Hien Tran:

LEVEL_2B failures were seen during SOR4b exercise for the calwebb_spec2 pipeline with IndexErrors : "list index out of range" and "No source catalog specified in association or datamodel ". It appears no source catalog was generated or provided.

set work_directory = /ifs/archive/ops/jwst/info/owlmgr/paths/sdp/asn_creation/cal/level2 
 set recipe_filename = /jwdms/local/pipeline/pkgs/miniconda3/envs/jwstdp-0.12.3-py36/lib/python3.6/site-packages/jwst-0.12.3-py3.6-linux-x86_64.egg/jwst/pipeline//calwebb_spec2.cfg 
 set input_filename = jw00624-o004_20190205t031003_spec2_055_asn.json 
 source `which crds_setup.csh` 
 which crds_setup.csh 
 setenv CRDS_SERVER_URL [https://jwst-serverless-mode.stsci.edu|https://jwst-serverless-mode.stsci.edu/] 
 setenv CRDS_OBSERVATORY jwst 
 setenv CRDS_READONLY_CACHE 1 
 setenv CRDS_LOG_TIME 1 
 setenv CRDS_CLIENT_RETRY_COUNT 1 
 setenv CRDS_CLIENT_RETRY_DELAY_SECONDS 0 
 cd /ifs/archive/ops/jwst/info/owlmgr/paths/sdp/asn_creation/cal/level2 
 source /jwdms/local/pipeline/jwstdp/JWSTDP-2018_2a-190118/bin/ssb_python_on 
 setenv PIPELINE_PATH /home/owlmgr/.local/bin:/jwdms/local/pipeline/owl/bin:/jwdms/local/pipeline/tools:/usr/local/Java/jdk1.8.0_144-64/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/owlmgr/overrides/bin:/jwdms/local/pipeline/jwstdp/JWSTDP-2018_2a-190118/bin:/jwdms/local/pipeline/jwstdp/JWSTDP-2018_2a-190118/bin/linux:/jwdms/local/pipeline/jwstdp/JWSTDP-2018_2a-190118/bin/java:/jwdms/local/pipeline/pkgs/DADSTools/envs/dads/bin 
 setenv PIPELINE_PYTHONPATH /home/owlmgr/overrides/bin:/jwdms/local/pipeline/jwstdp/JWSTDP-2018_2a-190118/bin:/jwdms/local/pipeline/jwstdp/JWSTDP-2018_2a-190118:/jwdms/local/pipeline/pkgs/htcondor-8.4.5/lib/python 
 setenv PIPELINE_XDG_CONFIG_HOME /home/owlmgr/rehearsal_config/defs/sdp_python 
 setenv PIPELINE_XDG_CACHE_HOME /home/owlmgr/rehearsal_config/defs/sdp_python 
 setenv PATH /jwdms/local/pipeline/pkgs/miniconda3/envs/jwstdp-0.12.3-py36/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin 
 rehash 
 unsetenv PYTHONPATH 
 setenv XDG_CONFIG_HOME /home/owlmgr/rehearsal_config/defs/scsb_python 
 setenv XDG_CACHE_HOME /home/owlmgr/rehearsal_config/defs/scsb_python 
 if ( ! -d /home/owlmgr/rehearsal_config/defs/scsb_python/astropy ) then 
 echo SSB python activated; OWL python settings preserved in PIPELINE_PATH,PIPELINE_PYTHONPATH 
 exit 0 
 strun /jwdms/local/pipeline/pkgs/miniconda3/envs/jwstdp-0.12.3-py36/lib/python3.6/site-packages/jwst-0.12.3-py3.6-linux-x86_64.egg/jwst/pipeline//calwebb_spec2.cfg /ifs/archive/ops/jwst/info/owlmgr/paths/sdp/asn_creation/cal/level2/jw00624-o004_20190205t031003_spec2_055_asn.json 
 2019-02-06 21:24:39,134 - stpipe.Spec2Pipeline - INFO - Spec2Pipeline instance created. 
 2019-02-06 21:24:39,136 - stpipe.Spec2Pipeline.bkg_subtract - INFO - BackgroundStep instance created. 
 2019-02-06 21:24:39,137 - stpipe.Spec2Pipeline.assign_wcs - INFO - AssignWcsStep instance created. 
 2019-02-06 21:24:39,138 - stpipe.Spec2Pipeline.imprint_subtract - INFO - ImprintStep instance created. 
 2019-02-06 21:24:39,139 - stpipe.Spec2Pipeline.msa_flagging - INFO - MSAFlagOpenStep instance created. 
 2019-02-06 21:24:39,140 - stpipe.Spec2Pipeline.extract_2d - INFO - Extract2dStep instance created. 
 2019-02-06 21:24:39,141 - stpipe.Spec2Pipeline.flat_field - INFO - FlatFieldStep instance created. 
 2019-02-06 21:24:39,142 - stpipe.Spec2Pipeline.srctype - INFO - SourceTypeStep instance created. 
 2019-02-06 21:24:39,144 - stpipe.Spec2Pipeline.straylight - INFO - StraylightStep instance created. 
 2019-02-06 21:24:39,145 - stpipe.Spec2Pipeline.fringe - INFO - FringeStep instance created. 
 2019-02-06 21:24:39,146 - stpipe.Spec2Pipeline.pathloss - INFO - PathLossStep instance created. 
 2019-02-06 21:24:39,147 - stpipe.Spec2Pipeline.barshadow - INFO - BarShadowStep instance created. 
 2019-02-06 21:24:39,148 - stpipe.Spec2Pipeline.photom - INFO - PhotomStep instance created. 
 2019-02-06 21:24:39,149 - stpipe.Spec2Pipeline.resample_spec - INFO - ResampleSpecStep instance created. 
 2019-02-06 21:24:39,151 - stpipe.Spec2Pipeline.cube_build - INFO - CubeBuildStep instance created. 
 2019-02-06 21:24:39,153 - stpipe.Spec2Pipeline.extract_1d - INFO - Extract1dStep instance created. 
 2019-02-06 21:24:39,157 - stpipe - INFO - Hostname: pljwdmsdp1.stsci.edu 
 2019-02-06 21:24:39,157 - stpipe - INFO - OS: Linux 
 2019-02-06 21:24:39,236 - stpipe.Spec2Pipeline - INFO - Step Spec2Pipeline running with args ('/ifs/archive/ops/jwst/info/owlmgr/paths/sdp/asn_creation/cal/level2/jw00624-o004_20190205t031003_spec2_055_asn.json',). 
 2019-02-06 21:24:40,340 - stpipe.Spec2Pipeline - INFO - Prefetching reference files for dataset: 'jw00624004001_03101_00002_nrcalong_rate.fits' reftypes = ['area', 'barshadow', 'camera', 'collimator', 'cubepar', 'dflat', 'disperser', 'distortion', 'drizpars', 'extract1d', 'fflat', 'filteroffset', 'flat', 'fore', 'fpa', 'fringe', 'ifufore', 'ifupost', 'ifuslicer', 'msa', 'msaoper', 'ote', 'pathloss', 'photom', 'regions', 'resol', 'sflat', 'specwcs', 'straymask', 'wavecorr', 'wavelengthrange', 'wfssbkg'] 
 CRDS - DEBUG - ======================================================================================================================== 
 CRDS - DEBUG - getreferences() CRDS version: 7.2.9, 7.2.9, 5044611 
 CRDS - DEBUG - getreferences() server: [https://jwst-serverless-mode.stsci.edu|https://jwst-serverless-mode.stsci.edu/] 
 CRDS - DEBUG - getreferences() observatory: jwst 
 CRDS - DEBUG - getreferences() reftypes: ('area', 'barshadow', 'camera', 'collimator', 'cubepar', 'dflat', 'disperser', 'distortion', 'drizpars', 'extract1d', 'fflat', 'filteroffset', 'flat', 'fore', 'fpa', 'fringe', 'ifufore', 'ifupost', 'ifuslicer', 'msa', 'msaoper', 'ote', 'pathloss', 'photom', 'regions', 'resol', 'sflat', 'specwcs', 'straymask', 'wavecorr', 'wavelengthrange', 'wfssbkg') 
 CRDS - DEBUG - getreferences() context: None 
 CRDS - DEBUG - getreferences() ignore_cache: False 
 CRDS - DEBUG - CRDS_PATH = /ifs/archive/ops/jwst/ref/tmp_crds/crds/cache 
 CRDS - DEBUG - CRDS_SERVER_URL = [https://jwst-serverless-mode.stsci.edu|https://jwst-serverless-mode.stsci.edu/] 
 CRDS - DEBUG - Using CACHED CRDS reference assignment rules last updated on '2019-01-29 13:55:50.704937' 
 CRDS - DEBUG - Using reference file selection rules 'jwst_0500.pmap' defined by cache. 
 CRDS - DEBUG - Final effective context is 'jwst_0500.pmap' 
 CRDS - DEBUG - Computing best references locally. 
 CRDS - DEBUG - Bestrefs header:\{'META.EXPOSURE.READPATT [READPATT]': 'SHALLOW4', 'META.EXPOSURE.TYPE [EXP_TYPE]': 'NRC_WFSS', 'META.INSTRUMENT.CHANNEL [CHANNEL]': 'LONG', 'META.INSTRUMENT.CORONAGRAPH [CORONMSK]': 'NONE', 'META.INSTRUMENT.DETECTOR [DETECTOR]': 'NRCALONG', 'META.INSTRUMENT.FILTER [FILTER]': 'F322W2', 'META.INSTRUMENT.MODULE [MODULE]': 'A', 'META.INSTRUMENT.NAME [INSTRUME]': 'NIRCAM', 'META.INSTRUMENT.PUPIL [PUPIL]': 'GRISMC', 'META.OBSERVATION.DATE [DATE-OBS]': '2019-02-06', 'META.OBSERVATION.TIME [TIME-OBS]': '14:30:55.389', 'META.SUBARRAY.NAME [SUBARRAY]': 'FULL', 'META.VISIT.TSOVISIT [TSOVISIT]': 'F', 'REFTYPE': 'UNDEFINED'}CRDS - DEBUG - READONLY CACHE skipping config update. 
 CRDS - DEBUG - Reference type 'distortion' defined as 'jwst_nircam_distortion_0017.asdf' 
 CRDS - DEBUG - Reference type 'drizpars' defined as 'jwst_nircam_drizpars_0001.fits' 
 CRDS - DEBUG - Reference type 'flat' defined as 'jwst_nircam_flat_0142.fits' 
 CRDS - DEBUG - Reference type 'photom' defined as 'jwst_nircam_photom_0051.fits' 
 CRDS - DEBUG - Reference type 'specwcs' defined as 'jwst_nircam_specwcs_0011.asdf' 
 CRDS - DEBUG - Reference type 'wavelengthrange' defined as 'jwst_nircam_wavelengthrange_0003.asdf' 
 CRDS - DEBUG - Reference type 'wfssbkg' defined as 'jwst_nircam_wfssbkg_0036.fits' 
 2019-02-06 21:24:40,708 - stpipe.Spec2Pipeline - INFO - Prefetch for AREA reference file is 'N/A'. 
 2019-02-06 21:24:40,708 - stpipe.Spec2Pipeline - INFO - Prefetch for BARSHADOW reference file is 'N/A'. 
 2019-02-06 21:24:40,708 - stpipe.Spec2Pipeline - INFO - Prefetch for CAMERA reference file is 'N/A'. 
 2019-02-06 21:24:40,708 - stpipe.Spec2Pipeline - INFO - Prefetch for COLLIMATOR reference file is 'N/A'. 
 2019-02-06 21:24:40,709 - stpipe.Spec2Pipeline - INFO - Prefetch for CUBEPAR reference file is 'N/A'. 
 2019-02-06 21:24:40,709 - stpipe.Spec2Pipeline - INFO - Prefetch for DFLAT reference file is 'N/A'. 
 2019-02-06 21:24:40,709 - stpipe.Spec2Pipeline - INFO - Prefetch for DISPERSER reference file is 'N/A'. 
 2019-02-06 21:24:40,709 - stpipe.Spec2Pipeline - INFO - Prefetch for DISTORTION reference file is '/ifs/archive/ops/jwst/ref/tmp_crds/crds/cache/references/jwst/nircam/jwst_nircam_distortion_0017.asdf'. 
 2019-02-06 21:24:40,709 - stpipe.Spec2Pipeline - INFO - Prefetch for DRIZPARS reference file is '/ifs/archive/ops/jwst/ref/tmp_crds/crds/cache/references/jwst/nircam/jwst_nircam_drizpars_0001.fits'. 
 2019-02-06 21:24:40,709 - stpipe.Spec2Pipeline - INFO - Prefetch for EXTRACT1D reference file is 'N/A'. 
 2019-02-06 21:24:40,709 - stpipe.Spec2Pipeline - INFO - Prefetch for FFLAT reference file is 'N/A'. 
 2019-02-06 21:24:40,709 - stpipe.Spec2Pipeline - INFO - Prefetch for FILTEROFFSET reference file is 'N/A'. 
 2019-02-06 21:24:40,709 - stpipe.Spec2Pipeline - INFO - Prefetch for FLAT reference file is '/ifs/archive/ops/jwst/ref/tmp_crds/crds/cache/references/jwst/nircam/jwst_nircam_flat_0142.fits'. 
 2019-02-06 21:24:40,710 - stpipe.Spec2Pipeline - INFO - Prefetch for FORE reference file is 'N/A'. 
 2019-02-06 21:24:40,710 - stpipe.Spec2Pipeline - INFO - Prefetch for FPA reference file is 'N/A'. 
 2019-02-06 21:24:40,710 - stpipe.Spec2Pipeline - INFO - Prefetch for FRINGE reference file is 'N/A'. 
 2019-02-06 21:24:40,710 - stpipe.Spec2Pipeline - INFO - Prefetch for IFUFORE reference file is 'N/A'. 
 2019-02-06 21:24:40,710 - stpipe.Spec2Pipeline - INFO - Prefetch for IFUPOST reference file is 'N/A'. 
 2019-02-06 21:24:40,710 - stpipe.Spec2Pipeline - INFO - Prefetch for IFUSLICER reference file is 'N/A'. 
 2019-02-06 21:24:40,710 - stpipe.Spec2Pipeline - INFO - Prefetch for MSA reference file is 'N/A'. 
 2019-02-06 21:24:40,710 - stpipe.Spec2Pipeline - INFO - Prefetch for MSAOPER reference file is 'N/A'. 
 2019-02-06 21:24:40,710 - stpipe.Spec2Pipeline - INFO - Prefetch for OTE reference file is 'N/A'. 
 2019-02-06 21:24:40,710 - stpipe.Spec2Pipeline - INFO - Prefetch for PATHLOSS reference file is 'N/A'. 
 2019-02-06 21:24:40,710 - stpipe.Spec2Pipeline - INFO - Prefetch for PHOTOM reference file is '/ifs/archive/ops/jwst/ref/tmp_crds/crds/cache/references/jwst/nircam/jwst_nircam_photom_0051.fits'. 
 2019-02-06 21:24:40,711 - stpipe.Spec2Pipeline - INFO - Prefetch for REGIONS reference file is 'N/A'. 
 2019-02-06 21:24:40,711 - stpipe.Spec2Pipeline - INFO - Prefetch for RESOL reference file is 'N/A'. 
 2019-02-06 21:24:40,711 - stpipe.Spec2Pipeline - INFO - Prefetch for SFLAT reference file is 'N/A'. 
 2019-02-06 21:24:40,711 - stpipe.Spec2Pipeline - INFO - Prefetch for SPECWCS reference file is '/ifs/archive/ops/jwst/ref/tmp_crds/crds/cache/references/jwst/nircam/jwst_nircam_specwcs_0011.asdf'. 
 2019-02-06 21:24:40,711 - stpipe.Spec2Pipeline - INFO - Prefetch for STRAYMASK reference file is 'N/A'. 
 2019-02-06 21:24:40,711 - stpipe.Spec2Pipeline - INFO - Prefetch for WAVECORR reference file is 'N/A'. 
 2019-02-06 21:24:40,711 - stpipe.Spec2Pipeline - INFO - Prefetch for WAVELENGTHRANGE reference file is '/ifs/archive/ops/jwst/ref/tmp_crds/crds/cache/references/jwst/nircam/jwst_nircam_wavelengthrange_0003.asdf'. 
 2019-02-06 21:24:40,711 - stpipe.Spec2Pipeline - INFO - Prefetch for WFSSBKG reference file is '/ifs/archive/ops/jwst/ref/tmp_crds/crds/cache/references/jwst/nircam/jwst_nircam_wfssbkg_0036.fits'. 
 2019-02-06 21:24:40,712 - stpipe.Spec2Pipeline - INFO - Starting calwebb_spec2 ... 
 2019-02-06 21:24:40,724 - stpipe.Spec2Pipeline - INFO - Processing product jw00624004001_03101_00002_nrcalong 
 2019-02-06 21:24:40,724 - stpipe.Spec2Pipeline - INFO - Working on input /ifs/archive/ops/jwst/info/owlmgr/paths/sdp/asn_creation/cal/level2/jw00624004001_03101_00002_nrcalong_rate.fits ... 
 Traceback (most recent call last): 
 File "/jwdms/local/pipeline/pkgs/miniconda3/envs/jwstdp-0.12.3-py36/lib/python3.6/site-packages/jwst-0.12.3-py3.6-linux-x86_64.egg/jwst/pipeline/calwebb_spec2.py", line 162, in process_exposure_product 
 input.meta.source_catalog.filename = members_by_type['sourcecat'][0] 
 IndexError: list index out of rangeDuring handling of the above exception, another exception occurred:Traceback (most recent call last): 
 File "/jwdms/local/pipeline/pkgs/miniconda3/envs/jwstdp-0.12.3-py36/lib/python3.6/site-packages/jwst-0.12.3-py3.6-linux-x86_64.egg/jwst/pipeline/calwebb_spec2.py", line 91, in process 
 asn.filename 
 File "/jwdms/local/pipeline/pkgs/miniconda3/envs/jwstdp-0.12.3-py36/lib/python3.6/site-packages/jwst-0.12.3-py3.6-linux-x86_64.egg/jwst/pipeline/calwebb_spec2.py", line 165, in process_exposure_product 
 raise IndexError("No source catalog specified in association or datamodel") 
 IndexError: No source catalog specified in association or datamodel 
 Traceback (most recent call last): 
 File "/jwdms/local/pipeline/pkgs/miniconda3/envs/jwstdp-0.12.3-py36/lib/python3.6/site-packages/jwst-0.12.3-py3.6-linux-x86_64.egg/EGG-INFO/scripts/strun", line 26, in <module> 
 step = Step.from_cmdline(sys.argv[1:]) 
 File "/jwdms/local/pipeline/pkgs/miniconda3/envs/jwstdp-0.12.3-py36/lib/python3.6/site-packages/jwst-0.12.3-py3.6-linux-x86_64.egg/jwst/stpipe/step.py", line 162, in from_cmdline 
 return cmdline.step_from_cmdline(args) 
 File "/jwdms/local/pipeline/pkgs/miniconda3/envs/jwstdp-0.12.3-py36/lib/python3.6/site-packages/jwst-0.12.3-py3.6-linux-x86_64.egg/jwst/stpipe/cmdline.py", line 297, in step_from_cmdline 
 step.run(*positional) 
 File "/jwdms/local/pipeline/pkgs/miniconda3/envs/jwstdp-0.12.3-py36/lib/python3.6/site-packages/jwst-0.12.3-py3.6-linux-x86_64.egg/jwst/stpipe/step.py", line 400, in run 
 step_result = self.process(*args) 
 File "/jwdms/local/pipeline/pkgs/miniconda3/envs/jwstdp-0.12.3-py36/lib/python3.6/site-packages/jwst-0.12.3-py3.6-linux-x86_64.egg/jwst/pipeline/calwebb_spec2.py", line 107, in process 
 'One or more products failed to process. Failing calibration.' 
 RuntimeError: One or more products failed to process. Failing calibration. 
 set savestatus = 1

Example logs at [https://pljwdmsweb.stsci.edu/owlgui2/logs/directory_view?directory_path=/ifs/archive/ops/jwst/info/owl/logs/owlmgr_jw00624-o004_20190205t031003_spec2_055_1549486121.298163]
diff --git a/jwst/combine_1d/tests/test_count_input.py b/jwst/combine_1d/tests/test_count_input.py
new file mode 100644
index 000000000..27e4a6794
--- /dev/null
+++ b/jwst/combine_1d/tests/test_count_input.py
@@ -0,0 +1,124 @@
+"""
+Test for combine1d.count_input
+"""
+import numpy as np
+
+# from jwst import datamodels
+from jwst.combine_1d import combine1d
+
+class DummySpectra:
+    def __init__(self, wavelength):
+        # The argument to count_input is a list of InputSpectrumModel
+        # objects, but the only attribute we need is wavelength, so this
+        # DummySpectra class will suffice.
+        self.wavelength = wavelength.copy()
+
+
+def test_1a():
+    input_spectra = create_input_spectra_1()
+
+    (wl, n_input_spectra) = combine1d.count_input(input_spectra)
+
+    truth_1a = truth_array_1a()
+
+    # Compare wl with the correct values.
+    assert np.allclose(wl, truth_1a, rtol=1.e-10)
+
+
+def test_1b():
+    input_spectra = create_input_spectra_1()
+    (wl, n_input_spectra) = combine1d.count_input(input_spectra)
+
+    truth_1b = truth_array_1b()
+
+    # Compare n_input_spectra with the correct values.
+    assert np.allclose(n_input_spectra, truth_1b, rtol=1.e-10)
+
+
+def test_1c():
+    input_spectra = create_input_spectra_1()
+    (wl, n_input_spectra) = combine1d.count_input(input_spectra)
+
+    output_wl = combine1d.compute_output_wl(wl, n_input_spectra)
+
+    truth_1c = truth_array_1c()
+
+    assert np.allclose(output_wl, truth_1c, rtol=1.e-10)
+
+
+def create_input_spectra_1():
+
+    input_spectra = []
+
+    # This is for the case that there are four input spectra, each one
+    # six pixels long.  The wavelengths are roughly the same in each
+    # spectrum.  The differences in wavelength between spectra is small
+    # compared to the spacing between wavelengths in any one spectrum.
+    # That is, the union of the wavelengths in all the input spectra
+    # will be clumped.  For this test, first specify the wavelengths in
+    # these clumps.  Then assign the wavelengths for each input spectrum
+    # by picking one element from each clump.
+    clumps = np.array([[1.372, 1.385, 1.391, 1.402],
+                       [1.701, 1.718, 1.719, 1.723],
+                       [1.979, 1.984, 1.991, 2.013],
+                       [2.212, 2.223, 2.228, 2.310],
+                       [2.475, 2.479, 2.487, 2.496],
+                       [2.716, 2.723, 2.729, 2.741]], dtype=np.float64)
+
+    spec = [clumps[0, 3], clumps[1, 0], clumps[2, 2], clumps[3, 1], clumps[4, 2], clumps[5, 1]]
+    input_spectra.append(DummySpectra(np.array(spec)))
+    spec = [clumps[0, 0], clumps[1, 3], clumps[2, 0], clumps[3, 3], clumps[4, 0], clumps[5, 2]]
+    input_spectra.append(DummySpectra(np.array(spec)))
+    spec = [clumps[0, 2], clumps[1, 1], clumps[2, 1], clumps[3, 2], clumps[4, 3], clumps[5, 0]]
+    input_spectra.append(DummySpectra(np.array(spec)))
+    spec = [clumps[0, 1], clumps[1, 2], clumps[2, 3], clumps[3, 0], clumps[4, 1], clumps[5, 3]]
+    input_spectra.append(DummySpectra(np.array(spec)))
+
+    return input_spectra
+
+
+def truth_array_1a():
+    """Merged and sorted wavelengths from all input files."""
+
+    # Wavelengths (merged and sorted) in input files.
+    return np.array([1.372, 1.385, 1.391, 1.402, 1.701, 1.718, 1.719, 1.723,
+                     1.979, 1.984, 1.991, 2.013, 2.212, 2.223, 2.228, 2.31,
+                     2.475, 2.479, 2.487, 2.496, 2.716, 2.723, 2.729, 2.741],
+                    dtype=np.float64)
+
+
+def truth_array_1b():
+    """Number of input models that cover each of the wavelengths in the
+       combined array.
+
+    The first element in the wl array (see function truth_array_1a) has
+    value 1.372, and that's not actually within the array of wavelengths
+    for the first input model, which are (see function
+    create_input_spectra_1):
+        [1.402 1.701 1.991 2.223 2.487 2.723]
+    But the array that we'll return has 4 (the number of input models)
+    for every element.  How can that be correct?  It's because we assume
+    the pixels in the input models have finite width, but the wavelengths
+    in the input models are the values at the centers of the pixels.  So
+    the range of wavelengths that are actually covered by an input model
+    is not just the interval between the first and last wavelengths in
+    the input model's wavelength array, it's the range from the wavelength
+    at the left edge of the first pixel to the wavelength at the right edge
+    of the last pixel.
+    """
+
+    # n_input_spectra
+    return np.array([4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+                     4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], dtype=np.int64)
+
+
+def truth_array_1c():
+    """Computed wavelengths for the output.
+
+    These are averages of the input wavelengths within clusters, sets
+    of input wavelengths that are closer together than the spacing
+    between pixels.
+    """
+
+    return np.array([1.3875, 1.71525, 1.99175, 2.24325, 2.48425, 2.72725],
+                    dtype=np.float64)
diff --git a/jwst/tests_nightly/general/associations/conftest.py b/jwst/tests_nightly/general/associations/conftest.py
new file mode 100644
index 000000000..3fb53658a
--- /dev/null
+++ b/jwst/tests_nightly/general/associations/conftest.py
@@ -0,0 +1,44 @@
+"""Pytest configurations"""
+import pytest
+
+from jwst.tests_nightly.general.associations.sdp_pools_source import SDPPoolsSource
+
+
+# Add option to specify a single pool name
+def pytest_addoption(parser):
+    parser.addoption(
+        '--sdp-pool', metavar='sdp_pool', default=None,
+        help='SDP test pool to run. Specify the name only, not extension or path'
+    )
+    parser.addoption(
+        '--standard-pool', metavar='standard_pool', default=None,
+        help='Standard test pool to run. Specify the name only, not extension or path'
+    )
+
+
+@pytest.fixture
+def sdp_pool(request):
+    """Retrieve a specific SDP pool to test"""
+    return request.config.getoption('--sdp-pool')
+
+
+@pytest.fixture
+def standard_pool(request):
+    """Retrieve a specific standard pool to test"""
+    return request.config.getoption('--standard-pool')
+
+
+def pytest_generate_tests(metafunc):
+    """Prefetch and parametrize a set of test pools"""
+    if 'pool_path' in metafunc.fixturenames:
+        SDPPoolsSource.inputs_root = metafunc.config.getini('inputs_root')[0]
+        SDPPoolsSource.results_root = metafunc.config.getini('results_root')[0]
+
+        pools = SDPPoolsSource()
+
+        try:
+            pool_paths = pools.pool_paths
+        except Exception:
+            pool_paths = []
+
+        metafunc.parametrize('pool_path', pool_paths)
diff --git a/jwst/tests_nightly/general/associations/sdp_pools_source.py b/jwst/tests_nightly/general/associations/sdp_pools_source.py
new file mode 100644
index 000000000..07a759cda
--- /dev/null
+++ b/jwst/tests_nightly/general/associations/sdp_pools_source.py
@@ -0,0 +1,42 @@
+# #############################################################
+# Setup a base class and instantiate it in order to provide the
+# SDP pool lists for test parametrization.
+# #############################################################
+from jwst.tests.base_classes import BaseJWSTTest
+
+
+class SDPPoolsSource(BaseJWSTTest):
+    """Retreive the SDP pools from the test data source
+
+    These tests are very much tied to using `pytest` and the `ci-watson` plugin.
+    In particular, there are references to `pytest.config` that only exist when
+    running under pytest. Such references are stubbed out with best defaults used.
+    """
+
+    # The following will be set during both plugin instantiation
+    # of `BaseJWSTTest`, or explicitly during dynamic parametrization
+    # The empty string defaults indicate that data source roots have
+    # been defined.
+    inputs_root = ''
+    results_root = ''
+
+    input_loc = 'associations'
+    test_dir = 'sdp'
+    ref_loc = [test_dir, 'truth']
+
+    _pool_paths = None
+    _truth_paths = None
+
+    @property
+    def pool_paths(self):
+        """Get the association pools"""
+        if self._pool_paths is None:
+            self._pool_paths = self.data_glob(self.test_dir, 'pools', glob='*.csv')
+        return self._pool_paths
+
+    @property
+    def truth_paths(self):
+        """Get the truth associations"""
+        if self._truth_paths is None:
+            self._truth_paths = self.data_glob(*self.ref_loc, glob='*.json')
+        return self._truth_paths
diff --git a/jwst/tests_nightly/general/associations/test_sdp_pools.py b/jwst/tests_nightly/general/associations/test_sdp_pools.py
index 62b7a9470..676fde538 100644
--- a/jwst/tests_nightly/general/associations/test_sdp_pools.py
+++ b/jwst/tests_nightly/general/associations/test_sdp_pools.py
@@ -10,10 +10,10 @@ import pytest
 from jwst.associations.lib.diff import (
     compare_asn_files,
 )
-from jwst.tests.base_classes import BaseJWSTTest
-
 from jwst.associations.main import Main as asn_generate
 
+from jwst.tests_nightly.general.associations.sdp_pools_source import SDPPoolsSource
+
 # Configure logging
 logger = logging.getLogger(__name__)
 logger.addHandler(logging.NullHandler())
@@ -25,72 +25,21 @@ TEST_ARGS = ['--dry-run', '--no-merge']
 pool_regex = re.compile(r'(?P<proposal>jw.+?)_(?P<versionid>.+)_pool')
 
 
-# #############################################################
-# Setup a base class and instantiate it in order to provide the
-# file lists for the test parametrization.
-# #############################################################
-class AssociationBase(BaseJWSTTest):
-    """Set testing environment
-
-    These tests are very much tied to using `pytest` and the `ci-watson` plugin.
-    In particular, there are references to `pytest.config` that only exist when
-    running under pytest. Such references are stubbed out with best defaults used.
-
-    """
-
-    try:
-        inputs_root = pytest.config.getini('inputs_root')[0]
-        results_root = pytest.config.getini('results_root')[0]
-    except AttributeError:
-        logger.warning(
-            '`pytest.config` referenced outside of a pytest run.'
-            '\n`inputs_root` and `results_root` set to empty.'
-        )
-        inputs_root = ''
-        results_root = ''
-
-    input_loc = 'associations'
-    test_dir = 'sdp'
-    ref_loc = [test_dir, 'truth']
-
-    _pool_paths = None
-    _truth_paths = None
-
-    @property
-    def pool_paths(self):
-        """Get the association pools"""
-        if self._pool_paths is None:
-            self._pool_paths = self.data_glob(self.test_dir, 'pools', glob='*.csv')
-        return self._pool_paths
-
-    @property
-    def truth_paths(self):
-        """Get the truth associations"""
-        if self._truth_paths is None:
-            self._truth_paths = self.data_glob(*self.ref_loc, glob='*.json')
-        return self._truth_paths
-
-ASN_BASE = AssociationBase()
-try:
-    POOL_PATHS = ASN_BASE.pool_paths
-except Exception as e:
-    POOL_PATHS = e
+# Mark expected failures. Key is the pool name
+# and value is the reason message.
+EXPECTED_FAILS = {
+    'jw00624_20190205t031003_pool': 'Issue #655',
+    'jw80600_20171108T041522_pool': 'PR #3450',
+    'jw87600_20180824T213416_pool': 'Issue #3039',
+    'jw98010_20171108T062332_pool': 'PR #3450',
+}
 
 
 # #####
 # Tests
 # #####
-@pytest.mark.skipif(
-    isinstance(POOL_PATHS, Exception),
-    reason='Test pool files not available. Reason={}'.format(POOL_PATHS)
-)
-class TestSDPPools(AssociationBase):
+class TestSDPPools(SDPPoolsSource):
     """Test createion of association from SDP-created pools"""
-
-    @pytest.mark.parametrize(
-        'pool_path',
-        [] if isinstance(POOL_PATHS, Exception) else POOL_PATHS
-    )
     def test_against_standard(self, pool_path):
         """Compare a generated association against a standard
 
@@ -120,23 +69,19 @@ class TestSDPPools(AssociationBase):
         )
         truth_paths = [
             self.get_data(truth_path)
-            for truth_path in ASN_BASE.truth_paths
+            for truth_path in self.truth_paths
             if asn_regex.match(truth_path)
         ]
 
         # Compare the association sets.
         try:
             compare_asn_files(generated_path.glob('*.json'), truth_paths)
-        except AssertionError as error:
-            if 'Associations do not share a common set of products' in str(error):
-                pytest.xfail('Issue #3039')
+        except AssertionError:
+            if pool in EXPECTED_FAILS:
+                pytest.xfail(EXPECTED_FAILS[pool])
             else:
                 raise
 
-    @pytest.mark.parametrize(
-        'pool_path',
-        [] if isinstance(POOL_PATHS, Exception) else POOL_PATHS
-    )
     def test_dup_product_names(self, pool_path):
         """Check for duplicate product names for a pool"""
 
@@ -160,3 +105,11 @@ class TestSDPPools(AssociationBase):
         ]
 
         assert not multiples, 'Multiple product names: {}'.format(multiples)
+
+    def test_specified_sdp_pool(self, sdp_pool):
+        """Test a command-line specified pool"""
+        if sdp_pool:
+            pool_path = Path(self.test_dir) / 'pools' / (sdp_pool + '.csv')
+            self.test_against_standard(pool_path)
+        else:
+            pytest.skip('No SDP pool specified using `--sdp-pool` command-line option.')
diff --git a/jwst/tests_nightly/general/associations/test_standards.py b/jwst/tests_nightly/general/associations/test_standards.py
index 5fe25713c..a4b4ec23b 100644
--- a/jwst/tests_nightly/general/associations/test_standards.py
+++ b/jwst/tests_nightly/general/associations/test_standards.py
@@ -66,24 +66,26 @@ standards = [
     MakePars('pool_005_spec_niriss'),
     MakePars('pool_006_spec_nirspec'),
     MakePars('pool_007_spec_miri'),
-    MakePars('pool_009_spec_miri_lv2bkg'),
-    MakePars('pool_010_spec_nirspec_lv2bkg'),
-    MakePars('pool_011_spec_miri_lv2bkg_lrs'),
+    MakePars('pool_009_spec_miri_lv2bkg', xfail='Issue #3041'),
+    MakePars('pool_010_spec_nirspec_lv2bkg', xfail='Issue #3041'),
+    MakePars('pool_011_spec_miri_lv2bkg_lrs', xfail='Issue #3041'),
     MakePars('pool_013_coron_nircam'),
     MakePars('pool_014_ami_niriss'),
-    MakePars('pool_015_spec_nirspec_lv2bkg_reversed', main_args=LV2_ONLY_ARGS),
-    MakePars('pool_016_spec_nirspec_lv2bkg_double', main_args=LV2_ONLY_ARGS),
+    MakePars('pool_015_spec_nirspec_lv2bkg_reversed', main_args=LV2_ONLY_ARGS,
+             xfail='Issue #3041'),
+    MakePars('pool_016_spec_nirspec_lv2bkg_double', main_args=LV2_ONLY_ARGS,
+             xfail='Issue #3041'),
     MakePars('pool_017_spec_nirspec_lv2imprint'),
-    MakePars('pool_018_all_exptypes', main_args=LV2_ONLY_ARGS),
-    MakePars('pool_019_niriss_wfss'),
+    MakePars('pool_018_all_exptypes', main_args=LV2_ONLY_ARGS, xfail='Issue #3041'),
+    MakePars('pool_019_niriss_wfss', xfail='Issue #3039'),
     MakePars('pool_020_00009_image_miri'),
     MakePars('pool_021_tso'),
     MakePars('pool_022_tso_noflag'),
     MakePars('pool_023_nirspec_msa_3nod', main_args=LV2_ONLY_ARGS),
     MakePars('pool_024_nirspec_fss_nods'),
-    MakePars('pool_025_nirspec_fss_nod_chop'),
+    MakePars('pool_025_nirspec_fss_nod_chop', xfail='Issue #3041'),
     MakePars('pool_026_mir_image_tso'),
-    MakePars('pool_027_nirspec_ifu_nods'),
+    MakePars('pool_027_nirspec_ifu_nods', xfail='Issue #3041'),
     MakePars('pool_028_mir_lrsfs_nods'),
 ]
 
@@ -130,10 +132,20 @@ class TestAgainstStandards(BaseJWSTTest):
         # Compare the association sets.
         try:
             compare_asn_files(generated_path.glob('*.json'), truth_paths)
-        except AssertionError as error:
-            if 'Associations do not share a common set of products' in str(error):
-                pytest.xfail('Issue #3039')
-            elif 'Associations have the following product name duplication' in str(error):
-                pytest.xfail('Issue #3041')
+        except AssertionError:
+            if standard_pars.xfail:
+                pytest.xfail(standard_pars.xfail)
             else:
                 raise
+
+    def test_specified_standard_pool(self, standard_pool):
+        """Test against a standard pool specified from the command-line"""
+        if standard_pool:
+            for standard_pars in standards:
+                if standard_pars.pool_root == standard_pool:
+                    break
+            else:
+                assert False, f'Pool "{standard_pool}" specified by `--standard_pool` is not in test set.'
+            self.test_against_standard(standard_pars)
+        else:
+            pytest.skip('No standard pool specified using `--standard_pool` command-line option.')
diff --git a/jwst/tests_nightly/general/nirspec/test_nirspec_steps_single.py b/jwst/tests_nightly/general/nirspec/test_nirspec_steps_single.py
index 127f0c6e3..a61f673fd 100644
--- a/jwst/tests_nightly/general/nirspec/test_nirspec_steps_single.py
+++ b/jwst/tests_nightly/general/nirspec/test_nirspec_steps_single.py
@@ -244,16 +244,16 @@ class TestNIRSpecMasterBackground_IFU(BaseJWSTTest):
         # read in the valid wavelengths of the user-1d
         user_background_model = datamodels.open(user_background)
         user_wave = user_background_model.spec[0].spec_table['wavelength']
-        user_flux = user_background_model.spec[0].spec_table['net']
+        user_flux = user_background_model.spec[0].spec_table['flux']
         user_wave_valid = np.where(user_flux > 0)
         min_user_wave = np.amin(user_wave[user_wave_valid])
         max_user_wave = np.amax(user_wave[user_wave_valid])
         user_background_model.close()
         # find the waverange covered by both user and science
-        sci_spec_1d = sci_1d.spec[0].spec_table['net']
+        sci_spec_1d = sci_1d.spec[0].spec_table['flux']
         sci_spec_wave = sci_1d.spec[0].spec_table['wavelength']
 
-        result_spec_1d = result_1d.spec[0].spec_table['net']
+        result_spec_1d = result_1d.spec[0].spec_table['flux']
 
         sci_wave_valid = np.where(sci_spec_1d > 0)
         min_wave = np.amin(sci_spec_wave[sci_wave_valid])

pip install -e .[test]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio
apt-get update apt-get install -y gcc
3.8
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
asdf==2.6.0
astropy==5.2.2
attrs==25.3.0
certifi==2025.1.31
charset-normalizer==3.4.1
ci-watson==0.6.1
coverage==7.6.1
crds==12.1.4
drizzle==1.14.4
exceptiongroup==1.2.2
execnet==2.1.1
filelock==3.16.1
gwcs==0.17.1
hypothesis==6.113.0
idna==3.10
iniconfig==2.1.0
jsonschema==2.6.0
-e git+https://github.com/spacetelescope/jwst.git@e616b03252652bbc769200dd116313f91ad506e3#egg=jwst
numpy==1.24.4
packaging==24.2
Parsley==1.3
photutils==1.8.0
pluggy==1.5.0
pyerfa==2.0.0.3
pytest==8.3.5
pytest-arraydiff==0.6.1
pytest-astropy==0.11.0
pytest-astropy-header==0.2.2
pytest-asyncio==0.24.0
pytest-cov==5.0.0
pytest-doctestplus==1.3.0
pytest-filter-subpackage==0.2.0
pytest-mock==3.14.0
pytest-remotedata==0.4.1
pytest-xdist==3.6.1
PyYAML==6.0.2
requests==2.32.3
requests-mock==1.12.1
scipy==1.10.1
semantic-version==2.10.0
sortedcontainers==2.4.0
spherical-geometry==1.2.23
stsci.image==2.3.5
stsci.imagestats==1.6.3
stsci.stimage==0.2.6
tomli==2.2.1
urllib3==2.2.3
verhawk==0.0.3

name: jwst
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=24.2=py38h06a4308_0
  - python=3.8.20=he870216_0
  - readline=8.2=h5eee18b_0
  - setuptools=75.1.0=py38h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.44.0=py38h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - asdf==2.6.0
      - astropy==5.2.2
      - attrs==25.3.0
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - ci-watson==0.6.1
      - coverage==7.6.1
      - crds==12.1.4
      - drizzle==1.14.4
      - exceptiongroup==1.2.2
      - execnet==2.1.1
      - filelock==3.16.1
      - gwcs==0.17.1
      - hypothesis==6.113.0
      - idna==3.10
      - iniconfig==2.1.0
      - jsonschema==2.6.0
      - jwst==0.13.3a0.dev22+ge616b032
      - numpy==1.24.4
      - packaging==24.2
      - parsley==1.3
      - photutils==1.8.0
      - pluggy==1.5.0
      - pyerfa==2.0.0.3
      - pytest==8.3.5
      - pytest-arraydiff==0.6.1
      - pytest-astropy==0.11.0
      - pytest-astropy-header==0.2.2
      - pytest-asyncio==0.24.0
      - pytest-cov==5.0.0
      - pytest-doctestplus==1.3.0
      - pytest-filter-subpackage==0.2.0
      - pytest-mock==3.14.0
      - pytest-remotedata==0.4.1
      - pytest-xdist==3.6.1
      - pyyaml==6.0.2
      - requests==2.32.3
      - requests-mock==1.12.1
      - scipy==1.10.1
      - semantic-version==2.10.0
      - sortedcontainers==2.4.0
      - spherical-geometry==1.2.23
      - stsci-image==2.3.5
      - stsci-imagestats==1.6.3
      - stsci-stimage==0.2.6
      - tomli==2.2.1
      - urllib3==2.2.3
      - verhawk==0.0.3
prefix: /opt/conda/envs/jwst

jwst/combine_1d/tests/test_count_input.py::test_1a jwst/combine_1d/tests/test_count_input.py::test_1b jwst/combine_1d/tests/test_count_input.py::test_1c
jwst/ami/ami_analyze.py docs/jwst/combine_1d/arguments.rst jwst/photom/photom.py jwst/ami/nrm_model.py CHANGES.rst jwst/assign_wcs/nirspec.py docs/jwst/combine_1d/description.rst jwst/combine_1d/combine1d.py
jwst/ami/ami_analyze.py docs/jwst/combine_1d/arguments.rst jwst/photom/photom.py jwst/ami/nrm_model.py CHANGES.rst jwst/assign_wcs/nirspec.py docs/jwst/combine_1d/description.rst jwst/combine_1d/combine1d.py