Passed
Push — master ( 1dc21f...b7666a )
by J. Michael
01:17
created

polarpy.polarlike.PolarLike.remove_rebinning()   A

Complexity

Conditions 1

Size

Total Lines 8
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
import collections
0 ignored issues
show
Coding Style introduced by
This module should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
2
from contextlib import contextmanager
3
import matplotlib.pyplot as plt
0 ignored issues
show
introduced by
Unable to import 'matplotlib.pyplot'
Loading history...
4
import numpy as np
0 ignored issues
show
introduced by
Unable to import 'numpy'
Loading history...
5
6
from astromodels import Parameter, Uniform_prior
0 ignored issues
show
introduced by
Unable to import 'astromodels'
Loading history...
7
from threeML import PluginPrototype
0 ignored issues
show
introduced by
Unable to import 'threeML'
Loading history...
8
from threeML.io.plotting.step_plot import step_plot
0 ignored issues
show
introduced by
Unable to import 'threeML.io.plotting.step_plot'
Loading history...
9
from threeML.utils.binner import Rebinner
0 ignored issues
show
introduced by
Unable to import 'threeML.utils.binner'
Loading history...
10
from threeML.utils.polarization.binned_polarization import BinnedModulationCurve
0 ignored issues
show
introduced by
Unable to import 'threeML.utils.polarization.binned_polarization'
Loading history...
11
from threeML.utils.statistics.likelihood_functions import poisson_observed_poisson_background, \
0 ignored issues
show
introduced by
Unable to import 'threeML.utils.statistics.likelihood_functions'
Loading history...
12
    poisson_observed_gaussian_background
13
14
from polarpy.modulation_curve_file import ModulationCurveFile
15
from polarpy.polar_response import PolarResponse
16
17
18
class PolarLike(PluginPrototype):
0 ignored issues
show
best-practice introduced by
Too many instance attributes (21/7)
Loading history...
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
19
    """
20
    Preliminary POLAR polarization plugin
21
    """
