Repository: gammapy/gammapy
Pull request #5837 (2025-04-23 11:21:31)

Adapt parameter conf_min/conf_max (more stable default for ul and sensitivity)
Resolves the issue found in https://github.com/gammapy/gammapy-benchmarks/pull/158
The recent changes of con_min/conf_max are not sufficient for ul computation  if the fitted norm/error tends to zero.
I don't see a way out other than setting the min/max to some large values by default.
 (it also solves nan in sensitivity computation).


Patch:
diff --git a/gammapy/estimators/flux.py b/gammapy/estimators/flux.py
index 158b3afc3..16949b9a6 100644
--- a/gammapy/estimators/flux.py
+++ b/gammapy/estimators/flux.py
@@ -76,7 +76,8 @@ class FluxEstimator(ParameterEstimator):
     ):
         self.source = source
 
-        self.norm = _get_default_norm(norm, scan_n_sigma=n_sigma_ul + 1, interp="log")
+        scan_n_sigma = np.maximum(n_sigma_ul, n_sigma_sensitivity) + 1
+        self.norm = _get_default_norm(norm, scan_n_sigma=scan_n_sigma, interp="log")
 
         super().__init__(
             null_value=0,
diff --git a/gammapy/estimators/parameter.py b/gammapy/estimators/parameter.py
index 8f434f5e8..9b5ce7c94 100644
--- a/gammapy/estimators/parameter.py
+++ b/gammapy/estimators/parameter.py
@@ -445,21 +445,12 @@ class ParameterSensitivityEstimator:
     def parameter_matching_significance(self, datasets):
         """Parameter value  matching the target significance"""
 
-        if ~np.isfinite(self.parameter.min):
-            vmin = self.parameter.value / 1e3
-        else:
-            vmin = self.parameter.min
-        if ~np.isfinite(self.parameter.max):
-            vmax = self.parameter.value * 1e3
-        else:
-            vmax = self.parameter.max
-
         with warnings.catch_warnings():
             warnings.simplefilter("ignore")
             roots, res = find_roots(
                 self._fcn,
-                vmin,
-                vmax,
+                self.parameter.conf_min,
+                self.parameter.conf_max,
                 args=(datasets,),
                 nbin=100,
                 maxiter=self.max_niter,
diff --git a/gammapy/modeling/parameter.py b/gammapy/modeling/parameter.py
index 93cf7ee70..28a9a339d 100644
--- a/gammapy/modeling/parameter.py
+++ b/gammapy/modeling/parameter.py
@@ -358,25 +358,30 @@ class Parameter:
     @property
     def conf_min(self):
         """Confidence minimum value as a `float`.
-
-        Return parameter minimum if defined, otherwise return the scan_min.
+        Return parameter minimum if defined, otherwise  a default is estimated from value and error.
         """
         if not np.isnan(self.min):
             return self.min
         else:
-            return self.value - self._step * self.scan_n_sigma
+            min_ = self.value - self._step * self.scan_n_sigma
+            large_step = np.maximum(self._step, np.abs(self.value))
+            min_ = np.minimum(min_, -large_step * 1e5)
+            return min_
 
     # TODO: possibly allow to set this independently
     @property
     def conf_max(self):
         """Confidence maximum value as a `float`.
-
-        Return parameter maximum if defined, otherwise return the scan_max.
+        Return parameter maximum if defined, otherwise a default is estimated from value and error.
         """
+
         if not np.isnan(self.max):
             return self.max
         else:
-            return self.value + self._step * self.scan_n_sigma
+            max_ = self.value + self._step * self.scan_n_sigma
+            large_step = np.maximum(self._step, np.abs(self.value))
+            max_ = np.maximum(max_, large_step * 1e5)
+            return max_
 
     @property
     def scan_min(self):


Test patch:
diff --git a/gammapy/estimators/points/tests/test_sensitivity.py b/gammapy/estimators/points/tests/test_sensitivity.py
index 03d4bb83e..eaa41439e 100644
--- a/gammapy/estimators/points/tests/test_sensitivity.py
+++ b/gammapy/estimators/points/tests/test_sensitivity.py
@@ -117,6 +117,6 @@ def test_parameter_sensitivity_estimator(spectrum_dataset):
     estimator = ParameterSensitivityEstimator(spectral_model.amplitude, 0, rtol=1e-2)
 
     value = estimator.run(datasets)
-    assert_allclose(value, 4.570882e-12, rtol=1e-2)
+    assert_allclose(value, 4.67553e-12, rtol=1e-2)
 
     assert_allclose(spectral_model.amplitude.value, default_value, rtol=1e-2)