We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
stat_null is not available on FluxPoints objects created with gammapy
**Gammapy version**
present dev, 1.1


**Bug description**
For `FluxPoints` created with gammapy estimators, the quantity `stat_null` is not available.


**To Reproduce**
1. Run the `spectral analysis notebook`. Then, accessing `flux_points.stat_null` gives an error
```
File ~/anaconda3/envs/gammapy-1.1/lib/python3.9/site-packages/gammapy/estimators/map/core.py:493, in FluxMaps.stat_null(self)
    490 @property
    491 def stat_null(self):
    492     """Fit statistic value for the null hypothesis"""
--> 493     self._check_quantity("stat_null")
    494     return self._data["stat_null"]

File ~/anaconda3/envs/gammapy-1.1/lib/python3.9/site-packages/gammapy/estimators/map/core.py:222, in FluxMaps._check_quantity(self, quantity)
    220 def _check_quantity(self, quantity):
    221     if quantity not in self.available_quantities:
--> 222         raise AttributeError(
    223             f"Quantity '{quantity}' is not defined on current flux estimate."
    224         )

AttributeError: Quantity 'stat_null' is not defined on current flux estimate.
```

Similarly for the PKS2155 lightcurve in gammapy-ata


**Other information**
This blocks the TS computation for #5007


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gammapy/estimators/parameter.py`
Content:
```
1 # Licensed under a 3-clause BSD style license - see LICENSE.rst
2 import logging
3 import numpy as np
4 from gammapy.datasets import Datasets
5 from gammapy.datasets.actors import DatasetsActor
6 from gammapy.modeling import Fit
7 from .core import Estimator
8 
9 log = logging.getLogger(__name__)
10 
11 
12 class ParameterEstimator(Estimator):
13     """Model parameter estimator.
14 
15     Estimates a model parameter for a group of datasets. Compute best fit value,
16     symmetric and delta(TS) for a given null value. Additionally asymmetric errors
17     as well as parameter upper limit and fit statistic profile can be estimated.
18 
19     Parameters
20     ----------
21     n_sigma : int
22         Sigma to use for asymmetric error computation. Default is 1.
23     n_sigma_ul : int
24         Sigma to use for upper limit computation. Default is 2.
25     null_value : float
26         Which null value to use for the parameter.
27     selection_optional : list of str, optional
28         Which additional quantities to estimate. Available options are:
29 
30             * "all": all the optional steps are executed.
31             * "errn-errp": estimate asymmetric errors on parameter best fit value.
32             * "ul": estimate upper limits.
33             * "scan": estimate fit statistic profiles.
34 
35         Default is None so the optional steps are not executed.
36     fit : `Fit`
37         Fit instance specifying the backend and fit options.
38     reoptimize : bool
39         Re-optimize other free model parameters. Default is True.
40     """
41 
42     tag = "ParameterEstimator"
43     _available_selection_optional = ["errn-errp", "ul", "scan"]
44 
45     def __init__(
46         self,
47         n_sigma=1,
48         n_sigma_ul=2,
49         null_value=1e-150,
50         selection_optional=None,
51         fit=None,
52         reoptimize=True,
53     ):
54         self.n_sigma = n_sigma
55         self.n_sigma_ul = n_sigma_ul
56         self.null_value = null_value
57         self.selection_optional = selection_optional
58 
59         if fit is None:
60             fit = Fit()
61 
62         self.fit = fit
63         self.reoptimize = reoptimize
64 
65     def estimate_best_fit(self, datasets, parameter):
66         """Estimate parameter asymmetric errors.
67 
68         Parameters
69         ----------
70         datasets : `~gammapy.datasets.Datasets`
71             Datasets.
72         parameter : `Parameter`
73             For which parameter to get the value.
74 
75         Returns
76         -------
77         result : dict
78             Dictionary with the various parameter estimation values. Entries are:
79 
80                 * parameter.name: best fit parameter value.
81                 * "stat": best fit total stat.
82                 * "success": boolean flag for fit success.
83                 * parameter.name_err: covariance-based error estimate on parameter value.
84         """
85         value, total_stat, success, error = np.nan, 0.0, False, np.nan
86 
87         if np.any(datasets.contributes_to_stat):
88             result = self.fit.run(datasets=datasets)
89             value, error = parameter.value, parameter.error
90             total_stat = result.optimize_result.total_stat
91             success = result.success
92 
93         return {
94             f"{parameter.name}": value,
95             "stat": total_stat,
96             "success": success,
97             f"{parameter.name}_err": error * self.n_sigma,
98         }
99 
100     def estimate_ts(self, datasets, parameter):
101         """Estimate parameter ts.
102 
103         Parameters
104         ----------
105         datasets : `~gammapy.datasets.Datasets`
106             Datasets.
107         parameter : `Parameter`
108             For which parameter to get the value.
109 
110         Returns
111         -------
112         result : dict
113             Dictionary with the test statistic of the best fit value compared to the null hypothesis. Entries are:
114 
115                 * "ts" : fit statistic difference with null hypothesis.
116                 * "npred" : predicted number of counts per dataset.
117         """
118         npred = self.estimate_npred(datasets=datasets)
119 
120         if not np.any(datasets.contributes_to_stat):
121             stat = np.nan
122             npred["npred"][...] = np.nan
123         else:
124             stat = datasets.stat_sum()
125 
126         with datasets.parameters.restore_status():
127             # compute ts value
128             parameter.value = self.null_value
129 
130             if self.reoptimize:
131                 parameter.frozen = True
132                 _ = self.fit.optimize(datasets=datasets)
133 
134             ts = datasets.stat_sum() - stat
135 
136         return {
137             "ts": ts,
138             "npred": npred["npred"],
139         }
140 
141     def estimate_errn_errp(self, datasets, parameter):
142         """Estimate parameter asymmetric errors.
143 
144         Parameters
145         ----------
146         datasets : `~gammapy.datasets.Datasets`
147             Datasets.
148         parameter : `Parameter`
149             For which parameter to get the value.
150 
151         Returns
152         -------
153         result : dict
154             Dictionary with the parameter asymmetric errors. Entries are:
155 
156                 * {parameter.name}_errp : positive error on parameter value.
157                 * {parameter.name}_errn : negative error on parameter value.
158         """
159         if not np.any(datasets.contributes_to_stat):
160             return {
161                 f"{parameter.name}_errp": np.nan,
162                 f"{parameter.name}_errn": np.nan,
163             }
164 
165         self.fit.optimize(datasets=datasets)
166 
167         res = self.fit.confidence(
168             datasets=datasets,
169             parameter=parameter,
170             sigma=self.n_sigma,
171             reoptimize=self.reoptimize,
172         )
173 
174         return {
175             f"{parameter.name}_errp": res["errp"],
176             f"{parameter.name}_errn": res["errn"],
177         }
178 
179     def estimate_scan(self, datasets, parameter):
180         """Estimate parameter statistic scan.
181 
182         Parameters
183         ----------
184         datasets : `~gammapy.datasets.Datasets`
185             The datasets used to estimate the model parameter.
186         parameter : `~gammapy.modeling.Parameter`
187             For which parameter to get the value.
188 
189         Returns
190         -------
191         result : dict
192             Dictionary with the parameter fit scan values. Entries are:
193 
194                 * parameter.name_scan : parameter values scan.
195                 * "stat_scan" : fit statistic values scan.
196         """
197         scan_values = parameter.scan_values
198 
199         if not np.any(datasets.contributes_to_stat):
200             return {
201                 f"{parameter.name}_scan": scan_values,
202                 "stat_scan": scan_values * np.nan,
203             }
204 
205         self.fit.optimize(datasets=datasets)
206 
207         profile = self.fit.stat_profile(
208             datasets=datasets, parameter=parameter, reoptimize=self.reoptimize
209         )
210 
211         return {
212             f"{parameter.name}_scan": scan_values,
213             "stat_scan": profile["stat_scan"],
214         }
215 
216     def estimate_ul(self, datasets, parameter):
217         """Estimate parameter ul.
218 
219         Parameters
220         ----------
221         datasets : `~gammapy.datasets.Datasets`
222             The datasets used to estimate the model parameter.
223         parameter : `~gammapy.modeling.Parameter`
224             For which parameter to get the value.
225 
226         Returns
227         -------
228         result : dict
229             Dictionary with the parameter upper limits. Entries are:
230 
231                 * parameter.name_ul : upper limit on parameter value.
232         """
233         if not np.any(datasets.contributes_to_stat):
234             return {f"{parameter.name}_ul": np.nan}
235 
236         self.fit.optimize(datasets=datasets)
237 
238         res = self.fit.confidence(
239             datasets=datasets,
240             parameter=parameter,
241             sigma=self.n_sigma_ul,
242             reoptimize=self.reoptimize,
243         )
244         return {f"{parameter.name}_ul": res["errp"] + parameter.value}
245 
246     @staticmethod
247     def estimate_counts(datasets):
248         """Estimate counts for the flux point.
249 
250         Parameters
251         ----------
252         datasets : Datasets
253             Datasets.
254 
255         Returns
256         -------
257         result : dict
258             Dictionary with an array with one entry per dataset with the sum of the
259             masked counts.
260         """
261         counts = []
262 
263         for dataset in datasets:
264             mask = dataset.mask
265             counts.append(dataset.counts.data[mask].sum())
266 
267         return {"counts": np.array(counts, dtype=int), "datasets": datasets.names}
268 
269     @staticmethod
270     def estimate_npred(datasets):
271         """Estimate npred for the flux point.
272 
273         Parameters
274         ----------
275         datasets : `~gammapy.datasets.Datasets`
276             Datasets.
277 
278         Returns
279         -------
280         result : dict
281             Dictionary with an array with one entry per dataset with the sum of the
282             masked npred.
283         """
284         npred = []
285 
286         for dataset in datasets:
287             mask = dataset.mask
288             npred.append(dataset.npred().data[mask].sum())
289 
290         return {"npred": np.array(npred), "datasets": datasets.names}
291 
292     def run(self, datasets, parameter):
293         """Run the parameter estimator.
294 
295         Parameters
296         ----------
297         datasets : `~gammapy.datasets.Datasets`
298             The datasets used to estimate the model parameter.
299         parameter : `str` or `~gammapy.modeling.Parameter`
300             For which parameter to run the estimator.
301 
302         Returns
303         -------
304         result : dict
305             Dictionary with the various parameter estimation values.
306         """
307         if not isinstance(datasets, DatasetsActor):
308             datasets = Datasets(datasets)
309         parameter = datasets.parameters[parameter]
310 
311         with datasets.parameters.restore_status():
312 
313             if not self.reoptimize:
314                 datasets.parameters.freeze_all()
315                 parameter.frozen = False
316 
317             result = self.estimate_best_fit(datasets, parameter)
318             result.update(self.estimate_ts(datasets, parameter))
319 
320             if "errn-errp" in self.selection_optional:
321                 result.update(self.estimate_errn_errp(datasets, parameter))
322 
323             if "ul" in self.selection_optional:
324                 result.update(self.estimate_ul(datasets, parameter))
325 
326             if "scan" in self.selection_optional:
327                 result.update(self.estimate_scan(datasets, parameter))
328 
329         result.update(self.estimate_counts(datasets))
330         return result
331
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gammapy/estimators/parameter.py b/gammapy/estimators/parameter.py
--- a/gammapy/estimators/parameter.py
+++ b/gammapy/estimators/parameter.py
@@ -114,6 +114,7 @@
 
                 * "ts" : fit statistic difference with null hypothesis.
                 * "npred" : predicted number of counts per dataset.
+                * "stat_null" : total stat corresponding to the null hypothesis
         """
         npred = self.estimate_npred(datasets=datasets)
 
@@ -132,11 +133,9 @@
                 _ = self.fit.optimize(datasets=datasets)
 
             ts = datasets.stat_sum() - stat
+            stat_null = datasets.stat_sum()
 
-        return {
-            "ts": ts,
-            "npred": npred["npred"],
-        }
+        return {"ts": ts, "npred": npred["npred"], "stat_null": stat_null}
 
     def estimate_errn_errp(self, datasets, parameter):
         """Estimate parameter asymmetric errors.

{"golden_diff": "diff --git a/gammapy/estimators/parameter.py b/gammapy/estimators/parameter.py\n--- a/gammapy/estimators/parameter.py\n+++ b/gammapy/estimators/parameter.py\n@@ -114,6 +114,7 @@\n \n                 * \"ts\" : fit statistic difference with null hypothesis.\n                 * \"npred\" : predicted number of counts per dataset.\n+                * \"stat_null\" : total stat corresponding to the null hypothesis\n         \"\"\"\n         npred = self.estimate_npred(datasets=datasets)\n \n@@ -132,11 +133,9 @@\n                 _ = self.fit.optimize(datasets=datasets)\n \n             ts = datasets.stat_sum() - stat\n+            stat_null = datasets.stat_sum()\n \n-        return {\n-            \"ts\": ts,\n-            \"npred\": npred[\"npred\"],\n-        }\n+        return {\"ts\": ts, \"npred\": npred[\"npred\"], \"stat_null\": stat_null}\n \n     def estimate_errn_errp(self, datasets, parameter):\n         \"\"\"Estimate parameter asymmetric errors.\n", "issue": "stat_null is not available on FluxPoints objects created with gammapy\n**Gammapy version**\r\npresent dev, 1.1\r\n\r\n\r\n**Bug description**\r\nFor `FluxPoints` created with gammapy estimators, the quantity `stat_null` is not available.\r\n\r\n\r\n**To Reproduce**\r\n1. Run the `spectral analysis notebook`. Then, accessing `flux_points.stat_null` gives an error\r\n```\r\nFile ~/anaconda3/envs/gammapy-1.1/lib/python3.9/site-packages/gammapy/estimators/map/core.py:493, in FluxMaps.stat_null(self)\r\n    490 @property\r\n    491 def stat_null(self):\r\n    492     \"\"\"Fit statistic value for the null hypothesis\"\"\"\r\n--> 493     self._check_quantity(\"stat_null\")\r\n    494     return self._data[\"stat_null\"]\r\n\r\nFile ~/anaconda3/envs/gammapy-1.1/lib/python3.9/site-packages/gammapy/estimators/map/core.py:222, in FluxMaps._check_quantity(self, quantity)\r\n    220 def _check_quantity(self, quantity):\r\n    221     if quantity not in self.available_quantities:\r\n--> 222         raise AttributeError(\r\n    223             f\"Quantity '{quantity}' is not defined on current flux estimate.\"\r\n    224         )\r\n\r\nAttributeError: Quantity 'stat_null' is not defined on current flux estimate.\r\n```\r\n\r\nSimilarly for the PKS2155 lightcurve in gammapy-ata\r\n\r\n\r\n**Other information**\r\nThis blocks the TS computation for #5007\r\n\n", "before_files": [{"content": "# Licensed under a 3-clause BSD style license - see LICENSE.rst\nimport logging\nimport numpy as np\nfrom gammapy.datasets import Datasets\nfrom gammapy.datasets.actors import DatasetsActor\nfrom gammapy.modeling import Fit\nfrom .core import Estimator\n\nlog = logging.getLogger(__name__)\n\n\nclass ParameterEstimator(Estimator):\n    \"\"\"Model parameter estimator.\n\n    Estimates a model parameter for a group of datasets. Compute best fit value,\n    symmetric and delta(TS) for a given null value. Additionally asymmetric errors\n    as well as parameter upper limit and fit statistic profile can be estimated.\n\n    Parameters\n    ----------\n    n_sigma : int\n        Sigma to use for asymmetric error computation. Default is 1.\n    n_sigma_ul : int\n        Sigma to use for upper limit computation. Default is 2.\n    null_value : float\n        Which null value to use for the parameter.\n    selection_optional : list of str, optional\n        Which additional quantities to estimate. Available options are:\n\n            * \"all\": all the optional steps are executed.\n            * \"errn-errp\": estimate asymmetric errors on parameter best fit value.\n            * \"ul\": estimate upper limits.\n            * \"scan\": estimate fit statistic profiles.\n\n        Default is None so the optional steps are not executed.\n    fit : `Fit`\n        Fit instance specifying the backend and fit options.\n    reoptimize : bool\n        Re-optimize other free model parameters. Default is True.\n    \"\"\"\n\n    tag = \"ParameterEstimator\"\n    _available_selection_optional = [\"errn-errp\", \"ul\", \"scan\"]\n\n    def __init__(\n        self,\n        n_sigma=1,\n        n_sigma_ul=2,\n        null_value=1e-150,\n        selection_optional=None,\n        fit=None,\n        reoptimize=True,\n    ):\n        self.n_sigma = n_sigma\n        self.n_sigma_ul = n_sigma_ul\n        self.null_value = null_value\n        self.selection_optional = selection_optional\n\n        if fit is None:\n            fit = Fit()\n\n        self.fit = fit\n        self.reoptimize = reoptimize\n\n    def estimate_best_fit(self, datasets, parameter):\n        \"\"\"Estimate parameter asymmetric errors.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            Datasets.\n        parameter : `Parameter`\n            For which parameter to get the value.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the various parameter estimation values. Entries are:\n\n                * parameter.name: best fit parameter value.\n                * \"stat\": best fit total stat.\n                * \"success\": boolean flag for fit success.\n                * parameter.name_err: covariance-based error estimate on parameter value.\n        \"\"\"\n        value, total_stat, success, error = np.nan, 0.0, False, np.nan\n\n        if np.any(datasets.contributes_to_stat):\n            result = self.fit.run(datasets=datasets)\n            value, error = parameter.value, parameter.error\n            total_stat = result.optimize_result.total_stat\n            success = result.success\n\n        return {\n            f\"{parameter.name}\": value,\n            \"stat\": total_stat,\n            \"success\": success,\n            f\"{parameter.name}_err\": error * self.n_sigma,\n        }\n\n    def estimate_ts(self, datasets, parameter):\n        \"\"\"Estimate parameter ts.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            Datasets.\n        parameter : `Parameter`\n            For which parameter to get the value.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the test statistic of the best fit value compared to the null hypothesis. Entries are:\n\n                * \"ts\" : fit statistic difference with null hypothesis.\n                * \"npred\" : predicted number of counts per dataset.\n        \"\"\"\n        npred = self.estimate_npred(datasets=datasets)\n\n        if not np.any(datasets.contributes_to_stat):\n            stat = np.nan\n            npred[\"npred\"][...] = np.nan\n        else:\n            stat = datasets.stat_sum()\n\n        with datasets.parameters.restore_status():\n            # compute ts value\n            parameter.value = self.null_value\n\n            if self.reoptimize:\n                parameter.frozen = True\n                _ = self.fit.optimize(datasets=datasets)\n\n            ts = datasets.stat_sum() - stat\n\n        return {\n            \"ts\": ts,\n            \"npred\": npred[\"npred\"],\n        }\n\n    def estimate_errn_errp(self, datasets, parameter):\n        \"\"\"Estimate parameter asymmetric errors.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            Datasets.\n        parameter : `Parameter`\n            For which parameter to get the value.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the parameter asymmetric errors. Entries are:\n\n                * {parameter.name}_errp : positive error on parameter value.\n                * {parameter.name}_errn : negative error on parameter value.\n        \"\"\"\n        if not np.any(datasets.contributes_to_stat):\n            return {\n                f\"{parameter.name}_errp\": np.nan,\n                f\"{parameter.name}_errn\": np.nan,\n            }\n\n        self.fit.optimize(datasets=datasets)\n\n        res = self.fit.confidence(\n            datasets=datasets,\n            parameter=parameter,\n            sigma=self.n_sigma,\n            reoptimize=self.reoptimize,\n        )\n\n        return {\n            f\"{parameter.name}_errp\": res[\"errp\"],\n            f\"{parameter.name}_errn\": res[\"errn\"],\n        }\n\n    def estimate_scan(self, datasets, parameter):\n        \"\"\"Estimate parameter statistic scan.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            The datasets used to estimate the model parameter.\n        parameter : `~gammapy.modeling.Parameter`\n            For which parameter to get the value.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the parameter fit scan values. Entries are:\n\n                * parameter.name_scan : parameter values scan.\n                * \"stat_scan\" : fit statistic values scan.\n        \"\"\"\n        scan_values = parameter.scan_values\n\n        if not np.any(datasets.contributes_to_stat):\n            return {\n                f\"{parameter.name}_scan\": scan_values,\n                \"stat_scan\": scan_values * np.nan,\n            }\n\n        self.fit.optimize(datasets=datasets)\n\n        profile = self.fit.stat_profile(\n            datasets=datasets, parameter=parameter, reoptimize=self.reoptimize\n        )\n\n        return {\n            f\"{parameter.name}_scan\": scan_values,\n            \"stat_scan\": profile[\"stat_scan\"],\n        }\n\n    def estimate_ul(self, datasets, parameter):\n        \"\"\"Estimate parameter ul.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            The datasets used to estimate the model parameter.\n        parameter : `~gammapy.modeling.Parameter`\n            For which parameter to get the value.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the parameter upper limits. Entries are:\n\n                * parameter.name_ul : upper limit on parameter value.\n        \"\"\"\n        if not np.any(datasets.contributes_to_stat):\n            return {f\"{parameter.name}_ul\": np.nan}\n\n        self.fit.optimize(datasets=datasets)\n\n        res = self.fit.confidence(\n            datasets=datasets,\n            parameter=parameter,\n            sigma=self.n_sigma_ul,\n            reoptimize=self.reoptimize,\n        )\n        return {f\"{parameter.name}_ul\": res[\"errp\"] + parameter.value}\n\n    @staticmethod\n    def estimate_counts(datasets):\n        \"\"\"Estimate counts for the flux point.\n\n        Parameters\n        ----------\n        datasets : Datasets\n            Datasets.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with an array with one entry per dataset with the sum of the\n            masked counts.\n        \"\"\"\n        counts = []\n\n        for dataset in datasets:\n            mask = dataset.mask\n            counts.append(dataset.counts.data[mask].sum())\n\n        return {\"counts\": np.array(counts, dtype=int), \"datasets\": datasets.names}\n\n    @staticmethod\n    def estimate_npred(datasets):\n        \"\"\"Estimate npred for the flux point.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            Datasets.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with an array with one entry per dataset with the sum of the\n            masked npred.\n        \"\"\"\n        npred = []\n\n        for dataset in datasets:\n            mask = dataset.mask\n            npred.append(dataset.npred().data[mask].sum())\n\n        return {\"npred\": np.array(npred), \"datasets\": datasets.names}\n\n    def run(self, datasets, parameter):\n        \"\"\"Run the parameter estimator.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            The datasets used to estimate the model parameter.\n        parameter : `str` or `~gammapy.modeling.Parameter`\n            For which parameter to run the estimator.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the various parameter estimation values.\n        \"\"\"\n        if not isinstance(datasets, DatasetsActor):\n            datasets = Datasets(datasets)\n        parameter = datasets.parameters[parameter]\n\n        with datasets.parameters.restore_status():\n\n            if not self.reoptimize:\n                datasets.parameters.freeze_all()\n                parameter.frozen = False\n\n            result = self.estimate_best_fit(datasets, parameter)\n            result.update(self.estimate_ts(datasets, parameter))\n\n            if \"errn-errp\" in self.selection_optional:\n                result.update(self.estimate_errn_errp(datasets, parameter))\n\n            if \"ul\" in self.selection_optional:\n                result.update(self.estimate_ul(datasets, parameter))\n\n            if \"scan\" in self.selection_optional:\n                result.update(self.estimate_scan(datasets, parameter))\n\n        result.update(self.estimate_counts(datasets))\n        return result\n", "path": "gammapy/estimators/parameter.py"}], "after_files": [{"content": "# Licensed under a 3-clause BSD style license - see LICENSE.rst\nimport logging\nimport numpy as np\nfrom gammapy.datasets import Datasets\nfrom gammapy.datasets.actors import DatasetsActor\nfrom gammapy.modeling import Fit\nfrom .core import Estimator\n\nlog = logging.getLogger(__name__)\n\n\nclass ParameterEstimator(Estimator):\n    \"\"\"Model parameter estimator.\n\n    Estimates a model parameter for a group of datasets. Compute best fit value,\n    symmetric and delta(TS) for a given null value. Additionally asymmetric errors\n    as well as parameter upper limit and fit statistic profile can be estimated.\n\n    Parameters\n    ----------\n    n_sigma : int\n        Sigma to use for asymmetric error computation. Default is 1.\n    n_sigma_ul : int\n        Sigma to use for upper limit computation. Default is 2.\n    null_value : float\n        Which null value to use for the parameter.\n    selection_optional : list of str, optional\n        Which additional quantities to estimate. Available options are:\n\n            * \"all\": all the optional steps are executed.\n            * \"errn-errp\": estimate asymmetric errors on parameter best fit value.\n            * \"ul\": estimate upper limits.\n            * \"scan\": estimate fit statistic profiles.\n\n        Default is None so the optional steps are not executed.\n    fit : `Fit`\n        Fit instance specifying the backend and fit options.\n    reoptimize : bool\n        Re-optimize other free model parameters. Default is True.\n    \"\"\"\n\n    tag = \"ParameterEstimator\"\n    _available_selection_optional = [\"errn-errp\", \"ul\", \"scan\"]\n\n    def __init__(\n        self,\n        n_sigma=1,\n        n_sigma_ul=2,\n        null_value=1e-150,\n        selection_optional=None,\n        fit=None,\n        reoptimize=True,\n    ):\n        self.n_sigma = n_sigma\n        self.n_sigma_ul = n_sigma_ul\n        self.null_value = null_value\n        self.selection_optional = selection_optional\n\n        if fit is None:\n            fit = Fit()\n\n        self.fit = fit\n        self.reoptimize = reoptimize\n\n    def estimate_best_fit(self, datasets, parameter):\n        \"\"\"Estimate parameter asymmetric errors.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            Datasets.\n        parameter : `Parameter`\n            For which parameter to get the value.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the various parameter estimation values. Entries are:\n\n                * parameter.name: best fit parameter value.\n                * \"stat\": best fit total stat.\n                * \"success\": boolean flag for fit success.\n                * parameter.name_err: covariance-based error estimate on parameter value.\n        \"\"\"\n        value, total_stat, success, error = np.nan, 0.0, False, np.nan\n\n        if np.any(datasets.contributes_to_stat):\n            result = self.fit.run(datasets=datasets)\n            value, error = parameter.value, parameter.error\n            total_stat = result.optimize_result.total_stat\n            success = result.success\n\n        return {\n            f\"{parameter.name}\": value,\n            \"stat\": total_stat,\n            \"success\": success,\n            f\"{parameter.name}_err\": error * self.n_sigma,\n        }\n\n    def estimate_ts(self, datasets, parameter):\n        \"\"\"Estimate parameter ts.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            Datasets.\n        parameter : `Parameter`\n            For which parameter to get the value.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the test statistic of the best fit value compared to the null hypothesis. Entries are:\n\n                * \"ts\" : fit statistic difference with null hypothesis.\n                * \"npred\" : predicted number of counts per dataset.\n                * \"stat_null\" : total stat corresponding to the null hypothesis\n        \"\"\"\n        npred = self.estimate_npred(datasets=datasets)\n\n        if not np.any(datasets.contributes_to_stat):\n            stat = np.nan\n            npred[\"npred\"][...] = np.nan\n        else:\n            stat = datasets.stat_sum()\n\n        with datasets.parameters.restore_status():\n            # compute ts value\n            parameter.value = self.null_value\n\n            if self.reoptimize:\n                parameter.frozen = True\n                _ = self.fit.optimize(datasets=datasets)\n\n            ts = datasets.stat_sum() - stat\n            stat_null = datasets.stat_sum()\n\n        return {\"ts\": ts, \"npred\": npred[\"npred\"], \"stat_null\": stat_null}\n\n    def estimate_errn_errp(self, datasets, parameter):\n        \"\"\"Estimate parameter asymmetric errors.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            Datasets.\n        parameter : `Parameter`\n            For which parameter to get the value.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the parameter asymmetric errors. Entries are:\n\n                * {parameter.name}_errp : positive error on parameter value.\n                * {parameter.name}_errn : negative error on parameter value.\n        \"\"\"\n        if not np.any(datasets.contributes_to_stat):\n            return {\n                f\"{parameter.name}_errp\": np.nan,\n                f\"{parameter.name}_errn\": np.nan,\n            }\n\n        self.fit.optimize(datasets=datasets)\n\n        res = self.fit.confidence(\n            datasets=datasets,\n            parameter=parameter,\n            sigma=self.n_sigma,\n            reoptimize=self.reoptimize,\n        )\n\n        return {\n            f\"{parameter.name}_errp\": res[\"errp\"],\n            f\"{parameter.name}_errn\": res[\"errn\"],\n        }\n\n    def estimate_scan(self, datasets, parameter):\n        \"\"\"Estimate parameter statistic scan.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            The datasets used to estimate the model parameter.\n        parameter : `~gammapy.modeling.Parameter`\n            For which parameter to get the value.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the parameter fit scan values. Entries are:\n\n                * parameter.name_scan : parameter values scan.\n                * \"stat_scan\" : fit statistic values scan.\n        \"\"\"\n        scan_values = parameter.scan_values\n\n        if not np.any(datasets.contributes_to_stat):\n            return {\n                f\"{parameter.name}_scan\": scan_values,\n                \"stat_scan\": scan_values * np.nan,\n            }\n\n        self.fit.optimize(datasets=datasets)\n\n        profile = self.fit.stat_profile(\n            datasets=datasets, parameter=parameter, reoptimize=self.reoptimize\n        )\n\n        return {\n            f\"{parameter.name}_scan\": scan_values,\n            \"stat_scan\": profile[\"stat_scan\"],\n        }\n\n    def estimate_ul(self, datasets, parameter):\n        \"\"\"Estimate parameter ul.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            The datasets used to estimate the model parameter.\n        parameter : `~gammapy.modeling.Parameter`\n            For which parameter to get the value.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the parameter upper limits. Entries are:\n\n                * parameter.name_ul : upper limit on parameter value.\n        \"\"\"\n        if not np.any(datasets.contributes_to_stat):\n            return {f\"{parameter.name}_ul\": np.nan}\n\n        self.fit.optimize(datasets=datasets)\n\n        res = self.fit.confidence(\n            datasets=datasets,\n            parameter=parameter,\n            sigma=self.n_sigma_ul,\n            reoptimize=self.reoptimize,\n        )\n        return {f\"{parameter.name}_ul\": res[\"errp\"] + parameter.value}\n\n    @staticmethod\n    def estimate_counts(datasets):\n        \"\"\"Estimate counts for the flux point.\n\n        Parameters\n        ----------\n        datasets : Datasets\n            Datasets.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with an array with one entry per dataset with the sum of the\n            masked counts.\n        \"\"\"\n        counts = []\n\n        for dataset in datasets:\n            mask = dataset.mask\n            counts.append(dataset.counts.data[mask].sum())\n\n        return {\"counts\": np.array(counts, dtype=int), \"datasets\": datasets.names}\n\n    @staticmethod\n    def estimate_npred(datasets):\n        \"\"\"Estimate npred for the flux point.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            Datasets.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with an array with one entry per dataset with the sum of the\n            masked npred.\n        \"\"\"\n        npred = []\n\n        for dataset in datasets:\n            mask = dataset.mask\n            npred.append(dataset.npred().data[mask].sum())\n\n        return {\"npred\": np.array(npred), \"datasets\": datasets.names}\n\n    def run(self, datasets, parameter):\n        \"\"\"Run the parameter estimator.\n\n        Parameters\n        ----------\n        datasets : `~gammapy.datasets.Datasets`\n            The datasets used to estimate the model parameter.\n        parameter : `str` or `~gammapy.modeling.Parameter`\n            For which parameter to run the estimator.\n\n        Returns\n        -------\n        result : dict\n            Dictionary with the various parameter estimation values.\n        \"\"\"\n        if not isinstance(datasets, DatasetsActor):\n            datasets = Datasets(datasets)\n        parameter = datasets.parameters[parameter]\n\n        with datasets.parameters.restore_status():\n\n            if not self.reoptimize:\n                datasets.parameters.freeze_all()\n                parameter.frozen = False\n\n            result = self.estimate_best_fit(datasets, parameter)\n            result.update(self.estimate_ts(datasets, parameter))\n\n            if \"errn-errp\" in self.selection_optional:\n                result.update(self.estimate_errn_errp(datasets, parameter))\n\n            if \"ul\" in self.selection_optional:\n                result.update(self.estimate_ul(datasets, parameter))\n\n            if \"scan\" in self.selection_optional:\n                result.update(self.estimate_scan(datasets, parameter))\n\n        result.update(self.estimate_counts(datasets))\n        return result\n", "path": "gammapy/estimators/parameter.py"}]}