Repository: gammapy/gammapy

Issue:
Save containment correction in SpectrumObservation as AREASCAL column
At the moment the containment correction is directly applied to the effective area
https://github.com/gammapy/gammapy/blob/master/gammapy/spectrum/extract.py#L222

I think it would be nicer to save this as AREASCAL column on the FITS file, so it's more transparent which correction was applied.

see https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/spectra/ogip_92_007/node7.html



Discussion:
@joleroi - Assigning to you. If you don't have time, please move to the 0.7 milestone, and unassign yourself if you don't want to do this at all.
Done in https://github.com/gammapy/gammapy/pull/972. Please leave open, the AREASCAL is not taken into account in the fit yet.
Moving milestone to 0.7 (I'll make the 0.6 release tomorrow)
This must go in otherwise no containment correction is possible

Resolving patch:
diff --git a/gammapy/spectrum/fit.py b/gammapy/spectrum/fit.py
--- a/gammapy/spectrum/fit.py
+++ b/gammapy/spectrum/fit.py
@@ -1,17 +1,13 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 import logging
-import os
 import copy
 import numpy as np
 import astropy.units as u
-from astropy.extern import six
-from ..extern.pathlib import Path
 from ..utils.scripts import make_path
 from .utils import CountsPredictor 
 from . import (
     SpectrumObservationList,
     SpectrumObservation,
-    models,
 )
 from .. import stats
 
@@ -67,7 +63,7 @@ def __init__(self, obs_list, model, stat='wstat', forward_folded=True,
         self.fit_range = fit_range
         self.background_model = background_model
         self.method = method
-        self.err_method = method
+        self.err_method = err_method
 
         self._predicted_counts = None
         self._statval = None
@@ -237,6 +233,9 @@ def _predict_counts_helper(self, obs, model, forward_folded=True):
         else:
             raise ValueError('Predicted counts {}'.format(counts))
 
+        # Apply AREASCAL column
+        counts *= obs.on_vector.areascal
+
         return counts
 
     def calc_statval(self):
@@ -248,11 +247,12 @@ def calc_statval(self):
         statval = list()
         for obs, npred in zip(self.obs_list, self.predicted_counts):
             on_stat, off_stat = self._calc_statval_helper(obs, npred)
-            stats = (on_stat, off_stat)
-            statval.append(stats)
+            statvals = (on_stat, off_stat)
+            statval.append(statvals)
         self._statval = statval
         self._restrict_statval()
 
+
     def _calc_statval_helper(self, obs, prediction):
         """Calculate statval one observation
 
@@ -311,7 +311,6 @@ def total_stat(self):
     def _restrict_statval(self):
         """Apply valid fit range to statval
         """
-        restricted_statval = list()
         for statval, valid_range in zip(self.statval, self.bins_in_fit_range):
             # Find bins outside safe range
             idx = np.where(np.invert(valid_range))[0]
@@ -421,8 +420,6 @@ def _make_fit_result(self):
         else:
             bkg_model = None
 
-        covariance = None
-        covar_axis = None
         statname = self.stat
 
         for idx, obs in enumerate(self.obs_list):
@@ -460,7 +457,7 @@ def _est_errors_sherpa(self):
         """Wrapper around Sherpa error estimator"""
         covar = self._sherpa_fit.est_errors()
         covar_axis = list()
-        for idx, par in enumerate(covar.parnames):
+        for par in covar.parnames:
             name = par.split('.')[-1]
             covar_axis.append(name)
         self.covar_axis = covar_axis


Test patch:
diff --git a/gammapy/spectrum/tests/test_fit.py b/gammapy/spectrum/tests/test_fit.py
--- a/gammapy/spectrum/tests/test_fit.py
+++ b/gammapy/spectrum/tests/test_fit.py
@@ -214,6 +214,16 @@ def test_basic_errors(self):
 
         self.fit.result[0].to_table()
 
+    def test_areascal(self):
+        areascal = np.ones(self.fit.obs_list[0].e_reco.nbins)
+        areascal *= 0.5
+        self.fit.obs_list[0].on_vector.areascal = areascal
+        self.fit.fit()
+        pars = self.fit.result[0].model.parameters
+        assert_quantity_allclose(pars['index'].value, 2.2542312883476465)
+        assert_quantity_allclose(pars['amplitude'].quantity,
+                                 4.0165729155447114e-7 * u.Unit('m-2 s-1 TeV-1'))
+
     def test_npred(self):
         self.fit.fit()
         actual = self.fit.obs_list[0].predicted_counts(