Repository: gammapy/gammapy

Issue:
Allow `MapAxis` to be passed in `SpectralModel.plot()` ?
Currently in the `SpectralModel.plot()` we allow the user to define the `energy_bounds`. (This would also apply to the `plot_error` function)
Should we also allow them to pass a `MapAxis`? 
This relates to discussions here: https://github.com/gammapy/gammapy/issues/5086#issuecomment-1935949104 and https://github.com/gammapy/gammapy/issues/3078#issuecomment-715320223. 


**Describe the solution you'd like**
If we wish to allow this there will be an API change. 
Both `energy_bounds` and `map_axis` would need to be set to `None` inside the function:
` def plot(self, energy_bounds=None, map_axis=None......)`

Then the user would defined either the `energy_bounds` or `map_axis`. I am not sure on this solution however because I do not think it makes sense to have them set to None, this would be confusing to the user and they may think the function can be run without defining either. 

Open to further discussion on whether we wish to allow the functionality to pass a `MapAxis`, and how we could implement that.


Discussion:
> I do not think it makes sense to have them set to None, this would be confusing to the user

I don't think this is a major issue. You can mention in the docstring that either of the 2 need to be specified, and `map_axis` takes preference if both are present.  And then also raise an error if both are None

Resolving patch:
diff --git a/gammapy/modeling/models/spectral.py b/gammapy/modeling/models/spectral.py
--- a/gammapy/modeling/models/spectral.py
+++ b/gammapy/modeling/models/spectral.py
@@ -11,6 +11,7 @@
 import astropy.units as u
 from astropy import constants as const
 from astropy.table import Table
+from astropy.units import Quantity
 from astropy.utils.decorators import classproperty
 from astropy.visualization import quantity_support
 import matplotlib.pyplot as plt
@@ -426,8 +427,9 @@ def plot(
 
         Parameters
         ----------
-        energy_bounds : `~astropy.units.Quantity`
-            Plot energy bounds passed to MapAxis.from_energy_bounds.
+        energy_bounds : `~astropy.units.Quantity`, list of `~astropy.units.Quantity` or `~gammapy.maps.MapAxis`
+            Energy bounds between which the model is to be plotted. Or an
+            axis defining the energy bounds between which the model is to be plotted.
         ax : `~matplotlib.axes.Axes`, optional
             Matplotlib axes. Default is None.
         sed_type : {"dnde", "flux", "eflux", "e2dnde"}
@@ -443,20 +445,28 @@ def plot(
         -------
         ax : `~matplotlib.axes.Axes`, optional
             Matplotlib axes.
+
+        Notes
+        -----
+        If ``energy_bounds`` is supplied as a list, tuple, or Quantity, an ``energy_axis`` is created internally with
+        ``n_points`` bins between the given bounds.
         """
         from gammapy.estimators.map.core import DEFAULT_UNIT
 
-        ax = plt.gca() if ax is None else ax
-
         if self.is_norm_spectral_model:
             sed_type = "norm"
 
-        energy_min, energy_max = energy_bounds
-        energy = MapAxis.from_energy_bounds(
-            energy_min,
-            energy_max,
-            n_points,
-        )
+        if isinstance(energy_bounds, (tuple, list, Quantity)):
+            energy_min, energy_max = energy_bounds
+            energy = MapAxis.from_energy_bounds(
+                energy_min,
+                energy_max,
+                n_points,
+            )
+        elif isinstance(energy_bounds, MapAxis):
+            energy = energy_bounds
+
+        ax = plt.gca() if ax is None else ax
 
         if ax.yaxis.units is None:
             ax.yaxis.set_units(DEFAULT_UNIT[sed_type] * energy.unit**energy_power)
@@ -498,8 +508,9 @@ def plot_error(
 
         Parameters
         ----------
-        energy_bounds : `~astropy.units.Quantity`
-            Plot energy bounds passed to `~gammapy.maps.MapAxis.from_energy_bounds`.
+        energy_bounds : `~astropy.units.Quantity`, list of `~astropy.units.Quantity` or `~gammapy.maps.MapAxis`
+            Energy bounds between which the model is to be plotted. Or an
+            axis defining the energy bounds between which the model is to be plotted.
         ax : `~matplotlib.axes.Axes`, optional
             Matplotlib axes. Default is None.
         sed_type : {"dnde", "flux", "eflux", "e2dnde"}
@@ -515,20 +526,28 @@ def plot_error(
         -------
         ax : `~matplotlib.axes.Axes`, optional
             Matplotlib axes.
+
+        Notes
+        -----
+        If ``energy_bounds`` is supplied as a list, tuple, or Quantity, an ``energy_axis`` is created internally with
+        ``n_points`` bins between the given bounds.
         """
         from gammapy.estimators.map.core import DEFAULT_UNIT
 
-        ax = plt.gca() if ax is None else ax
-
         if self.is_norm_spectral_model:
             sed_type = "norm"
 
-        energy_min, energy_max = energy_bounds
-        energy = MapAxis.from_energy_bounds(
-            energy_min,
-            energy_max,
-            n_points,
-        )
+        if isinstance(energy_bounds, (tuple, list, Quantity)):
+            energy_min, energy_max = energy_bounds
+            energy = MapAxis.from_energy_bounds(
+                energy_min,
+                energy_max,
+                n_points,
+            )
+        elif isinstance(energy_bounds, MapAxis):
+            energy = energy_bounds
+
+        ax = plt.gca() if ax is None else ax
 
         kwargs.setdefault("facecolor", "black")
         kwargs.setdefault("alpha", 0.2)


Test patch:
diff --git a/gammapy/modeling/models/tests/test_spectral.py b/gammapy/modeling/models/tests/test_spectral.py
--- a/gammapy/modeling/models/tests/test_spectral.py
+++ b/gammapy/modeling/models/tests/test_spectral.py
@@ -431,6 +431,7 @@ def test_model_plot():
         amplitude=1e-12 * u.Unit("TeV-1 cm-2 s-1"), reference=1 * u.Unit("TeV"), index=2
     )
     pwl.amplitude.error = 0.1e-12 * u.Unit("TeV-1 cm-2 s-1")
+    energy_axis = MapAxis.from_energy_bounds(1 * u.TeV, 10 * u.TeV, 100)
 
     with mpl_plot_check():
         pwl.plot((1 * u.TeV, 10 * u.TeV))
@@ -438,6 +439,24 @@ def test_model_plot():
     with mpl_plot_check():
         pwl.plot_error((1 * u.TeV, 10 * u.TeV))
 
+    with mpl_plot_check():
+        pwl.plot([0.08, 20] * u.TeV)
+
+    with mpl_plot_check():
+        pwl.plot_error([0.08, 20] * u.TeV)
+
+    with mpl_plot_check():
+        pwl.plot([0.08 * u.TeV, 20 * u.TeV])
+
+    with mpl_plot_check():
+        pwl.plot_error([0.08 * u.TeV, 20 * u.TeV])
+
+    with mpl_plot_check():
+        pwl.plot(energy_bounds=energy_axis)
+
+    with mpl_plot_check():
+        pwl.plot_error(energy_bounds=energy_axis)
+
 
 def test_model_plot_sed_type():
     pwl = PowerLawSpectralModel(