internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: gammapy/gammapy

Issue:
Remove sub-module hierarchy for gammapy.detect
All functionality in `gammapy.detect` should be exposed in that namespace.
I.e. users should not have to import from sub-modules like `gammapy.detect.cwt`.

The number of user-facing functions / classes should be reduced and names should be made more explicit where needed so that they make sense in `gammapy.detect`.



Discussion:

Resolving patch:
diff --git a/gammapy/detect/__init__.py b/gammapy/detect/__init__.py
--- a/gammapy/detect/__init__.py
+++ b/gammapy/detect/__init__.py
@@ -1,9 +1,10 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-"""Source detection and measurement methods"""
-from . import blob
-from . import cwt
-from . import iterfind
-from . import matched_filter
-from . import sex
-from . import test_statistic
-from . import utils
+"""Source detection and measurement methods.
+"""
+from .blob import *
+from .cwt import *
+from .iterfind import *
+from .matched_filter import *
+from .sex import *
+from .test_statistic import *
+from .utils import *
diff --git a/gammapy/detect/iterfind.py b/gammapy/detect/iterfind.py
--- a/gammapy/detect/iterfind.py
+++ b/gammapy/detect/iterfind.py
@@ -273,11 +273,15 @@ def fit_stat(xpos, ypos, sigma, flux):
     def estimate_flux(self, source, method='sum_and_divide'):
         """Estimate flux in a circular region around the source.
 
-        Note: It's not clear which is the better flux estimate:
-        If method == 'sum_and_divide':
-            flux = (counts.sum() - background.sum()) / exposure.mean()
-        else:
-            flux = ((counts - background) / exposure).sum()
+        Note: It's not clear which is the better flux estimate.
+
+        * ``method == 'sum_and_divide'``::
+
+              flux = (counts.sum() - background.sum()) / exposure.mean()
+
+        * ``method = 'divide_and_sum'``::
+
+              flux = ((counts - background) / exposure).sum()
         """
         logging.debug('Estimating flux')
         SOURCE_RADIUS_FACTOR = 2


Test patch:
Repository: gammapy/gammapy

Issue:
gamma_rate wrongly computed in data.ObservationStats
Hi,

We (@santiagopita and I) just noticed that `gamma_rate` is wrongly computed in `data.ObservationStats`. For instance, the final result in https://github.com/gammapy/gammapy-extra/blob/master/notebooks/cta_simulation.ipynb reports (entry 13) a `gamma_rate` of 2.51/min, while it should be `excess/livetime`~30/min.

I am not sure where the problem exactly lies, but to start with, I noticed that in `data.obs_stats.ObservationStats.__init__`, the statement:

`self.gamma_rate = gamma_rate or n_on / livetime`

should be

`self.gamma_rate = gamma_rate or (n_on - self.alpha_obs * n_off) / livetime`

however that does not solve the issue reported here.

Thanks a lot.
Cheers


Discussion:

