Source code for gammapy_plugin.models.spectral
import logging
from typing import Union
import numpy as np
from astromodels.functions.function import Function
from gammapy.modeling.models import (
SpectralModel,
)
from gammapy.modeling.parameter import Parameter, Parameters
log = logging.getLogger(__name__)
[docs]
class SpectralModelConverted(SpectralModel):
"""Class for converting a spectral astromodel function into an gammapy
SpectralModel."""
tag = ["SpectralModelConverted", "spec_conv"]
def __init__(self, function: Union[Function, list], **kwargs) -> None:
self._components_parameters = None
if isinstance(function, Function):
self._astromodel_function = function
self._components = None
if hasattr(self._astromodel_function, "_requested_x_unit"):
# this is a composite function
self._x_unit = self._astromodel_function._requested_x_unit
self._y_unit = self._astromodel_function._requested_y_unit
else:
self._x_unit = self._astromodel_function.x_unit
self._y_unit = self._astromodel_function.y_unit
self._components_parameters = self._astromodel_function.parameters.values()
elif isinstance(function, list):
for f in function:
assert isinstance(
f, Function
), f"{f.name} is not a function but {type(f)}"
self._astromodel_function = function
self._components = len(function)
x_unit = None
y_unit = None
self._components_parameters = []
for f in self._astromodel_function:
if not hasattr(f, "_requested_x_unit"):
if x_unit is None and f.x_unit is not None:
x_unit = f.x_unit
elif x_unit is not None and f.x_unit is not None:
assert x_unit == f.x_unit, "Component x_unit not matching"
# TODO: maybe transform also possible need to check
else:
raise ValueError(f"Your Component {f.name} has no x_unit")
else:
x_unit = f._requested_x_unit
if not hasattr(f, "_requested_y_unit"):
if y_unit is None and f.y_unit is not None:
y_unit = f.y_unit
elif y_unit is not None and f.y_unit is not None:
assert y_unit == f.y_unit, "Component y_unit not matching"
# TODO maybe transform also possible need to check
else: # pragma: no cover
raise ValueError(f"Your Component {f.name} has no y_unit")
else:
y_unit = f._requested_y_unit
for p in f.parameters.values():
self._components_parameters.append(p)
self._x_unit = x_unit
self._y_unit = y_unit
else:
raise NotImplementedError(
"Can only convert astromodels Function or list of Functions"
)
if self._x_unit is None or self._y_unit is None:
raise ValueError("You need to specify units for your spectral component")
log.debug(f"These are the units: {self._x_unit}, {self._y_unit}")
self._setup_parameters()
self._integral_unit = self._y_unit * self._x_unit
super().__init__()
def _setup_parameters(self):
"""Setup the parameters by creating gammapy Parameters and setting them
as attributes to this class."""
paras = []
# needed later for correctly evaluating the function
self._mapping = {}
self._mapping_free = {}
parameter_dict = self._components_parameters
for v in parameter_dict:
vmin = np.nan
vmax = np.nan
if v.min_value is not None:
vmin = v.min_value
if v.max_value is not None:
vmax = v.max_value
self._mapping[v.path] = v.path
if v.free:
self._mapping_free[v.path] = v.path
paras.append(
Parameter(
name=v.path,
value=v.value,
unit=v.unit,
min=vmin,
max=vmax,
frozen=not bool(v.free),
)
)
setattr(self, v.path, paras[-1])
self.default_parameters = Parameters(paras)
[docs]
def evaluate(self, energy, **kwargs):
"""Evaluates the astromodels function instead of a gammapy one."""
shape = None
if len(energy.shape) > 1:
shape = energy.shape
energy = energy.flatten()
if self._components is not None:
vals = []
if shape is None:
for i in range(self._components):
val = self._astromodel_function[i](energy)
vals.append(val)
else:
for i in range(self._components):
vals.append(self._astromodel_function[i](energy).reshape(shape))
return sum(vals)
else:
if shape is None:
return self._astromodel_function(energy)
else:
return self._astromodel_function(energy).reshape(shape)
@property
def mapping(self):
return self._mapping
@property
def mapping_free(self):
return self._mapping_free