Repository: astropy/specutils

Issue:
FluxConservingResampler does not properly propagate the uncertainty
The FluxConservingResampler function does not properly propagate the uncertainties. This function, as stated in the documentation (https://specutils.readthedocs.io/en/stable/api/specutils.manipulation.FluxConservingResampler.html#specutils.manipulation.FluxConservingResampler.resample1d) should be based on formulas reported on Carnall+2017 paper
(https://arxiv.org/pdf/1705.05165.pdf). However, you can verify that the result obtained in a simple case, is different from the predictions of the native python package SpectRes (https://github.com/ACCarnall/SpectRes). The error in the source code is probably in computing the output uncertainties as

`        # Calculate output uncertainty
        if pixel_uncer is not None:
            pixel_uncer = pixel_uncer.reshape(new_flux_shape)
            out_variance = np.sum(pixel_uncer * resample_grid**2, axis=-1) / np.sum(
                resample_grid**2, axis=-1)
            out_uncertainty = InverseVariance(np.reciprocal(out_variance))`

While, following Equation (4) in Carnall+2017, the correct "out_variance" should be instead

`            out_variance = np.sum(pixel_uncer * resample_grid**2, axis=-1) / np.sum(
                resample_grid, axis=-1)**2`

Could you please check these lines?

Comments:

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 57103267..f19d7994 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -9,6 +9,8 @@ Bug Fixes
 
 - Arithmetic with constants and Spectrum1D now works in either order. [#964]
 
+- Fixed uncertainty propagation in FluxConservingResampler. [#976]
+
 Other Changes and Additions
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/specutils/manipulation/resample.py b/specutils/manipulation/resample.py
index c55d3c5c..8babeb95 100644
--- a/specutils/manipulation/resample.py
+++ b/specutils/manipulation/resample.py
@@ -196,7 +196,7 @@ class FluxConservingResampler(ResamplerBase):
             pixel_uncer = pixel_uncer.reshape(new_flux_shape)
 
             out_variance = np.sum(pixel_uncer * resample_grid**2, axis=-1) / np.sum(
-                resample_grid**2, axis=-1)
+                resample_grid, axis=-1)**2
             out_uncertainty = InverseVariance(np.reciprocal(out_variance))
         else:
             out_uncertainty = None


Test patch:
diff --git a/specutils/tests/test_resample.py b/specutils/tests/test_resample.py
index 65ad9b2d..ee7136c4 100644
--- a/specutils/tests/test_resample.py
+++ b/specutils/tests/test_resample.py
@@ -64,7 +64,7 @@ def test_stddev_uncert_propogation():
     results = inst(input_spectra, [25, 35, 50, 55]*u.AA)
 
     assert np.allclose(results.uncertainty.array,
-                       np.array([27.5862069, 38.23529412, 17.46724891, 27.5862069]))
+                       np.array([55.17241379, 73.52941176, 27.94759825, 55.17241379]))
 
 
 def delta_wl(saxis):
@@ -127,9 +127,9 @@ def test_multi_dim_spectrum1D():
                                       [6., 6., 6., 6.],
                                       [7., 7., 7., 7.]]) * u.Jy)
     assert np.allclose(results.uncertainty.array,
-                       np.array([[4., 4., 4., 4.],
-                                 [2.77777778, 2.77777778, 2.77777778, 2.77777778],
-                                 [2.04081633, 2.04081633, 2.04081633, 2.04081633]] ))
+                       np.array([[10.66666667, 10.66666667, 10.66666667, 10.66666667],
+                                 [ 7.40740741, 7.40740741, 7.40740741, 7.40740741],
+                                 [ 5.44217687, 5.44217687, 5.44217687, 5.44217687]]))
 
 
 def test_expanded_grid_interp_linear():