22
23
    def __init__(self, name, observation, background, response, interval_number=None, verbose=False):
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (101/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
best-practice introduced by
Too many arguments (7/5)
Loading history...
24
        """
25
26
27
28
        :param interval_number:
29
        :param name:
30
        :param observation:
31
        :param background:
32
        :param response:
33
34
        :param verbose:
35
36
        """
37
38
        # if we pass a string, there may be multiple time intervals
39
        # saved so we must specify a time interval
40
41
        if isinstance(observation, str):
42
            assert interval_number is not None, 'must specify an interval number'
43
44
            # this is a file
45
            read_file = ModulationCurveFile.read(observation)
46
47
            # create the bmc
48
            observation = read_file.to_binned_modulation_curve(interval=interval_number)
49
50
        # the same applies for the background
51
        if isinstance(background, str):
52
            assert interval_number is not None, 'must specify an interval number'
53
54
            # this is a file
55
            read_file = ModulationCurveFile.read(background)
56
57
            background = read_file.to_binned_modulation_curve(interval=interval_number)
58
59
        assert isinstance(observation, BinnedModulationCurve), 'The observation must be a BinnedModulationCurve'
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (112/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
60
        assert isinstance(background, BinnedModulationCurve), 'The observation must be a BinnedModulationCurve'
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (111/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
61
62
        # attach the required variables
63
64
        self._observation = observation
65
        self._background = background
66
67
        self._observed_counts = observation.counts
68
        self._background_counts = background.counts
69
        self._background_count_errors = background.count_errors
70
        self._scale = observation.exposure / background.exposure
71
        self._exposure = observation.exposure
72
        self._background_exposure = background.exposure
73
74
        self._likelihood_model = None
75
        self._rebinner = None
76
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
77
        # now do some double checks
78
79
        assert len(self._observed_counts) == len(self._background_counts)
80
81
        self._n_synthetic_datasets = 0
82
83
        # set up the effective area correction
84
85
        self._nuisance_parameter = Parameter(
86
            "cons_%s" % name,
87
            1.0,
88
            min_value=0.8,
89
            max_value=1.2,
90
            delta=0.05,
91
            free=False,
92
            desc="Effective area correction for %s" % name)
93
94
        nuisance_parameters = collections.OrderedDict()
95
        nuisance_parameters[self._nuisance_parameter.name] = self._nuisance_parameter
96
97
        # pass to the plugin proto
98
99
        super(PolarLike, self).__init__(name, nuisance_parameters)
100
101
102
        # The following vectors are the ones that will be really used for the computation. At the beginning they just
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (117/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
103
        # point to the original ones, but if a rebinner is used and/or a mask is created through set_active_measurements,
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (121/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
104
        # they will contain the rebinned and/or masked versions
105
106
        self._current_observed_counts = self._observed_counts
107
        self._current_background_counts = self._background_counts
108
        self._current_background_count_errors = self._background_count_errors
109
110
111
        self._verbose = verbose
112
113
        # we can either attach or build a response
114
115
        assert isinstance(response, str) or isinstance(
0 ignored issues
show
Unused Code introduced by
Consider merging these isinstance calls to isinstance(response, (PolarResponse, str))
Loading history...
116
            response, PolarResponse), 'The response must be a file name or a PolarResponse'
117
118
        if isinstance(response, PolarResponse):
119
120
            self._response = response
121
122
        else:
123
124
            self._response = PolarResponse(response)
125
126
        # attach the interpolators to the
127
128
        self._all_interp = self._response.interpolators
129
130
        # we also make sure the lengths match up here
131
        assert self._response.n_scattering_bins == len(
132
            self._observation.counts), 'observation counts shape does not agree with response shape'
133
134
    def use_effective_area_correction(self, lower=0.5, upper=1.5):
135
        """
136
        Use an area constant to correct for response issues
137
138
        :param lower:
139
        :param upper:
140
        :return:
141
        """
142
143
        self._nuisance_parameter.free = True
144
        self._nuisance_parameter.bounds = (lower, upper)
145
        self._nuisance_parameter.prior = Uniform_prior(lower_bound=lower, upper_bound=upper)
146
        if self._verbose:
147
            print('Using effective area correction')
148
149
    def fix_effective_area_correction(self, value=1):
150
        """
151
152
        fix the effective area correction to a particular values
153
154
        :param value:
155
        :return:
156
        """
157
158
        # allow the value to be outside the bounds
159
        if self._nuisance_parameter.max_value < value:
160
161
            self._nuisance_parameter.max_value = value + 0.1
162
163
        elif self._nuisance_parameter.min_value > value:
164
165
            self._nuisance_parameter.min_value = value = 0.1
166
167
        self._nuisance_parameter.fix = True
168
        self._nuisance_parameter.value = value
169
170
        if self._verbose:
171
            print('Fixing effective area correction')
172
173
    @property
174
    def effective_area_correction(self):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
175
176
        return self._nuisance_parameter
177
178
    def get_simulated_dataset(self, new_name=None, **kwargs):
0 ignored issues
show
Unused Code introduced by
The argument kwargs seems to be unused.
Loading history...
179
        """
180
        Returns another Binned instance where data have been obtained by randomizing the current expectation from the
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (117/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
181
        model, as well as from the background (depending on the respective noise models)
182
183
        :return: an BinnedSpectrum or child instance
184
        """
185
186
        assert self._likelihood_model is not None, "You need to set up a model before randomizing"
187
188
        # Keep track of how many syntethic datasets we have generated
189
190
        self._n_synthetic_datasets += 1
191
192
        # Generate a name for the new dataset if needed
193
        if new_name is None:
194
            new_name = "%s_sim_%i" % (self.name, self._n_synthetic_datasets)
195
196
        # Generate randomized data depending on the different noise models
197
198
        # We remove the mask temporarily because we need the various elements for all channels. We will restore it
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (114/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
199
        # at the end
200
201
        # Get the source model for all channels (that's why we don't use the .folded_model property)
202
203
        source_model_counts = self._get_model_counts()
204
205
        if self._background.is_poisson:
206
            _, background_model_counts = poisson_observed_poisson_background(
207
                        self._observed_counts, self._background_counts, self._scale, source_model_counts)
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 8 spaces).
Loading history...
Coding Style introduced by
This line is too long as per the coding-style (105/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
208
        else:
209
210
            _, background_model_counts = poisson_observed_gaussian_background(
211
                        self._observed_counts, self._background_counts, self._background.count_errors, source_model_counts)
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation (remove 8 spaces).
Loading history...
Coding Style introduced by
This line is too long as per the coding-style (123/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
212
213
        # Now randomize the expectations
214
215
        # Randomize expectations for the source
216
217
        randomized_source_counts = np.random.poisson(source_model_counts + background_model_counts)
218
219
        randomized_background_counts = np.random.poisson(background_model_counts)
220
221
        new_observation = self._observation.clone(new_counts=randomized_source_counts)
222
223
        new_background = self._background.clone(new_counts=randomized_background_counts)
224
225
        new_plugin = PolarLike(
226
            name=new_name,
227
            observation=new_observation,
228
            background=new_background,
229
            response=self._response,
230
            verbose=False,
231
        )
232
233
        return new_plugin
234
235
    def set_model(self, likelihood_model_instance):
236
        """
237
        Set the model to be used in the joint minimization. Must be a LikelihoodModel instance.
238
        :param likelihood_model_instance: instance of Model
239
        :type likelihood_model_instance: astromodels.Model
240
        """
241
242
        if likelihood_model_instance is None:
243
            return
244
245
        # if self._source_name is not None:
246
247
        #     # Make sure that the source is in the model
248
        #     assert self._source_name in likelihood_model_instance.sources, \
249
        #                                         "This XYLike plugin refers to the source %s, " \
250
        #                                         "but that source is not in the likelihood model" % (self._source_name)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (120/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
251
252
        for k, v in likelihood_model_instance.free_parameters.items():
0 ignored issues
show
Coding Style Naming introduced by
The name v does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
253
254
            if 'polarization.degree' in k:
255
                self._pol_degree = v
0 ignored issues
show
Coding Style introduced by
The attribute _pol_degree was defined outside __init__.

It is generally a good practice to initialize all attributes to default values in the __init__ method:

class Foo:
    def __init__(self, x=None):
        self.x = x
Loading history...
256
257
            if 'polarization.angle' in k:
258
                self._pol_angle = v
0 ignored issues
show
Coding Style introduced by
The attribute _pol_angle was defined outside __init__.

It is generally a good practice to initialize all attributes to default values in the __init__ method:

class Foo:
    def __init__(self, x=None):
        self.x = x
Loading history...
259
260
        # now we need to get the intergal flux
261
262
        _, integral = self._get_diff_flux_and_integral(likelihood_model_instance)
263
264
        self._integral_flux = integral
0 ignored issues
show
Coding Style introduced by
The attribute _integral_flux was defined outside __init__.

It is generally a good practice to initialize all attributes to default values in the __init__ method:

class Foo:
    def __init__(self, x=None):
        self.x = x
Loading history...
265
266
        self._likelihood_model = likelihood_model_instance
267
268
    def _get_diff_flux_and_integral(self, likelihood_model):
269
270
        n_point_sources = likelihood_model.get_number_of_point_sources()
271
272
        # Make a function which will stack all point sources (OGIP do not support spatial dimension)
273
274
        def differential_flux(scattering_edges):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
275
            fluxes = likelihood_model.get_point_source_fluxes(0, scattering_edges, tag=self._tag)
276
277
            # If we have only one point source, this will never be executed
278
            for i in range(1, n_point_sources):
279
                fluxes += likelihood_model.get_point_source_fluxes(i, scattering_edges, tag=self._tag)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (102/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
280
281
            return fluxes
282
283
        # The following integrates the diffFlux function using Simpson's rule
284
        # This assume that the intervals e1,e2 are all small, which is guaranteed
285
        # for any reasonable response matrix, given that e1 and e2 are Monte-Carlo
286
        # scattering_edges. It also assumes that the function is smooth in the interval
287
        # e1 - e2 and twice-differentiable, again reasonable on small intervals for
288
        # decent models. It might fail for models with too sharp features, smaller
289
        # than the size of the monte carlo interval.
290
291
        def integral(e1, e2):
0 ignored issues
show
Coding Style Naming introduced by
The name e1 does not conform to the argument naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name e2 does not conform to the argument naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
292
            # Simpson's rule
293
294
            return (e2 - e1) / 6.0 * (differential_flux(e1) + 4 * differential_flux(
295
                (e1 + e2) / 2.0) + differential_flux(e2))
296
297
        return differential_flux, integral
298
299
    def _get_model_rate(self):
300
301
        # first we need to get the integrated expectation from the spectrum
302
303
        intergal_spectrum = np.array(
304
            [self._integral_flux(emin, emax) for emin, emax in zip(self._response.ene_lo, self._response.ene_hi)])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (114/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
305
306
        # we evaluate at the center of the bin. the bin widths are already included
307
        eval_points = np.array(
308
            [[ene, self._pol_angle.value, self._pol_degree.value] for ene in self._response.energy_mid])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (104/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
309
310
        expectation = []
311
312
        # create the model counts by summing over energy
313
314
        for i, interpolator in enumerate(self._all_interp):
0 ignored issues
show
Unused Code introduced by
The variable i seems to be unused.
Loading history...
315
            rate = np.dot(interpolator(eval_points), intergal_spectrum)
316
317
            expectation.append(rate)
318
319
        return np.array(expectation)
320
321
    def _get_model_counts(self):
322
323
324
        if self._rebinner is None:
325
            model_rate = self._get_model_rate()
326
327
        else:
328
329
            model_rate, = self._rebinner.rebin(self._get_model_rate)
330
331
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
332
        return self._nuisance_parameter.value * self._exposure * model_rate
333
334
    def get_log_like(self):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
335
336
        model_counts = self._get_model_counts()
337
338
        if self._background.is_poisson:
339
340
            loglike, bkg_model = poisson_observed_poisson_background(        self._current_observed_counts, self._current_background_counts,
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (140/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
Coding Style introduced by
No space allowed after bracket
Loading history...
Unused Code introduced by
The variable bkg_model seems to be unused.
Loading history...
341
                                                                     self._scale, model_counts)
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (add 8 spaces).
Loading history...
342
343
        else:
344
345
            loglike, bkg_model = poisson_observed_gaussian_background(        self._current_observed_counts, self._current_background_counts,
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (141/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
Coding Style introduced by
No space allowed after bracket
Loading history...
346
                                                                      self._current_background_count_errors, model_counts)
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (add 8 spaces).
Loading history...
Coding Style introduced by
This line is too long as per the coding-style (122/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
347
348
        return np.sum(loglike)
349
350
    def inner_fit(self):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
351
352
        return self.get_log_like()
353
354
    def writeto(self, file_name):
355
        """
356
        Write the data to HDF5 modulation curve files. Both background and observation
357
        files are created
358
        :param file_name: the file name header. The .h5 extension is added automatically
359
        """
360
        # first create a file container
361
        observation_file = ModulationCurveFile.from_binned_modulation_curve(self._observation)
362
363
        background_file = ModulationCurveFile.from_binned_modulation_curve(self._background)
364
365
        observation_file.writeto("%s.h5" % file_name)
366
367
        background_file.writeto("%s_bak.h5" % file_name)
368
369
370
371
    @property
372
    def scattering_boundaries(self):
373
        """
374
        Energy boundaries of channels currently in use (rebinned, if a rebinner is active)
375
376
        :return: (sa_min, sa_max)
377
        """
378
379
        scattering_edges = np.array(self._observation.edges)
380
381
        sa_min, sa_max = scattering_edges[:-1], scattering_edges[1:]
382
383
        if self._rebinner is not None:
384
            # Get the rebinned chans. NOTE: these are already masked
385
386
            sa_min, sa_max = self._rebinner.get_new_start_and_stop(sa_min, sa_max)
387
388
 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
389
        return sa_min, sa_max
390
391
392
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
393
    def display(self, ax=None, show_data=True, show_model=True, show_total=False, model_kwargs={}, data_kwargs={}):
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (115/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
Bug Best Practice introduced by
The default value {} might cause unintended side-effects.

Objects as default values are only created once in Python and not on each invocation of the function. If the default object is modified, this modification is carried over to the next invocation of the method.

# Bad:
# If array_param is modified inside the function, the next invocation will
# receive the modified object.
def some_function(array_param=[]):
    # ...

# Better: Create an array on each invocation
def some_function(array_param=None):
    array_param = array_param or []
    # ...
Loading history...
Coding Style Naming introduced by
The name ax does not conform to the argument naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
best-practice introduced by
Too many arguments (7/5)
Loading history...
Comprehensibility introduced by
This function exceeds the maximum number of variables (17/15).
Loading history...
394
        """
395
396
        :param ax:
397
        :param show_data:
398
        :param show_model:
399
        :param show_total:
400
        :param model_kwargs:
401
        :param data_kwargs:
402
        :return:
403
        """
404
405
        sa_min, sa_max = self.scattering_boundaries
406
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
407
        if show_total:
408
            show_model = False
409
            show_data = False
410
411
        if ax is None:
412
413
            fig, ax = plt.subplots()
414
415
        else:
416
417
            fig = ax.get_figure()
418
419
        if show_total:
420
421
            total_rate =         self._current_observed_counts / self._exposure
0 ignored issues
show
Coding Style introduced by
Exactly one space required after assignment
Loading history...
422
            bkg_rate = self._current_background_counts / self._background_exposure
423
424
            total_errors = np.sqrt(total_rate)
425
426
            if self._background.is_poisson:
427
428
                bkg_errors = np.sqrt(bkg_rate)
429
430
            else:
431
432
                bkg_errors = self._current_background_count_errors
433
434
            ax.hlines(
435
                total_rate,
436
                sa_min,
437
                sa_max,
438
                color='#7D0505',
439
                **data_kwargs)
440
            ax.vlines(
441
                np.mean([self.scattering_boundaries],axis=1),
0 ignored issues
show
Coding Style introduced by
Exactly one space required after comma
Loading history...
442
                total_rate - total_errors,
443
                total_rate + total_errors,
444
                color='#7D0505',
445
                **data_kwargs)
446
447
            ax.hlines(
448
                bkg_rate,
449
                sa_min,
450
                sa_max,
451
                color='#0D5BAE',
452
                **data_kwargs)
453
            ax.vlines(
454
                np.mean([self.scattering_boundaries],axis=1),
0 ignored issues
show
Coding Style introduced by
Exactly one space required after comma
Loading history...
455
                bkg_rate - bkg_errors,
456
                bkg_rate + bkg_errors,
457
                color='#0D5BAE',
458
                **data_kwargs)
459
460
        if show_data:
461
462
            net_rate = (        self._observed_counts / self._exposure) - self._background_counts / self._background_exposure
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (125/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
Coding Style introduced by
No space allowed after bracket
Loading history...
463
464
            if self._background.is_poisson:
465
466
                errors = np.sqrt((        self._observed_counts / self._exposure) +
0 ignored issues
show
Coding Style introduced by
No space allowed after bracket
Loading history...
467
                                 (self._background_counts / self._background_exposure))
468
469
            else:
470
471
                errors = np.sqrt((        self._observed_counts / self._exposure) +
0 ignored issues
show
Coding Style introduced by
No space allowed after bracket
Loading history...
472
                                 (self._background.count_errors / self._background_exposure)**2)
473
474
            ax.hlines(net_rate, self._response.scattering_bins_lo, self._response.scattering_bins_hi, **data_kwargs)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (116/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
475
            ax.vlines(self._response.scattering_bins, net_rate - errors, net_rate + errors, **data_kwargs)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (106/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
476
477
        if show_model:
478
            step_plot(
479
                ax=ax,
480
                xbins=np.vstack([self._response.scattering_bins_lo, self._response.scattering_bins_hi]).T,
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (106/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
481
                y=self._get_model_counts() / self._exposure,
482
                **model_kwargs)
483
484
        ax.set_xlabel('Scattering Angle')
485
        ax.set_ylabel('Net Rate (cnt/s/bin)')
486
487
        return fig
488
489
    @property
490
    def observation(self):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
491
        return self._observation
492
493
    @property
494
    def background(self):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
495
        return self._background
496
497
    @contextmanager
498
    def _without__rebinner(self):
499
500
        # Store rebinner for later use
501
502
        rebinner = self._rebinner
503
504
        # Clean mask and rebinning
505
506
        self.remove_rebinning()
507
508
509
        # Execute whathever
510
511
        yield
512
513
        # Restore mask and rebinner (if any)
514
515
516
517
        if rebinner is not None:
518
519
            # There was a rebinner, use it. Note that the rebinner applies the mask by itself
520
521
            self._apply_rebinner(rebinner)
522
523
524
525
526
            
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
527
    def rebin_on_background(self, min_number_of_counts):
528
        """
529
        Rebin the spectrum guaranteeing the provided minimum number of counts in each background bin. This is usually
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (117/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
530
        required for spectra with very few background counts to make the Poisson profile likelihood meaningful.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (111/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
531
        Of course this is not relevant if you treat the background as ideal, nor if the background spectrum has
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (111/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
532
        Gaussian errors.
533
534
        The observed spectrum will be rebinned in the same fashion as the background spectrum.
535
536
        To neutralize this completely, use "remove_rebinning"
537
538
        :param min_number_of_counts: the minimum number of counts in each bin
539
        :return: none
540
        """
541
542
        # NOTE: the rebinner takes care of the mask already
543
544
        assert self._background is not None, "This data has no background, cannot rebin on background!"
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (103/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
545
546
        rebinner = Rebinner(self._background_counts, min_number_of_counts, mask = None)
0 ignored issues
show
Coding Style introduced by
No space allowed around keyword argument assignment
Loading history...
547
548
        self._apply_rebinner(rebinner)
549
550
    def rebin_on_source(self, min_number_of_counts):
551
        """
552
        Rebin the spectrum guaranteeing the provided minimum number of counts in each source bin.
553
554
        To neutralize this completely, use "remove_rebinning"
555
556
        :param min_number_of_counts: the minimum number of counts in each bin
557
        :return: none
558
        """
559
560
        # NOTE: the rebinner takes care of the mask already
561
562
563
564
        rebinner = Rebinner(self._observed_counts, min_number_of_counts, self._mask)
565
566
        self._apply_rebinner(rebinner)
567
568
    def _apply_rebinner(self, rebinner):
569
570
        self._rebinner = rebinner
571
572
        # Apply the rebinning to everything.
573
        # NOTE: the output of the .rebin method are the vectors with the mask *already applied*
574
575
        self._current_observed_counts, = self._rebinner.rebin(self._observed_counts)
576
577
        if self._background is not None:
578
579
            self._current_background_counts, = self._rebinner.rebin(self._background_counts)
580
581
            if self._background_count_errors is not None:
582
                # NOTE: the output of the .rebin method are the vectors with the mask *already applied*
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (103/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
583
584
                self._current_background_count_errors, = self._rebinner.rebin_errors(self._background_count_errors)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (115/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
585
586
        if self._verbose:
587
            print("Now using %s bins" % self._rebinner.n_bins)
588
589
    def remove_rebinning(self):
590
        """
591
        Remove the rebinning scheme set with rebin_on_background.
592
593
        :return:
594
        """
595
596
        self._rebinner = None
597