gammapy-plugin 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gammapy_plugin/__init__.py +5 -0
- gammapy_plugin/_version.py +24 -0
- gammapy_plugin/converter.py +276 -0
- gammapy_plugin/gammapy_like.py +325 -0
- gammapy_plugin/io/__init__.py +0 -0
- gammapy_plugin/io/plot_model.py +89 -0
- gammapy_plugin/models/__init__.py +8 -0
- gammapy_plugin/models/spatial.py +153 -0
- gammapy_plugin/models/spectral.py +184 -0
- gammapy_plugin/test/conftest.py +224 -0
- gammapy_plugin/test/test_GammapyLike.py +114 -0
- gammapy_plugin/test/test_astromodels_converter.py +79 -0
- gammapy_plugin/test/test_astromodels_functions.py +59 -0
- gammapy_plugin/test/test_astromodels_numba_functions.py +26 -0
- gammapy_plugin/test/test_extended-source.py +230 -0
- gammapy_plugin/test/test_gammapy_converter.py +9 -0
- gammapy_plugin/test/test_gammapy_parser.py +53 -0
- gammapy_plugin/test/test_models.py +69 -0
- gammapy_plugin/test/test_point-source.py +118 -0
- gammapy_plugin/test/test_validation.py +91 -0
- gammapy_plugin/test/utils.py +54 -0
- gammapy_plugin/utils/__init__.py +0 -0
- gammapy_plugin/utils/astromodels_functions.py +118 -0
- gammapy_plugin/utils/gammapy_parser.py +90 -0
- gammapy_plugin/utils/package_data.py +28 -0
- gammapy_plugin-0.0.1.dist-info/METADATA +122 -0
- gammapy_plugin-0.0.1.dist-info/RECORD +30 -0
- gammapy_plugin-0.0.1.dist-info/WHEEL +5 -0
- gammapy_plugin-0.0.1.dist-info/licenses/LICENSE +674 -0
- gammapy_plugin-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.0.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 0, 1)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = 'g71c123a'
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from astromodels.core.model import Model
|
|
4
|
+
from astromodels.sources import ExtendedSource, PointSource, Source
|
|
5
|
+
from gammapy.modeling.models import SkyModel
|
|
6
|
+
|
|
7
|
+
from gammapy_plugin.models import (
|
|
8
|
+
PointSourceModelConverted,
|
|
9
|
+
SpatialModelConverted,
|
|
10
|
+
SpectralModelConverted,
|
|
11
|
+
)
|
|
12
|
+
from gammapy_plugin.utils.gammapy_parser import (
|
|
13
|
+
parameter_to_gammapy_dict,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = ["AstromodelConverter", "SourceConverter"]
|
|
17
|
+
|
|
18
|
+
log = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AstromodelConverter:
|
|
22
|
+
"""Class for analyizing an astromodel model and converting all the
|
|
23
|
+
individual sources such that it can be used with gammapy.
|
|
24
|
+
|
|
25
|
+
Every Source in the Model will get its own Gammapy skymodel. The
|
|
26
|
+
evaluation happens via the astromodel definition.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self, model: Model, frame: str | None = None, convert_ps: bool = True
|
|
31
|
+
) -> None:
|
|
32
|
+
"""
|
|
33
|
+
:param model: the astromodel model
|
|
34
|
+
:type model: astromodels.core.model.Model
|
|
35
|
+
:param frame: geometry frame for gammapy, defaults to ICRS, optional
|
|
36
|
+
:type frame: str
|
|
37
|
+
:param convert_ps: convert PointSources to a PointSpatialModel, optional
|
|
38
|
+
:type convert_ps: bool
|
|
39
|
+
"""
|
|
40
|
+
assert isinstance(model, Model), (
|
|
41
|
+
"AstromodelConverter needs an astromodels Model"
|
|
42
|
+
)
|
|
43
|
+
self._astromodel_model = model
|
|
44
|
+
if frame is not None:
|
|
45
|
+
self._frame = frame
|
|
46
|
+
else:
|
|
47
|
+
log.info("No frame passed - will use ICRS")
|
|
48
|
+
self._frame = "icrs"
|
|
49
|
+
self._convert_ps = convert_ps
|
|
50
|
+
self._converted_sources = {}
|
|
51
|
+
self._gammapy_models = []
|
|
52
|
+
self._convert_extendend_sources()
|
|
53
|
+
self._convert_point_sources()
|
|
54
|
+
self._create_gammapy_models_list()
|
|
55
|
+
|
|
56
|
+
def _convert_extendend_sources(self) -> None:
|
|
57
|
+
"""Converts all extended sources into individual gammapy skymodels."""
|
|
58
|
+
for (
|
|
59
|
+
source_name,
|
|
60
|
+
source_instance,
|
|
61
|
+
) in self._astromodel_model.extended_sources.items():
|
|
62
|
+
self._converted_sources[source_name] = SourceConverter(
|
|
63
|
+
source_instance, frame=self._frame, converter=self
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def _convert_point_sources(self) -> None:
|
|
67
|
+
"""Converts all point sources into individual skymodels."""
|
|
68
|
+
for (
|
|
69
|
+
source_name,
|
|
70
|
+
source_instance,
|
|
71
|
+
) in self._astromodel_model.point_sources.items():
|
|
72
|
+
log.debug(f"Converting pointsource {source_name}")
|
|
73
|
+
self._converted_sources[source_name] = SourceConverter(
|
|
74
|
+
source_instance, converter=self, convert_spatial=self._convert_ps
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def _create_gammapy_models_list(self) -> None:
|
|
78
|
+
"""Creates a list with all SkyModels fro the converted sources."""
|
|
79
|
+
for source in self._converted_sources.values():
|
|
80
|
+
self._gammapy_models.append(source.skymodel)
|
|
81
|
+
|
|
82
|
+
def _update_parameters(self) -> None:
|
|
83
|
+
"""Update all the parameters in the SkyModels with the values from the
|
|
84
|
+
astromodels model using the underlying SourceConverters."""
|
|
85
|
+
for source in self._converted_sources.values():
|
|
86
|
+
source._update_parameters()
|
|
87
|
+
|
|
88
|
+
def update(self) -> None:
|
|
89
|
+
"""Update all parameters of all SkyModels witht the current values from the
|
|
90
|
+
astromodels model. Public method for _update_parameters"""
|
|
91
|
+
self._update_parameters()
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def gammapy_models(self) -> list[SkyModel]:
|
|
95
|
+
"""
|
|
96
|
+
Returns all the gammapy skymodels for that model.
|
|
97
|
+
|
|
98
|
+
:return: list with all the SkyModels
|
|
99
|
+
:rtype: list[SkyModel]
|
|
100
|
+
"""
|
|
101
|
+
return self._gammapy_models
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def model(self) -> Model:
|
|
105
|
+
"""
|
|
106
|
+
Return the corresponding astromodels model
|
|
107
|
+
|
|
108
|
+
:return: the astromodels model
|
|
109
|
+
:rtype: astromodels.core.model.Model
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
return self._astromodel_model
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def converted_sources(self) -> dict:
|
|
116
|
+
"""
|
|
117
|
+
Returns dictionary with all the converted sources.
|
|
118
|
+
|
|
119
|
+
:return: the converted sources dict
|
|
120
|
+
:rtype: dict
|
|
121
|
+
"""
|
|
122
|
+
return self._converted_sources
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class SourceConverter:
|
|
126
|
+
"""Takes a astromodels source and converts it to a SkyModel"""
|
|
127
|
+
|
|
128
|
+
def __init__(
|
|
129
|
+
self, source: Source, converter: AstromodelConverter, **kwargs
|
|
130
|
+
) -> None:
|
|
131
|
+
"""
|
|
132
|
+
Initialize the SourceConverter.
|
|
133
|
+
:param source: The astromodels source
|
|
134
|
+
:type source: astromodels.core.model.Model
|
|
135
|
+
:param converter: The used AstromodelConverter instance
|
|
136
|
+
:type converter: AstromodelConverter
|
|
137
|
+
:param kwargs: Additional keyword arguments, optional
|
|
138
|
+
:type kwrags: dict
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
self._conv_spatial = kwargs.get("convert_spatial", True)
|
|
142
|
+
self._converter = converter
|
|
143
|
+
self._frame = self._converter._frame
|
|
144
|
+
self._source = source
|
|
145
|
+
|
|
146
|
+
# init everything to None
|
|
147
|
+
self._spatial_model = None
|
|
148
|
+
self._spectral_model = None
|
|
149
|
+
self._temporal_model = None
|
|
150
|
+
self._gammapy_model = None
|
|
151
|
+
self._parameter_dict = None
|
|
152
|
+
|
|
153
|
+
# start with converting the spatial model
|
|
154
|
+
if self._conv_spatial:
|
|
155
|
+
log.debug("Converting the spatial Model")
|
|
156
|
+
self._convert_spatial_model()
|
|
157
|
+
|
|
158
|
+
log.debug("Converting the spectral Model")
|
|
159
|
+
# converting the spectral model
|
|
160
|
+
self._convert_spectral_model()
|
|
161
|
+
|
|
162
|
+
self._create_skymodel()
|
|
163
|
+
|
|
164
|
+
def _gather_mappings(self) -> None:
|
|
165
|
+
"""
|
|
166
|
+
Gather all the parameter mappings from astromodels to gammapy for the individual
|
|
167
|
+
spectral and spatial models
|
|
168
|
+
"""
|
|
169
|
+
self._mapping = None
|
|
170
|
+
self._mapping_free = None
|
|
171
|
+
for comp in [self._spectral_model, self._spatial_model]:
|
|
172
|
+
if comp is not None:
|
|
173
|
+
if self._mapping is None:
|
|
174
|
+
self._mapping = comp.mapping
|
|
175
|
+
else:
|
|
176
|
+
self._mapping.update(comp.mapping)
|
|
177
|
+
if self._mapping_free is None:
|
|
178
|
+
self._mapping_free = comp.mapping_free
|
|
179
|
+
else:
|
|
180
|
+
self._mapping_free.update(comp.mapping_free)
|
|
181
|
+
|
|
182
|
+
def _update_parameters(self) -> None:
|
|
183
|
+
"""Update the skymodel parameters during the sampling process using the
|
|
184
|
+
parameter dict."""
|
|
185
|
+
# update the parameter dict for this skymodel
|
|
186
|
+
for k, v in self._mapping_free.items():
|
|
187
|
+
self.skymodel.parameters[v].update_from_dict(
|
|
188
|
+
parameter_to_gammapy_dict(self._converter.model[k])
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
def _convert_spectral_model(self) -> None:
|
|
192
|
+
"""Convert the spectral model of the source."""
|
|
193
|
+
if len(self._source.components.keys()) > 1:
|
|
194
|
+
self._spectral_model = SpectralModelConverted(
|
|
195
|
+
[x.shape for x in self._source.components.values()],
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
else:
|
|
199
|
+
self._spectral_model = SpectralModelConverted(
|
|
200
|
+
self._source.components[
|
|
201
|
+
next(iter(self._source.components.keys()))
|
|
202
|
+
].shape,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
def _convert_spatial_model(self) -> None:
|
|
206
|
+
"""Convert the spatial model of the source."""
|
|
207
|
+
if isinstance(self._source, ExtendedSource):
|
|
208
|
+
ps = False
|
|
209
|
+
elif isinstance(self._source, PointSource):
|
|
210
|
+
position = self._source.position
|
|
211
|
+
ps = True
|
|
212
|
+
if ps:
|
|
213
|
+
self._spatial_model = PointSourceModelConverted(
|
|
214
|
+
sky_position=position,
|
|
215
|
+
frame=self._frame,
|
|
216
|
+
)
|
|
217
|
+
else:
|
|
218
|
+
self._spatial_model = SpatialModelConverted(
|
|
219
|
+
self._source.spatial_shape, frame=self._frame
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def _create_skymodel(self) -> None:
|
|
223
|
+
"""Create the skymodel instance out of the individual components."""
|
|
224
|
+
self._skymodel = SkyModel(
|
|
225
|
+
name=self._source.name,
|
|
226
|
+
spectral_model=self._spectral_model,
|
|
227
|
+
spatial_model=self._spatial_model,
|
|
228
|
+
temporal_model=self._temporal_model,
|
|
229
|
+
)
|
|
230
|
+
self._gather_mappings()
|
|
231
|
+
|
|
232
|
+
def update(self) -> None:
|
|
233
|
+
"""
|
|
234
|
+
This invokes the updating of all parameters of the converted model in the
|
|
235
|
+
gammapy SkyModels
|
|
236
|
+
"""
|
|
237
|
+
self._update_parameters()
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def skymodel(self) -> SkyModel:
|
|
241
|
+
"""Returns the Gammapy skymodel for this source.
|
|
242
|
+
|
|
243
|
+
:returns: the created gammapy SkyModel
|
|
244
|
+
:rtype: gammapy.modeling.models.SkyModel
|
|
245
|
+
|
|
246
|
+
"""
|
|
247
|
+
return self._skymodel
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def astromodels_source(self) -> Source:
|
|
251
|
+
"""Returns the original astromodel source.
|
|
252
|
+
|
|
253
|
+
:returns: the converted astromodels Source
|
|
254
|
+
:rtype: astromodels.sources.Source
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
return self._source
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class GammapyConverter: # pragma: no cover
|
|
261
|
+
"""Takes a gammapy SkyModel and transforms it into a astromodels Model."""
|
|
262
|
+
|
|
263
|
+
def __init__(self, model: SkyModel) -> None:
|
|
264
|
+
assert isinstance(model, SkyModel), "You must provide a gammapy.model.SkyModel"
|
|
265
|
+
self._sky_model = model
|
|
266
|
+
raise NotImplementedError("Sorry this is not yet a feature :(")
|
|
267
|
+
|
|
268
|
+
def _parse_sources(self):
|
|
269
|
+
spectral_model = self._sky_model.spectral_model # noqa: F841
|
|
270
|
+
spatial_model = self._sky_model.spatial_model # noqa: F841
|
|
271
|
+
|
|
272
|
+
def _create_astromodel(self):
|
|
273
|
+
# load the sources
|
|
274
|
+
# load the
|
|
275
|
+
sources = []
|
|
276
|
+
self._astromodel = Model(*sources)
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
import gammapy
|
|
6
|
+
import numpy as np
|
|
7
|
+
from astromodels.core.model import Model
|
|
8
|
+
from astromodels.functions.priors import Truncated_gaussian
|
|
9
|
+
from gammapy.datasets import Dataset, Datasets
|
|
10
|
+
from gammapy.modeling.models import DatasetModels, ModelBase, Models
|
|
11
|
+
from packaging.version import Version
|
|
12
|
+
from threeML.plugin_prototype import PluginPrototype
|
|
13
|
+
|
|
14
|
+
from gammapy_plugin.converter import AstromodelConverter
|
|
15
|
+
from gammapy_plugin.io.plot_model import plot_model
|
|
16
|
+
from gammapy_plugin.utils.gammapy_parser import (
|
|
17
|
+
parameter_to_gammapy_dict,
|
|
18
|
+
parse_gammapy_model,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from threeML.io.plotting.data_residual_plot import ResidualPlot
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ["GammapyLike"]
|
|
26
|
+
|
|
27
|
+
log = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
__instrument_name = "gammapy"
|
|
30
|
+
|
|
31
|
+
gammapy_version = Version(gammapy.__version__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class GammapyLike(PluginPrototype):
|
|
35
|
+
"""A plugin for including Gammapy datasets."""
|
|
36
|
+
|
|
37
|
+
def __new__(cls, *args, **kwargs) -> PluginPrototype:
|
|
38
|
+
instance = object.__new__(cls)
|
|
39
|
+
return instance
|
|
40
|
+
|
|
41
|
+
def __init__(self, name: str, **kwargs) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Initialize the GammapyLike plugin.
|
|
44
|
+
|
|
45
|
+
:param name: Name of the plugin
|
|
46
|
+
:type name: str
|
|
47
|
+
"""
|
|
48
|
+
nuisance_parameters = kwargs.get("nuisance_parameters", {})
|
|
49
|
+
super().__init__(name, nuisance_parameters=nuisance_parameters)
|
|
50
|
+
self._frame: str = kwargs.get("frame", "icrs")
|
|
51
|
+
self._sources: list[str] | None = kwargs.get("sources", None)
|
|
52
|
+
self._nuisance_mapping = {}
|
|
53
|
+
self._background_models: dict = kwargs.get("background_models", {})
|
|
54
|
+
self._background_models_mapper: dict = {}
|
|
55
|
+
self._nuisance_parameters_dicts: dict = {}
|
|
56
|
+
if len(self._background_models.keys()) > 0:
|
|
57
|
+
self._parse_background_models()
|
|
58
|
+
|
|
59
|
+
def set_datasets(
|
|
60
|
+
self,
|
|
61
|
+
datasets: Dataset | Datasets | list[Dataset] | Path | str,
|
|
62
|
+
mode: str = "individual",
|
|
63
|
+
stacked_name: str = "stacked",
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Set the Gammapy Dataset.
|
|
66
|
+
|
|
67
|
+
:param datasets: list of Gammapy datasets or a single Dataset object
|
|
68
|
+
:param mode: individual or stacked - defaults to individual, stacked
|
|
69
|
+
stacks the passed datasets
|
|
70
|
+
:param stacked_name: name of the stacked datasset if mode is stacked
|
|
71
|
+
"""
|
|
72
|
+
if mode not in [
|
|
73
|
+
"individual",
|
|
74
|
+
"stacked",
|
|
75
|
+
]:
|
|
76
|
+
raise ValueError("mode needs to be individual or stacked")
|
|
77
|
+
if isinstance(datasets, (str, Path)):
|
|
78
|
+
log.warning(
|
|
79
|
+
"You have provided a path to a file - we will assume this is a Datasets"
|
|
80
|
+
" file. If not this will likely fail"
|
|
81
|
+
)
|
|
82
|
+
datasets = Datasets.read(datasets)
|
|
83
|
+
self._datasets = datasets
|
|
84
|
+
if mode == "stacked":
|
|
85
|
+
self._datasets = Datasets(
|
|
86
|
+
self._datasets.stack_reduce(name=stacked_name)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if isinstance(datasets, list):
|
|
90
|
+
self._datasets = Datasets()
|
|
91
|
+
for d in datasets:
|
|
92
|
+
self._datasets.append(d)
|
|
93
|
+
if mode == "stacked":
|
|
94
|
+
self._datasets = Datasets(
|
|
95
|
+
self._datasets.stack_reduce(name=stacked_name)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
elif isinstance(datasets, Datasets):
|
|
99
|
+
self._datasets = datasets
|
|
100
|
+
if mode == "stacked":
|
|
101
|
+
self._datasets = Datasets(
|
|
102
|
+
self._datasets.stack_reduce(name=stacked_name)
|
|
103
|
+
)
|
|
104
|
+
elif isinstance(datasets, Dataset):
|
|
105
|
+
self._datasets = Datasets(datasets)
|
|
106
|
+
if mode == "stacked":
|
|
107
|
+
log.info("Only using a single dataset - can not stack that")
|
|
108
|
+
else:
|
|
109
|
+
msg = "datasets has to be list of Dataset,"
|
|
110
|
+
msg += " a single Datasets or Dataset object"
|
|
111
|
+
raise TypeError(msg)
|
|
112
|
+
|
|
113
|
+
def set_sources(self, sources: list[str] | str | None = None) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Set the sources to be used by this plugin - No need to specify bkg models
|
|
116
|
+
|
|
117
|
+
:param sources: Source(s) to be used in the analysis defaults to all
|
|
118
|
+
:type sources: list of str or str
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
if isinstance(sources, list):
|
|
122
|
+
self._sources = sources
|
|
123
|
+
elif isinstance(sources, str):
|
|
124
|
+
self._sources = [sources]
|
|
125
|
+
elif sources is None:
|
|
126
|
+
self._sources = None
|
|
127
|
+
else:
|
|
128
|
+
raise ValueError("")
|
|
129
|
+
|
|
130
|
+
def set_model(
|
|
131
|
+
self,
|
|
132
|
+
likelihood_model: Model,
|
|
133
|
+
converted_model: AstromodelConverter = None,
|
|
134
|
+
) -> None:
|
|
135
|
+
"""
|
|
136
|
+
Set the model to be used in the joint minimization.
|
|
137
|
+
|
|
138
|
+
:param likelihood_model: astromodels model
|
|
139
|
+
:param converted_model: converted astromodels
|
|
140
|
+
:type likelihood_model: Model
|
|
141
|
+
:type converted_model: AstromodelConverter
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
if self._sources is None:
|
|
145
|
+
log.debug(
|
|
146
|
+
"If you want to specify sources for this Plugin you MUST do so before"
|
|
147
|
+
)
|
|
148
|
+
else:
|
|
149
|
+
log.debug(f"Will use {self._sources} for this plugin")
|
|
150
|
+
|
|
151
|
+
self._likelihood_model: Model = likelihood_model
|
|
152
|
+
if converted_model is not None:
|
|
153
|
+
self._likelihood_model_converted: AstromodelConverter = converted_model
|
|
154
|
+
elif hasattr(self, "_likelihood_model_converted"):
|
|
155
|
+
pass
|
|
156
|
+
else:
|
|
157
|
+
self._likelihood_model_converted: AstromodelConverter = AstromodelConverter(
|
|
158
|
+
model=self._likelihood_model, frame=self._frame
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
self._update_gammapy_model_list()
|
|
162
|
+
self._assign_models()
|
|
163
|
+
self._set_get_log_like()
|
|
164
|
+
|
|
165
|
+
def _update_gammapy_model_list(self) -> Models:
|
|
166
|
+
"""
|
|
167
|
+
Update the list of gammapy models.
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
# this will only be run after setting the model
|
|
171
|
+
if hasattr(self, "_likelihood_model_converted"):
|
|
172
|
+
if self._sources is not None:
|
|
173
|
+
tmp2 = [
|
|
174
|
+
x
|
|
175
|
+
for x in self._likelihood_model_converted.gammapy_models
|
|
176
|
+
if x.name in self._sources
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
tmp = [*tmp2]
|
|
180
|
+
else:
|
|
181
|
+
tmp2 = [x for x in self._likelihood_model_converted.gammapy_models]
|
|
182
|
+
tmp = [*tmp2]
|
|
183
|
+
else:
|
|
184
|
+
tmp = []
|
|
185
|
+
tmp2 = []
|
|
186
|
+
self._global_models = Models(tmp2)
|
|
187
|
+
if hasattr(self, "_background_models"):
|
|
188
|
+
for m in list(self._background_models.values()):
|
|
189
|
+
tmp.append(m)
|
|
190
|
+
self._gammapy_model = Models(tmp)
|
|
191
|
+
|
|
192
|
+
def _assign_models(self):
|
|
193
|
+
"""
|
|
194
|
+
Assign the gammapy models to the datasets
|
|
195
|
+
"""
|
|
196
|
+
for d in self._datasets:
|
|
197
|
+
d.models = []
|
|
198
|
+
tmp = list(self._global_models).copy()
|
|
199
|
+
if d.name in self._background_models_mapper:
|
|
200
|
+
tmp.append(
|
|
201
|
+
self._background_models[self._background_models_mapper[d.name]]
|
|
202
|
+
)
|
|
203
|
+
d.models = Models(tmp)
|
|
204
|
+
|
|
205
|
+
def set_background_models(
|
|
206
|
+
self, bkg_model: ModelBase | list | Models | DatasetModels
|
|
207
|
+
) -> None:
|
|
208
|
+
"""
|
|
209
|
+
Set the gammapy background models (e.g. FoVBackgroundModel)
|
|
210
|
+
|
|
211
|
+
:param bkg_model: Background model(s) :type bkg_model: ModelBase or list of
|
|
212
|
+
ModelBase or Models or DatasetModels.
|
|
213
|
+
"""
|
|
214
|
+
if isinstance(bkg_model, ModelBase):
|
|
215
|
+
bkg_model = [bkg_model]
|
|
216
|
+
else:
|
|
217
|
+
if not isinstance(bkg_model, (list, Models, DatasetModels)):
|
|
218
|
+
raise TypeError(
|
|
219
|
+
"either pass a singular gammapy model or a list of models"
|
|
220
|
+
)
|
|
221
|
+
for b in bkg_model:
|
|
222
|
+
self._background_models[b.name] = b
|
|
223
|
+
self._background_models_mapper[b.datasets_names[0]] = b.name
|
|
224
|
+
self._parse_background_models()
|
|
225
|
+
self._update_gammapy_model_list()
|
|
226
|
+
self._assign_models()
|
|
227
|
+
|
|
228
|
+
def _parse_background_models(self):
|
|
229
|
+
"""
|
|
230
|
+
Parse the background models and link the gammapy parameters to
|
|
231
|
+
nuissance parameters of this plugin and set the prior.
|
|
232
|
+
"""
|
|
233
|
+
# TODO: way of manually specifying the priors
|
|
234
|
+
for bkg in self._background_models.values():
|
|
235
|
+
bkg_paras = parse_gammapy_model(bkg, self._name)
|
|
236
|
+
for k, v in bkg_paras.items():
|
|
237
|
+
para_path = k.split(".")
|
|
238
|
+
self._nuisance_mapping[k] = para_path
|
|
239
|
+
self._nuisance_parameters[k] = v
|
|
240
|
+
if v.is_normalization and v.free:
|
|
241
|
+
self._nuisance_parameters[k].prior = Truncated_gaussian(
|
|
242
|
+
mu=1.0, sigma=0.1, lower_bound=0.2, upper_bound=1.8
|
|
243
|
+
)
|
|
244
|
+
self._nuisance_parameters_dicts[k] = parameter_to_gammapy_dict(v)
|
|
245
|
+
|
|
246
|
+
def _update_background_models(self):
|
|
247
|
+
"""
|
|
248
|
+
Update the background models with the current values of the nuisance
|
|
249
|
+
parameters.
|
|
250
|
+
"""
|
|
251
|
+
# TODO: rewrite this to directly update
|
|
252
|
+
for k, v in self._nuisance_parameters.items():
|
|
253
|
+
self._nuisance_parameters_dicts[k]["value"] = v.value
|
|
254
|
+
p = self._nuisance_mapping[k]
|
|
255
|
+
# TODO find an elegant way to not hardcode this
|
|
256
|
+
self._background_models[p[1]].parameters[p[2]].update_from_dict(
|
|
257
|
+
self._nuisance_parameters_dicts[k]
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
def _set_get_log_like(self):
|
|
261
|
+
if hasattr(self._datasets, "_stat_sum_likelihood"):
|
|
262
|
+
func = self._datasets._stat_sum_likelihood
|
|
263
|
+
elif hasattr(self._datasets, "stat_sum_likelihood"):
|
|
264
|
+
func = self._datasets.stat_sum_likelihood
|
|
265
|
+
else:
|
|
266
|
+
raise AttributeError(
|
|
267
|
+
"gammapy.Datasets has neither _stat_sum_likelihood nor "
|
|
268
|
+
"stat_sum_likelihood - something went fundamentally wrong!"
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
self.log_like_func = func
|
|
272
|
+
|
|
273
|
+
def get_log_like(self) -> float:
|
|
274
|
+
"""
|
|
275
|
+
Return the value of the log-likelihood with the current values for
|
|
276
|
+
the parameters stored in the model instance.
|
|
277
|
+
"""
|
|
278
|
+
self._likelihood_model_converted._update_parameters()
|
|
279
|
+
self._update_background_models()
|
|
280
|
+
return -0.5 * self.log_like_func()
|
|
281
|
+
|
|
282
|
+
def inner_fit(self):
|
|
283
|
+
return self.get_log_like()
|
|
284
|
+
|
|
285
|
+
def get_number_of_data_points(self) -> np.int64:
|
|
286
|
+
"""
|
|
287
|
+
Return the number of data points in the datasets.
|
|
288
|
+
"""
|
|
289
|
+
return np.sum([np.prod(d.counts.data.shape) for d in self._datasets])
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def datasets(self) -> Datasets:
|
|
293
|
+
"""Gammapy datasets of the plugin."""
|
|
294
|
+
return self._datasets
|
|
295
|
+
|
|
296
|
+
@property
|
|
297
|
+
def model(self) -> Model:
|
|
298
|
+
"""Astromodels model of the plugin."""
|
|
299
|
+
return self._likelihood_model
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def astromodel_converter(self) -> AstromodelConverter:
|
|
303
|
+
"""AstromodelConverter object used for this plugin."""
|
|
304
|
+
return self._likelihood_model_converted
|
|
305
|
+
|
|
306
|
+
@property
|
|
307
|
+
def gammapy_model(self) -> Models:
|
|
308
|
+
"""List of all the Gammapy SkyModels."""
|
|
309
|
+
|
|
310
|
+
if not hasattr(self, "_gammapy_model"):
|
|
311
|
+
self._update_gammapy_model_list()
|
|
312
|
+
return self._gammapy_model
|
|
313
|
+
|
|
314
|
+
@property
|
|
315
|
+
def frame(self) -> str:
|
|
316
|
+
"""Coordinate Frame of the plugin."""
|
|
317
|
+
return self._frame
|
|
318
|
+
|
|
319
|
+
def display_model(
|
|
320
|
+
self,
|
|
321
|
+
*args,
|
|
322
|
+
**kwargs,
|
|
323
|
+
) -> "ResidualPlot":
|
|
324
|
+
|
|
325
|
+
return plot_model(self._datasets, *args, **kwargs)
|
|
File without changes
|