Resolving patch:
diff --git a/gammapy/data/obs_stats.py b/gammapy/data/obs_stats.py
--- a/gammapy/data/obs_stats.py
+++ b/gammapy/data/obs_stats.py
@@ -52,12 +52,14 @@ def __init__(self,
         self.obs_id = obs_id
         self.livetime = livetime
         if alpha is not None:
+            self.a_on = alpha
+            self.a_off = 1
             self.alpha_obs = alpha
         elif a_off > 0:
             self.alpha_obs = a_on / a_off
         else:
             self.alpha_obs = 0
-        self.gamma_rate = gamma_rate or n_on / livetime
+        self.gamma_rate = gamma_rate or self.excess / livetime
         self.bg_rate = bg_rate or self.alpha_obs * n_off / livetime
 
     @classmethod
diff --git a/gammapy/spectrum/observation.py b/gammapy/spectrum/observation.py
--- a/gammapy/spectrum/observation.py
+++ b/gammapy/spectrum/observation.py
@@ -30,8 +30,8 @@ class SpectrumStats(ObservationStats):
     """
 
     def __init__(self, **kwargs):
-        self.energy_min = kwargs.pop('energy_min', None)
-        self.energy_max = kwargs.pop('energy_max', None)
+        self.energy_min = kwargs.pop('energy_min', Quantity(0, 'TeV'))
+        self.energy_max = kwargs.pop('energy_max', Quantity(0, 'TeV'))
         super(SpectrumStats, self).__init__(**kwargs)
 
     def __str__(self):
@@ -313,6 +313,7 @@ def stats_in_range(self, bin_min, bin_max):
         stats_list = [self.stats(ii) for ii in idx]
         stacked_stats = SpectrumStats.stack(stats_list)
         stacked_stats.livetime = self.livetime
+        stacked_stats.gamma_rate = stacked_stats.excess / stacked_stats.livetime
         stacked_stats.obs_id = self.obs_id
         stacked_stats.energy_min = self.e_reco[bin_min]
         stacked_stats.energy_max = self.e_reco[bin_max + 1]


Test patch:
Repository: gammapy/gammapy

Issue:
WcsGeom.create bug in handling of refpix argument
I tried to make a WcsGeom with image center on the Galactic center at `skydir = (glon, glat) = (0, 0)`.

```python
from gammapy.maps import WcsGeom
geom = WcsGeom.create(skydir=(0, 0), npix=(800, 600), refpix=(400, 300), binsz=0.1, coordsys='GAL')
```
and got this:
```
>>> print(geom, geom.wcs)
WcsGeom

	npix      : 800 x 600 pix
	coordsys  : GAL
	projection: CAR
	center    : 359.9 deg, -10.0 deg
	width     : 80.0 x 60.0 deg
	ndim      : 2
	axes      : 
 WCS Keywords

Number of WCS axes: 2
CTYPE : 'GLON-CAR'  'GLAT-CAR'  
CRVAL : 0.0  0.0  
CRPIX : 400.0  400.0  
PC1_1 PC1_2  : 1.0  0.0  
PC2_1 PC2_2  : 0.0  1.0  
CDELT : -0.1  0.1  
NAXIS : 800  600
```

It looks like `skydir` is correctly assigned to `CRVAL`, but `refpix=(400, 300)` is not used correctly, it results in `CRPIX=(400, 400)` and as a result the sky image center isn't at `skydir = (glon, glat) = (0, 0)` like I wanted.

@woodmd @adonath - This is a bug, no?



Discussion:

Resolving patch:
diff --git a/gammapy/maps/wcs.py b/gammapy/maps/wcs.py
--- a/gammapy/maps/wcs.py
+++ b/gammapy/maps/wcs.py
@@ -373,12 +373,19 @@ def create(cls, npix=None, binsz=0.5, proj='CAR', coordsys='CEL', refpix=None,
         else:
             npix = cast_to_shape(npix, shape, int)
 
-        # FIXME: Need to propagate refpix
+        if refpix is None:
+            refpix = (None, None)
 
         header = _make_image_header(
-            npix[0].flat[0], npix[1].flat[0],
-            binsz[0].flat[0], float(xref), float(yref),
-            proj, coordsys, refpix, refpix,
+            nxpix=npix[0].flat[0],
+            nypix=npix[1].flat[0],
+            binsz=binsz[0].flat[0],
+            xref=float(xref),
+            yref=float(yref),
+            proj=proj,
+            coordsys=coordsys,
+            xrefpix=refpix[0],
+            yrefpix=refpix[1],
         )
         wcs = WCS(header)
         return cls(wcs, npix, cdelt=binsz, axes=axes, conv=conv)


Test patch:
diff --git a/gammapy/maps/tests/test_wcs.py b/gammapy/maps/tests/test_wcs.py
--- a/gammapy/maps/tests/test_wcs.py
+++ b/gammapy/maps/tests/test_wcs.py
@@ -199,3 +199,10 @@ def test_geom_repr():
     geom = WcsGeom.create(skydir=(0, 0), npix=(10, 4), binsz=50,
                           coordsys='GAL', proj='AIT')
     assert geom.__class__.__name__ in repr(geom)
+
+
+def test_geom_refpix():
+    refpix = (400, 300)
+    geom = WcsGeom.create(skydir=(0, 0), npix=(800, 600),
+                          refpix=refpix, binsz=0.1, coordsys='GAL')
+    assert_allclose(geom.wcs.wcs.crpix, refpix)
\ No newline at end of file
Repository: gammapy/gammapy

Issue:
Install fails when Cython is not already installed
**Gammapy version**

Every I tested

**Bug description**

The setup.py hard imports Cython, so setuptools has no chance to install it.

**Expected behavior**

Install cython if needed before attempting to import it

**To Reproduce**


```
$ python -m venv test_gammapy
$ . test_gammapy/bin/activate
$ pip install gammapy
```


**Other information**
There is a very simple fix: provide a pyproject.toml that requires cython for the build system.



Discussion:

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -1,16 +1,5 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-import sys
 import setuptools
-from distutils.version import LooseVersion
-
-if LooseVersion(setuptools.__version__) < "30.3":
-    sys.stderr.write("ERROR: setuptools 30.3 or later is required by gammapy\n")
-    sys.exit(1)
-
-# TODO: check if setuptools_scm, numpy, ... are OK
-# Exit with good error message telling people to install those first if not
-
-
 from Cython.Build import cythonize
 from distutils.extension import Extension
 import numpy as np


Test patch:
Repository: gammapy/gammapy

Issue:
Empty edisp in stacked spectral dataset
**Gammapy version**

Latest dev

**Bug description**

When I use the `extended_source_spectral_analysis` notebook and stack the spectral datasets, I end up with an empty edisp. See cell 11 here: https://github.com/AtreyeeS/test_notebooks/blob/master/extended_source_spectral_analysis.ipynb

Stacking of spectral datasets seems to work normally in the standard spectral analysis tutorial, so I do not understand what is going wrong here. The edisp_kernel is behaving normally on the stacked map dataset as well.



Discussion:

Resolving patch:
diff --git a/gammapy/datasets/map.py b/gammapy/datasets/map.py
--- a/gammapy/datasets/map.py
+++ b/gammapy/datasets/map.py
@@ -1216,6 +1216,7 @@ def to_spectrum_dataset(self, on_region, containment_correction=False, name=None
             edisp = EDispKernelMap.from_edisp_kernel(
                 edisp=edisp, geom=RegionGeom(on_region)
             )
+            edisp.exposure_map.data = kwargs["exposure"].data.copy()
             kwargs["edisp"] = edisp
 
         return SpectrumDataset(**kwargs)


Test patch:
diff --git a/gammapy/datasets/tests/test_map.py b/gammapy/datasets/tests/test_map.py
--- a/gammapy/datasets/tests/test_map.py
+++ b/gammapy/datasets/tests/test_map.py
@@ -217,6 +217,7 @@ def test_to_spectrum_dataset(sky_model, geom, geom_etrue, edisp_mode):
     assert spectrum_dataset.exposure.unit == "m2s"
     assert spectrum_dataset.edisp.get_edisp_kernel().e_reco.nbin == 2
     assert spectrum_dataset.edisp.get_edisp_kernel().e_true.nbin == 3
+    assert_allclose(spectrum_dataset.edisp.exposure_map.data[1], 3.070884e+09, rtol=1e-5)
     assert np.sum(spectrum_dataset_mask.counts.data) == 0
     assert spectrum_dataset_mask.data_shape == (2, 1, 1)
     assert spectrum_dataset_corrected.exposure.unit == "m2s"
@@ -1003,6 +1004,7 @@ def test_map_dataset_on_off_to_spectrum_dataset(images):
 
     assert spectrum_dataset.name != dataset.name
 
+
 @requires_data()
 def test_map_dataset_on_off_to_spectrum_dataset_weights():
     e_reco = MapAxis.from_bounds(1, 10, nbin=3, unit="TeV", name="energy")
@@ -1046,7 +1048,6 @@ def test_map_dataset_on_off_to_spectrum_dataset_weights():
     assert_allclose(spectrum_dataset.alpha.data[:,0,0], [0, 0.25, 0.25])
 
 
-
 @requires_data()
 def test_map_dataset_on_off_cutout(images):
     dataset = get_map_dataset_onoff(images)
Repository: gammapy/gammapy

Issue:
Label issue in background rate figures
**Gammapy version**
0.18.2

**Bug description**
The label of the background rate figure seems wrong.
See the documentation, cell 24, bottom left figure:
https://docs.gammapy.org/0.18.2/tutorials/cta.html?highlight=cta#IRFs

The labels are all identical `offset = 0.2 deg`.




Discussion:
Merci! We will respond to your issue shortly. In the meantime, try `import gammapy; gammapy.song()`

Resolving patch:
diff --git a/gammapy/irf/background.py b/gammapy/irf/background.py
--- a/gammapy/irf/background.py
+++ b/gammapy/irf/background.py
@@ -13,6 +13,7 @@
 
 class BackgroundIRF(IRF):
     """Background IRF base class"""
+
     default_interp_kwargs = dict(
         bounds_error=False, fill_value=None, values_scale="log"
     )
@@ -35,7 +36,7 @@ def from_table(cls, table, format="gadf-dl3"):
             Background IRF class.
         """
         axes = MapAxes.from_table(table, format=format)[cls.required_axes]
-        
+
         # Spec says key should be "BKG", but there are files around
         # (e.g. CTA 1DC) that use "BGD". For now we support both
         if "BKG" in table.colnames:
@@ -64,12 +65,7 @@ def from_table(cls, table, format="gadf-dl3"):
             log.debug("Transposing background table on read")
             data = data.transpose()
 
-        return cls(
-            axes=axes,
-            data=data.value,
-            meta=table.meta,
-            unit=data.unit
-        )
+        return cls(axes=axes, data=data.value, meta=table.meta, unit=data.unit)
 
 
 class Background3D(BackgroundIRF):
@@ -107,6 +103,7 @@ class Background3D(BackgroundIRF):
     <BLANKLINE>
 
     """
+
     tag = "bkg_3d"
     required_axes = ["energy", "fov_lon", "fov_lat"]
 
@@ -249,8 +246,8 @@ def plot_energy_dependence(self, ax=None, offset=None, **kwargs):
 
         for off in offset:
             bkg = self.evaluate(offset=off, energy=energy)
-            kwargs.setdefault("label", f"offset = {off:.1f}")
-            ax.plot(energy, bkg.value, **kwargs)
+            label = f"offset = {off:.2f}"
+            ax.plot(energy, bkg.value, label=label, **kwargs)
 
         ax.set_xscale("log")
         ax.set_yscale("log")


Test patch:
Repository: gammapy/gammapy

Issue:
MapDataset._counts_data  property should be reset when counts are modified
The `MapDataset._counts_data` property is used to convert `counts` to `float` for likelihood calculations. 
It is currently defined as `@lazyproperty`. The direct consequence is that when `counts` is modified (e.g. by `MapDataset.fake`) the counts data actually used for likelihood calculations are silently different from the ones actually defined on the `MapDataset`. 
This could be solved by adding a getter/setter definition for `counts` that would reset the lazy property.



Discussion:
The `.counts` property currently uses the `LazyFitsData` descriptor. This requires a little workaround for the setter, e.g. by introducing a hidden `_counts` and `_counts_data` attribute. 

Resolving patch:
diff --git a/gammapy/datasets/map.py b/gammapy/datasets/map.py
--- a/gammapy/datasets/map.py
+++ b/gammapy/datasets/map.py
@@ -902,13 +902,9 @@ def plot_residuals(
 
         return ax_spatial, ax_spectral
 
-    @lazyproperty
-    def _counts_data(self):
-        return self.counts.data.astype(float)
-
     def stat_sum(self):
         """Total likelihood given the current model parameters."""
-        counts, npred = self._counts_data, self.npred().data
+        counts, npred = self.counts.data.astype(float), self.npred().data
 
         if self.mask is not None:
             return cash_sum_cython(counts[self.mask.data], npred[self.mask.data])


Test patch:
Repository: gammapy/gammapy

Issue:
MapAxis.from_energy_bounds does not necessarily return axis with energy unit
**Gammapy version**
dev

**Bug description**
Calling `MapAxis.from_energy_edges` and `MapAxis.from_energy_bounds` without `unit` argument and with regular floats will return an axis without unit. You can also create an axis with any unit actually.

**Expected behavior**
Only axes with energy equivalent units should be returned. An energy axis is not only the name, but also the energy.

So we could set a default unit to TeV. `Quantity.__init__` would then fail if units are not consistent. 

We could also add a `is_equivalent` test.

**To Reproduce**
axis = MapAxis.from_energy_bounds(0.1,10,2, unit = "deg")
axis = MapAxis.from_energy_bounds(0.1,10,2)

**Other information**
Any other information you think will be useful for us to fix the issue can go here.



Discussion:

Resolving patch:
diff --git a/gammapy/maps/axes.py b/gammapy/maps/axes.py
--- a/gammapy/maps/axes.py
+++ b/gammapy/maps/axes.py
@@ -419,9 +419,8 @@ def from_energy_edges(cls, energy_edges, unit=None, name=None, interp="log"):
         """
         energy_edges = u.Quantity(energy_edges, unit)
 
-        if unit is None:
-            unit = energy_edges.unit
-            energy_edges = energy_edges.to(unit)
+        if not energy_edges.unit.is_equivalent("TeV"):
+            raise ValueError(f"Please provide a valid energy unit, got {energy_edges.unit} instead.")
 
         if name is None:
             name = "energy"
@@ -472,6 +471,9 @@ def from_energy_bounds(
             unit = energy_max.unit
             energy_min = energy_min.to(unit)
 
+        if not energy_max.unit.is_equivalent("TeV"):
+            raise ValueError(f"Please provide a valid energy unit, got {energy_max.unit} instead.")
+
         if per_decade:
             nbin = np.ceil(np.log10(energy_max / energy_min).value * nbin)
 


Test patch:
diff --git a/gammapy/maps/tests/test_axes.py b/gammapy/maps/tests/test_axes.py
--- a/gammapy/maps/tests/test_axes.py
+++ b/gammapy/maps/tests/test_axes.py
@@ -309,6 +309,14 @@ def test_mapaxis_from_bounds(nodes, interp):
         MapAxis.from_bounds(1, 1, 1)
 
 
+def test_map_axis_from_energy_units():
+    with pytest.raises(ValueError):
+        axis = MapAxis.from_energy_bounds(0.1, 10, 2, unit="deg")
+
+    with pytest.raises(ValueError):
+        axis = MapAxis.from_energy_edges([0.1, 1, 10] * u.deg)
+
+
 @pytest.mark.parametrize(("nodes", "interp", "node_type"), MAP_AXIS_NODE_TYPES)
 def test_mapaxis_pix_to_coord(nodes, interp, node_type):
     axis = MapAxis(nodes, interp=interp, node_type=node_type)
Repository: gammapy/gammapy

Issue:
Warning incorrectly raised by SpectrumDatasetMaker.make_exposure with use_region_center=True
**Gammapy version**
v0.19

**Bug description**
If using `SpectrumDatasetMaker.make_exposure` with pointlike IRFs, a warning is incorrectly raised if the option `user_region_center` is set to True.

See this lien of code:
https://github.com/gammapy/gammapy/blob/ba7d377bf48bd53d268d2dc14be1c1eb013a1e42/gammapy/makers/spectrum.py#L67

**Expected behavior**
No warning should be raised.

**To Reproduce**

**Other information**



Discussion:

Resolving patch:
diff --git a/gammapy/makers/spectrum.py b/gammapy/makers/spectrum.py
--- a/gammapy/makers/spectrum.py
+++ b/gammapy/makers/spectrum.py
@@ -64,7 +64,7 @@ def make_exposure(self, geom, observation):
         )
 
         is_pointlike = exposure.meta.get("is_pointlike", False)
-        if is_pointlike:
+        if is_pointlike and self.use_region_center is False:
             log.warning(
                 "MapMaker: use_region_center=False should not be used with point-like IRF. "
                 "Results are likely inaccurate."


Test patch:
diff --git a/gammapy/makers/tests/test_spectrum.py b/gammapy/makers/tests/test_spectrum.py
--- a/gammapy/makers/tests/test_spectrum.py
+++ b/gammapy/makers/tests/test_spectrum.py
@@ -245,25 +245,30 @@ def test_region_center_spectrum_dataset_maker_magic_dl3(
         containment_correction=True, selection=["exposure"]
     )
 
-    dataset = maker.run(spectrum_dataset_magic_crab, observations_magic_dl3[0])
-    dataset_average = maker_average.run(
-        spectrum_dataset_magic_crab, observations_magic_dl3[0]
-    )
+
+    # containment correction should fail
     with pytest.raises(ValueError):
         maker_correction.run(spectrum_dataset_magic_crab, observations_magic_dl3[0])
 
+    # use_center = True should run and raise no warning
+    dataset = maker.run(spectrum_dataset_magic_crab, observations_magic_dl3[0])
+
     assert isinstance(dataset, SpectrumDataset)
     assert dataset.exposure.meta["is_pointlike"]
-    assert dataset_average.exposure.meta["is_pointlike"]
+    assert "WARNING" not in [record.levelname for record in caplog.records]
 
-    assert "WARNING" in [record.levelname for record in caplog.records]
+    # use_center = False should raise a warning
+    dataset_average = maker_average.run(
+        spectrum_dataset_magic_crab, observations_magic_dl3[0]
+    )
 
+    assert "WARNING" in [record.levelname for record in caplog.records]
     message = (
         "MapMaker: use_region_center=False should not be used with point-like IRF. "
         "Results are likely inaccurate."
     )
-
     assert message in [record.message for record in caplog.records]
+    assert dataset_average.exposure.meta["is_pointlike"]
 
 
 @requires_data()
Repository: gammapy/gammapy

Issue:
FitResult print output is confusing
A `print(fit_result)` displays both the `covariance_result` and the `optimize_result` as `OptimizeResult`, eg: see cell 19
https://docs.gammapy.org/dev/tutorials/starting/analysis_2.html#Fit-the-model

Reminder issue to fix it during the sprint week


Discussion:
Indeed this line return the incorrect private data member: https://github.com/gammapy/gammapy/blob/master/gammapy/modeling/fit.py#L638

Resolving patch:
diff --git a/gammapy/modeling/fit.py b/gammapy/modeling/fit.py
--- a/gammapy/modeling/fit.py
+++ b/gammapy/modeling/fit.py
@@ -635,7 +635,7 @@ def optimize_result(self):
     @property
     def covariance_result(self):
         """Optimize result"""
-        return self._optimize_result
+        return self._covariance_result
 
     def __repr__(self):
         str_ = ""


Test patch:
diff --git a/gammapy/modeling/tests/test_fit.py b/gammapy/modeling/tests/test_fit.py
--- a/gammapy/modeling/tests/test_fit.py
+++ b/gammapy/modeling/tests/test_fit.py
@@ -95,6 +95,8 @@ def test_run(backend):
 
     assert result.success
     assert result.optimize_result.method == "migrad"
+    assert result.covariance_result.method == "hesse"
+    assert result.covariance_result.success == True
 
     assert_allclose(pars["x"].value, 2, rtol=1e-3)
     assert_allclose(pars["y"].value, 3e2, rtol=1e-3)
Repository: gammapy/gammapy

Issue:
Use context of Pool directly
**Gammapy version**

Current master


gammapy uses a `multiprocessing.Pool` in two locations and confusingly for me, it is used like this:

```
            with contextlib.closing(Pool(processes=self.n_jobs)) as pool:
                 do_stuff_with_pool
```


Any reason why the context manager of the pool is not used directly?

```
            with Pool(processes=self.n_jobs) as pool:
                 do_stuff_with_pool
```

From the docs:
> Warning
> multiprocessing.pool objects have internal resources that need to be properly managed (like any other resource) by using the pool > as a context manager or by calling close() and terminate() manually. Failure to do this can lead to the process hanging on finalization.

So it seems this might also be the reason for the stuck CI, as using `closing` is not enough, one has to call both `close` and `terminate`? Or just use the pool as context.


Discussion:

Resolving patch:
diff --git a/gammapy/estimators/map/ts.py b/gammapy/estimators/map/ts.py
--- a/gammapy/estimators/map/ts.py
+++ b/gammapy/estimators/map/ts.py
@@ -1,6 +1,5 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """Functions to compute TS images."""
-import contextlib
 import functools
 import logging
 import warnings
@@ -390,12 +389,10 @@ def estimate_flux_map(self, dataset):
         if self.n_jobs is None:
             results = list(map(wrap, positions))
         else:
-            with contextlib.closing(Pool(processes=self.n_jobs)) as pool:
+            with Pool(processes=self.n_jobs) as pool:
                 log.info("Using {} jobs to compute TS map.".format(self.n_jobs))
                 results = pool.map(wrap, positions)
 
-            pool.join()
-
         result = {}
 
         j, i = zip(*positions)
diff --git a/gammapy/makers/reduce.py b/gammapy/makers/reduce.py
--- a/gammapy/makers/reduce.py
+++ b/gammapy/makers/reduce.py
@@ -1,4 +1,3 @@
-import contextlib
 import logging
 from multiprocessing import Pool
 import numpy as np
@@ -146,7 +145,7 @@ def run(self, dataset, observations, datasets=None):
 
         if self.n_jobs is not None and self.n_jobs > 1:
             n_jobs = min(self.n_jobs, len(observations))
-            with contextlib.closing(Pool(processes=n_jobs)) as pool:
+            with Pool(processes=n_jobs) as pool:
                 log.info("Using {} jobs.".format(n_jobs))
                 results = []
                 for base, obs in zip(datasets, observations):


Test patch: