We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
FluxEstimator._set_norm_parameter crashes
**Gammapy version**
0.19

**Bug description**
Any code passing trough the "scan" branch of `FluxPointsEstimator._nan_result()` will crash due the invocation of `_set_norm_parameter` without the argument `scaled_parameter`.

**Expected behavior**
Either `_nan_result` passing the correct arguments, or `_set_norm_parameter` properly handling its default arguments.


--- 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/flux.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.estimators.parameter import ParameterEstimator
6 from gammapy.maps import Map, MapAxis
7 from gammapy.modeling import Parameter
8 from gammapy.modeling.models import ScaleSpectralModel
9 
10 log = logging.getLogger(__name__)
11 
12 
13 class FluxEstimator(ParameterEstimator):
14     """Flux estimator.
15 
16     Estimates flux for a given list of datasets with their model in a given energy range.
17 
18     To estimate the model flux the amplitude of the reference spectral model is
19     fitted within the energy range. The amplitude is re-normalized using the "norm" parameter,
20     which specifies the deviation of the flux from the reference model in this
21     energy range.
22 
23     Parameters
24     ----------
25     source : str or int
26         For which source in the model to compute the flux.
27     norm_min : float
28         Minimum value for the norm used for the fit statistic profile evaluation.
29     norm_max : float
30         Maximum value for the norm used for the fit statistic profile evaluation.
31     norm_n_values : int
32         Number of norm values used for the fit statistic profile.
33     norm_values : `numpy.ndarray`
34         Array of norm values to be used for the fit statistic profile.
35     n_sigma : int
36         Sigma to use for asymmetric error computation.
37     n_sigma_ul : int
38         Sigma to use for upper limit computation.
39     selection_optional : list of str
40         Which additional quantities to estimate. Available options are:
41 
42             * "all": all the optional steps are executed
43             * "errn-errp": estimate asymmetric errors.
44             * "ul": estimate upper limits.
45             * "scan": estimate fit statistic profiles.
46 
47         Default is None so the optional steps are not executed.
48     fit : `Fit`
49         Fit instance specifying the backend and fit options.
50     reoptimize : bool
51         Re-optimize other free model parameters. Default is False.
52     """
53 
54     tag = "FluxEstimator"
55 
56     def __init__(
57         self,
58         source=0,
59         norm_min=0.2,
60         norm_max=5,
61         norm_n_values=11,
62         norm_values=None,
63         n_sigma=1,
64         n_sigma_ul=2,
65         selection_optional=None,
66         fit=None,
67         reoptimize=False,
68     ):
69         self.norm_values = norm_values
70         self.norm_min = norm_min
71         self.norm_max = norm_max
72         self.norm_n_values = norm_n_values
73         self.source = source
74         super().__init__(
75             null_value=0,
76             n_sigma=n_sigma,
77             n_sigma_ul=n_sigma_ul,
78             selection_optional=selection_optional,
79             fit=fit,
80             reoptimize=reoptimize,
81         )
82 
83     def _set_norm_parameter(self, norm=None, scaled_parameter=None):
84         """Define properties of the norm spectral parameter."""
85         if norm is None:
86             norm = Parameter("norm", 1, unit="", interp="log")
87 
88         norm.value = 1.0
89         norm.frozen = False
90 
91         norm.min = scaled_parameter.min / scaled_parameter.value
92         norm.max = scaled_parameter.max / scaled_parameter.value
93         norm.interp = scaled_parameter.interp
94         norm.scan_values = self.norm_values
95         norm.scan_min = self.norm_min
96         norm.scan_max = self.norm_max
97         norm.scan_n_values = self.norm_n_values
98         return norm
99 
100     def get_scale_model(self, models):
101         """Set scale model
102 
103         Parameters
104         ----------
105         models : `Models`
106             Models
107 
108         Returns
109         -------
110         model : `ScaleSpectralModel`
111             Scale spectral model
112         """
113         ref_model = models[self.source].spectral_model
114         scale_model = ScaleSpectralModel(ref_model)
115 
116         if "amplitude" in ref_model.parameters.names:
117             scaled_parameter = ref_model.parameters["amplitude"]
118         else:
119             scaled_parameter = ref_model.parameters["norm"]
120 
121         scale_model.norm = self._set_norm_parameter(scale_model.norm, scaled_parameter)
122         return scale_model
123 
124     def estimate_npred_excess(self, datasets):
125         """Estimate npred excess for the source.
126 
127         Parameters
128         ----------
129         datasets : Datasets
130             Datasets
131 
132         Returns
133         -------
134         result : dict
135             Dict with an array with one entry per dataset with the sum of the
136             masked npred excess.
137         """
138         npred_excess = []
139 
140         for dataset in datasets:
141             name = datasets.models[self.source].name
142             npred_signal = dataset.npred_signal(model_name=name)
143             npred = Map.from_geom(dataset.mask.geom)
144             npred.stack(npred_signal)
145             npred_excess.append(npred.data[dataset.mask].sum())
146 
147         return {"npred_excess": np.array(npred_excess), "datasets": datasets.names}
148 
149     def run(self, datasets):
150         """Estimate flux for a given energy range.
151 
152         Parameters
153         ----------
154         datasets : list of `~gammapy.datasets.SpectrumDataset`
155             Spectrum datasets.
156 
157         Returns
158         -------
159         result : dict
160             Dict with results for the flux point.
161         """
162         datasets = Datasets(datasets)
163         models = datasets.models.copy()
164 
165         model = self.get_scale_model(models)
166 
167         energy_min, energy_max = datasets.energy_ranges
168         energy_axis = MapAxis.from_energy_edges([energy_min.min(), energy_max.max()])
169 
170         with np.errstate(invalid="ignore", divide="ignore"):
171             result = model.reference_fluxes(energy_axis=energy_axis)
172             # convert to scalar values
173             result = {key: value.item() for key, value in result.items()}
174 
175         models[self.source].spectral_model = model
176         datasets.models = models
177         result.update(super().run(datasets, model.norm))
178 
179         # TODO: find a cleaner way of including the npred_excess info
180         datasets.models[self.source].spectral_model.norm.value = result["norm"]
181         result.update(self.estimate_npred_excess(datasets=datasets))
182         return result
183
```
--- 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/flux.py b/gammapy/estimators/flux.py
--- a/gammapy/estimators/flux.py
+++ b/gammapy/estimators/flux.py
@@ -113,10 +113,12 @@
         ref_model = models[self.source].spectral_model
         scale_model = ScaleSpectralModel(ref_model)
 
-        if "amplitude" in ref_model.parameters.names:
-            scaled_parameter = ref_model.parameters["amplitude"]
+        for scaled_parameter in ref_model.parameters:
+            if scaled_parameter.is_norm:
+                break
         else:
-            scaled_parameter = ref_model.parameters["norm"]
+            raise ValueError(f"{self.tag} requires a 'norm' or 'amplitude' parameter"
+                             " in the model to run")
 
         scale_model.norm = self._set_norm_parameter(scale_model.norm, scaled_parameter)
         return scale_model

{"golden_diff": "diff --git a/gammapy/estimators/flux.py b/gammapy/estimators/flux.py\n--- a/gammapy/estimators/flux.py\n+++ b/gammapy/estimators/flux.py\n@@ -113,10 +113,12 @@\n         ref_model = models[self.source].spectral_model\n         scale_model = ScaleSpectralModel(ref_model)\n \n-        if \"amplitude\" in ref_model.parameters.names:\n-            scaled_parameter = ref_model.parameters[\"amplitude\"]\n+        for scaled_parameter in ref_model.parameters:\n+            if scaled_parameter.is_norm:\n+                break\n         else:\n-            scaled_parameter = ref_model.parameters[\"norm\"]\n+            raise ValueError(f\"{self.tag} requires a 'norm' or 'amplitude' parameter\"\n+                             \" in the model to run\")\n \n         scale_model.norm = self._set_norm_parameter(scale_model.norm, scaled_parameter)\n         return scale_model\n", "issue": "FluxEstimator._set_norm_parameter crashes\n**Gammapy version**\r\n0.19\r\n\r\n**Bug description**\r\nAny code passing trough the \"scan\" branch of `FluxPointsEstimator._nan_result()` will crash due the invocation of `_set_norm_parameter` without the argument `scaled_parameter`.\r\n\r\n**Expected behavior**\r\nEither `_nan_result` passing the correct arguments, or `_set_norm_parameter` properly handling its default arguments.\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.estimators.parameter import ParameterEstimator\nfrom gammapy.maps import Map, MapAxis\nfrom gammapy.modeling import Parameter\nfrom gammapy.modeling.models import ScaleSpectralModel\n\nlog = logging.getLogger(__name__)\n\n\nclass FluxEstimator(ParameterEstimator):\n    \"\"\"Flux estimator.\n\n    Estimates flux for a given list of datasets with their model in a given energy range.\n\n    To estimate the model flux the amplitude of the reference spectral model is\n    fitted within the energy range. The amplitude is re-normalized using the \"norm\" parameter,\n    which specifies the deviation of the flux from the reference model in this\n    energy range.\n\n    Parameters\n    ----------\n    source : str or int\n        For which source in the model to compute the flux.\n    norm_min : float\n        Minimum value for the norm used for the fit statistic profile evaluation.\n    norm_max : float\n        Maximum value for the norm used for the fit statistic profile evaluation.\n    norm_n_values : int\n        Number of norm values used for the fit statistic profile.\n    norm_values : `numpy.ndarray`\n        Array of norm values to be used for the fit statistic profile.\n    n_sigma : int\n        Sigma to use for asymmetric error computation.\n    n_sigma_ul : int\n        Sigma to use for upper limit computation.\n    selection_optional : list of str\n        Which additional quantities to estimate. Available options are:\n\n            * \"all\": all the optional steps are executed\n            * \"errn-errp\": estimate asymmetric errors.\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 False.\n    \"\"\"\n\n    tag = \"FluxEstimator\"\n\n    def __init__(\n        self,\n        source=0,\n        norm_min=0.2,\n        norm_max=5,\n        norm_n_values=11,\n        norm_values=None,\n        n_sigma=1,\n        n_sigma_ul=2,\n        selection_optional=None,\n        fit=None,\n        reoptimize=False,\n    ):\n        self.norm_values = norm_values\n        self.norm_min = norm_min\n        self.norm_max = norm_max\n        self.norm_n_values = norm_n_values\n        self.source = source\n        super().__init__(\n            null_value=0,\n            n_sigma=n_sigma,\n            n_sigma_ul=n_sigma_ul,\n            selection_optional=selection_optional,\n            fit=fit,\n            reoptimize=reoptimize,\n        )\n\n    def _set_norm_parameter(self, norm=None, scaled_parameter=None):\n        \"\"\"Define properties of the norm spectral parameter.\"\"\"\n        if norm is None:\n            norm = Parameter(\"norm\", 1, unit=\"\", interp=\"log\")\n\n        norm.value = 1.0\n        norm.frozen = False\n\n        norm.min = scaled_parameter.min / scaled_parameter.value\n        norm.max = scaled_parameter.max / scaled_parameter.value\n        norm.interp = scaled_parameter.interp\n        norm.scan_values = self.norm_values\n        norm.scan_min = self.norm_min\n        norm.scan_max = self.norm_max\n        norm.scan_n_values = self.norm_n_values\n        return norm\n\n    def get_scale_model(self, models):\n        \"\"\"Set scale model\n\n        Parameters\n        ----------\n        models : `Models`\n            Models\n\n        Returns\n        -------\n        model : `ScaleSpectralModel`\n            Scale spectral model\n        \"\"\"\n        ref_model = models[self.source].spectral_model\n        scale_model = ScaleSpectralModel(ref_model)\n\n        if \"amplitude\" in ref_model.parameters.names:\n            scaled_parameter = ref_model.parameters[\"amplitude\"]\n        else:\n            scaled_parameter = ref_model.parameters[\"norm\"]\n\n        scale_model.norm = self._set_norm_parameter(scale_model.norm, scaled_parameter)\n        return scale_model\n\n    def estimate_npred_excess(self, datasets):\n        \"\"\"Estimate npred excess for the source.\n\n        Parameters\n        ----------\n        datasets : Datasets\n            Datasets\n\n        Returns\n        -------\n        result : dict\n            Dict with an array with one entry per dataset with the sum of the\n            masked npred excess.\n        \"\"\"\n        npred_excess = []\n\n        for dataset in datasets:\n            name = datasets.models[self.source].name\n            npred_signal = dataset.npred_signal(model_name=name)\n            npred = Map.from_geom(dataset.mask.geom)\n            npred.stack(npred_signal)\n            npred_excess.append(npred.data[dataset.mask].sum())\n\n        return {\"npred_excess\": np.array(npred_excess), \"datasets\": datasets.names}\n\n    def run(self, datasets):\n        \"\"\"Estimate flux for a given energy range.\n\n        Parameters\n        ----------\n        datasets : list of `~gammapy.datasets.SpectrumDataset`\n            Spectrum datasets.\n\n        Returns\n        -------\n        result : dict\n            Dict with results for the flux point.\n        \"\"\"\n        datasets = Datasets(datasets)\n        models = datasets.models.copy()\n\n        model = self.get_scale_model(models)\n\n        energy_min, energy_max = datasets.energy_ranges\n        energy_axis = MapAxis.from_energy_edges([energy_min.min(), energy_max.max()])\n\n        with np.errstate(invalid=\"ignore\", divide=\"ignore\"):\n            result = model.reference_fluxes(energy_axis=energy_axis)\n            # convert to scalar values\n            result = {key: value.item() for key, value in result.items()}\n\n        models[self.source].spectral_model = model\n        datasets.models = models\n        result.update(super().run(datasets, model.norm))\n\n        # TODO: find a cleaner way of including the npred_excess info\n        datasets.models[self.source].spectral_model.norm.value = result[\"norm\"]\n        result.update(self.estimate_npred_excess(datasets=datasets))\n        return result\n", "path": "gammapy/estimators/flux.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.estimators.parameter import ParameterEstimator\nfrom gammapy.maps import Map, MapAxis\nfrom gammapy.modeling import Parameter\nfrom gammapy.modeling.models import ScaleSpectralModel\n\nlog = logging.getLogger(__name__)\n\n\nclass FluxEstimator(ParameterEstimator):\n    \"\"\"Flux estimator.\n\n    Estimates flux for a given list of datasets with their model in a given energy range.\n\n    To estimate the model flux the amplitude of the reference spectral model is\n    fitted within the energy range. The amplitude is re-normalized using the \"norm\" parameter,\n    which specifies the deviation of the flux from the reference model in this\n    energy range.\n\n    Parameters\n    ----------\n    source : str or int\n        For which source in the model to compute the flux.\n    norm_min : float\n        Minimum value for the norm used for the fit statistic profile evaluation.\n    norm_max : float\n        Maximum value for the norm used for the fit statistic profile evaluation.\n    norm_n_values : int\n        Number of norm values used for the fit statistic profile.\n    norm_values : `numpy.ndarray`\n        Array of norm values to be used for the fit statistic profile.\n    n_sigma : int\n        Sigma to use for asymmetric error computation.\n    n_sigma_ul : int\n        Sigma to use for upper limit computation.\n    selection_optional : list of str\n        Which additional quantities to estimate. Available options are:\n\n            * \"all\": all the optional steps are executed\n            * \"errn-errp\": estimate asymmetric errors.\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 False.\n    \"\"\"\n\n    tag = \"FluxEstimator\"\n\n    def __init__(\n        self,\n        source=0,\n        norm_min=0.2,\n        norm_max=5,\n        norm_n_values=11,\n        norm_values=None,\n        n_sigma=1,\n        n_sigma_ul=2,\n        selection_optional=None,\n        fit=None,\n        reoptimize=False,\n    ):\n        self.norm_values = norm_values\n        self.norm_min = norm_min\n        self.norm_max = norm_max\n        self.norm_n_values = norm_n_values\n        self.source = source\n        super().__init__(\n            null_value=0,\n            n_sigma=n_sigma,\n            n_sigma_ul=n_sigma_ul,\n            selection_optional=selection_optional,\n            fit=fit,\n            reoptimize=reoptimize,\n        )\n\n    def _set_norm_parameter(self, norm=None, scaled_parameter=None):\n        \"\"\"Define properties of the norm spectral parameter.\"\"\"\n        if norm is None:\n            norm = Parameter(\"norm\", 1, unit=\"\", interp=\"log\")\n\n        norm.value = 1.0\n        norm.frozen = False\n\n        norm.min = scaled_parameter.min / scaled_parameter.value\n        norm.max = scaled_parameter.max / scaled_parameter.value\n        norm.interp = scaled_parameter.interp\n        norm.scan_values = self.norm_values\n        norm.scan_min = self.norm_min\n        norm.scan_max = self.norm_max\n        norm.scan_n_values = self.norm_n_values\n        return norm\n\n    def get_scale_model(self, models):\n        \"\"\"Set scale model\n\n        Parameters\n        ----------\n        models : `Models`\n            Models\n\n        Returns\n        -------\n        model : `ScaleSpectralModel`\n            Scale spectral model\n        \"\"\"\n        ref_model = models[self.source].spectral_model\n        scale_model = ScaleSpectralModel(ref_model)\n\n        for scaled_parameter in ref_model.parameters:\n            if scaled_parameter.is_norm:\n                break\n        else:\n            raise ValueError(f\"{self.tag} requires a 'norm' or 'amplitude' parameter\"\n                             \" in the model to run\")\n\n        scale_model.norm = self._set_norm_parameter(scale_model.norm, scaled_parameter)\n        return scale_model\n\n    def estimate_npred_excess(self, datasets):\n        \"\"\"Estimate npred excess for the source.\n\n        Parameters\n        ----------\n        datasets : Datasets\n            Datasets\n\n        Returns\n        -------\n        result : dict\n            Dict with an array with one entry per dataset with the sum of the\n            masked npred excess.\n        \"\"\"\n        npred_excess = []\n\n        for dataset in datasets:\n            name = datasets.models[self.source].name\n            npred_signal = dataset.npred_signal(model_name=name)\n            npred = Map.from_geom(dataset.mask.geom)\n            npred.stack(npred_signal)\n            npred_excess.append(npred.data[dataset.mask].sum())\n\n        return {\"npred_excess\": np.array(npred_excess), \"datasets\": datasets.names}\n\n    def run(self, datasets):\n        \"\"\"Estimate flux for a given energy range.\n\n        Parameters\n        ----------\n        datasets : list of `~gammapy.datasets.SpectrumDataset`\n            Spectrum datasets.\n\n        Returns\n        -------\n        result : dict\n            Dict with results for the flux point.\n        \"\"\"\n        datasets = Datasets(datasets)\n        models = datasets.models.copy()\n\n        model = self.get_scale_model(models)\n\n        energy_min, energy_max = datasets.energy_ranges\n        energy_axis = MapAxis.from_energy_edges([energy_min.min(), energy_max.max()])\n\n        with np.errstate(invalid=\"ignore\", divide=\"ignore\"):\n            result = model.reference_fluxes(energy_axis=energy_axis)\n            # convert to scalar values\n            result = {key: value.item() for key, value in result.items()}\n\n        models[self.source].spectral_model = model\n        datasets.models = models\n        result.update(super().run(datasets, model.norm))\n\n        # TODO: find a cleaner way of including the npred_excess info\n        datasets.models[self.source].spectral_model.norm.value = result[\"norm\"]\n        result.update(self.estimate_npred_excess(datasets=datasets))\n        return result\n", "path": "gammapy/estimators/flux.py"}]}