hawc_hal.psf_fast.psf_wrapper   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 212
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 26
eloc 101
dl 0
loc 212
rs 10
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A PSFWrapper.to_pandas() 0 5 1
A PSFWrapper.xs() 0 6 1
A PSFWrapper.brightness() 0 3 1
A PSFWrapper.combine_with_other_psf() 0 22 3
A PSFWrapper._prepare_brightness_interpolation_points() 0 15 2
A PSFWrapper.ys() 0 6 1
A PSFWrapper.__init__() 0 38 2
A PSFWrapper.find_eef_radius() 0 9 2
A PSFWrapper.from_TF1() 0 22 3
A InvalidPSF.to_pandas() 0 5 1
A InvalidPSF.__deepcopy__() 0 2 1
A PSFWrapper.integral() 0 3 1
A InvalidPSF.__getattribute__() 0 8 2
A PSFWrapper.total_integral() 0 3 1
A PSFWrapper.truncation_radius() 0 3 1
A PSFWrapper.from_pandas() 0 18 2
A PSFWrapper.kernel_radius() 0 3 1
1
import numpy as np
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
import scipy.interpolate
3
import scipy.optimize
4
import pandas as pd
5
6
_INTEGRAL_OUTER_RADIUS = 15.0
7
8
9
class InvalidPSFError(ValueError):
0 ignored issues
show
Coding Style introduced by
This class 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...
10
11
    pass
12
13
14
class PSFWrapper(object):
0 ignored issues
show
Coding Style introduced by
This class 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...
best-practice introduced by
Too many instance attributes (10/7)
Loading history...
15
16
    def __init__(self, xs, ys, brightness_interp_x=None, brightness_interp_y=None):
17
18
        self._xs = xs
19
        self._ys = ys
20
21
        self._psf_interpolated = scipy.interpolate.InterpolatedUnivariateSpline(xs, ys, k=2,
22
                                                                                ext='raise',
23
                                                                                check_finite=True)
24
25
        # Memorize the total integral (will use it for normalization)
26
27
        self._total_integral = self._psf_interpolated.integral(self._xs[0], _INTEGRAL_OUTER_RADIUS)
28
29
        # Now compute the truncation radius, which is a very conservative measurement
30
        # of the size of the PSF
31
32
        self._truncation_radius = self.find_eef_radius(0.9999)
33
34
        # Let's also compute another measurement more appropriate for convolution
35
        self._kernel_radius = self.find_eef_radius(0.999)
36
37
        assert self._kernel_radius <= self._truncation_radius
38
        assert self._truncation_radius <= _INTEGRAL_OUTER_RADIUS
39
40
        # Prepare brightness interpolation
41
42
        if brightness_interp_x is None:
43
44
            brightness_interp_x, brightness_interp_y = self._prepare_brightness_interpolation_points()
45
46
        self._brightness_interp_x = brightness_interp_x
47
        self._brightness_interp_y = brightness_interp_y
48
49
        self._brightness_interpolation = scipy.interpolate.InterpolatedUnivariateSpline(brightness_interp_x,
50
                                                                                        brightness_interp_y,
51
                                                                                        k=2,
52
                                                                                        ext='extrapolate',
53
                                                                                        check_finite=True)
54
55
    def _prepare_brightness_interpolation_points(self):
56
57
        # Get the centers of the bins
58
        interp_x = (self._xs[1:] + self._xs[:-1]) / 2.0
59
60
        # Compute the density
61
62
        interp_y = np.array(map(lambda (a, b): self.integral(a, b) / (np.pi * (b ** 2 - a ** 2)) / self._total_integral,
0 ignored issues
show
introduced by
map/filter on lambda could be replaced by comprehension
Loading history...
63
                                zip(self._xs[:-1], self._xs[1:])))
64
65
        # Add zero at r = _INTEGRAL_OUTER_RADIUS so that the extrapolated values will be correct
66
        interp_x = np.append(interp_x, [_INTEGRAL_OUTER_RADIUS])
67
        interp_y = np.append(interp_y, [0.0])
68
69
        return interp_x, interp_y
70
71
    def find_eef_radius(self, fraction):
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...
72
73
        f = lambda r: fraction - self.integral(1e-4, r) / self._total_integral
0 ignored issues
show
Coding Style Naming introduced by
Variable name "f" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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...
74
75
        radius, status = scipy.optimize.brentq(f, 0.005, _INTEGRAL_OUTER_RADIUS, full_output = True)
0 ignored issues
show
Coding Style introduced by
No space allowed around keyword argument assignment
Loading history...
76
77
        assert status.converged, "Brentq did not converged"
78
79
        return radius
80
81
    def brightness(self, r):
0 ignored issues
show
Coding Style Naming introduced by
Argument name "r" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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 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...
82
83
        return self._brightness_interpolation(r)
84
85
    @property
86
    def xs(self):
0 ignored issues
show
Coding Style Naming introduced by
Attribute name "xs" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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...
87
        """
88
        X of the interpolation data
89
        """
90
        return self._xs
91
92
    @property
93
    def ys(self):
0 ignored issues
show
Coding Style Naming introduced by
Attribute name "ys" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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...
94
        """
95
        Y of the interpolation data
96
        """
97
        return self._ys
98
99
    def combine_with_other_psf(self, other_psf, w1, w2):
0 ignored issues
show
Coding Style Naming introduced by
Argument name "w1" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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
Argument name "w2" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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...
100
        """
101
        Return a PSF which is the linear interpolation between this one and the other one provided
102
103
        :param other_psf: another psf
104
        :param w1: weight for self (i.e., this PSF)
105
        :param w2: weight for the other psf
106
        :return: another PSF instance
107
        """
108
109
        if isinstance(self, InvalidPSF) or isinstance(other_psf, InvalidPSF):
110
            return InvalidPSF()
111
112
        # Weight the ys
113
        new_ys = w1 * self.ys + w2 * other_psf.ys
114
115
        # Also weight the brightness interpolation points
116
        new_br_interp_y = w1 * self._brightness_interp_y + w2 * other_psf._brightness_interp_y
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _brightness_interp_y was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
117
118
        return PSFWrapper(self.xs, new_ys,
119
                          brightness_interp_x=self._brightness_interp_x,
120
                          brightness_interp_y=new_br_interp_y)
121
122
    def to_pandas(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...
123
124
        items = (('xs', self._xs), ('ys', self._ys))
125
126
        return pd.DataFrame.from_dict(dict(items))
127
128
    @classmethod
129
    def from_pandas(cls, df):
0 ignored issues
show
Coding Style Naming introduced by
Argument name "df" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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 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...
130
131
        # Check for an invalid PSF
132
        xs = df.loc[:, 'xs'].values
0 ignored issues
show
Coding Style Naming introduced by
Variable name "xs" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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...
133
        ys = df.loc[:, 'ys'].values
0 ignored issues
show
Coding Style Naming introduced by
Variable name "ys" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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...
134
135
        if len(xs) == 0:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
Unused Code introduced by
Do not use len(SEQUENCE) to determine if a sequence is empty
Loading history...
136
137
            # Should never happen
138
            assert len(ys) == 0, "Corrupted response file? A PSF has 0 xs values but more than 0 ys values"
0 ignored issues
show
Unused Code introduced by
Do not use len(SEQUENCE) to determine if a sequence is empty
Loading history...
139
140
            # An invalid PSF
141
            return InvalidPSF()
142
143
        else:
144
145
            return cls(xs, ys)
146
147
    @classmethod
148
    def from_TF1(cls, tf1_instance):
0 ignored issues
show
Coding Style Naming introduced by
Method name "from_TF1" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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 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...
149
150
        # Annoyingly, some PSFs for some Dec bins (at large Zenith angles) have
151
        # zero or negative integrals, i.e., they are useless. Return an unusable PSF
152
        # object in that case
153
        if tf1_instance.Integral(0, _INTEGRAL_OUTER_RADIUS) <= 0.0:
154
155
            return InvalidPSF()
156
157
        # Make interpolation
158
        xs = np.logspace(-3, np.log10(_INTEGRAL_OUTER_RADIUS), 500)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "xs" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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...
159
        ys = np.array(map(lambda x:tf1_instance.Eval(x), xs), float)
0 ignored issues
show
Coding Style introduced by
Exactly one space required after :
Loading history...
Coding Style Naming introduced by
Variable name "ys" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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...
introduced by
map/filter on lambda could be replaced by comprehension
Loading history...
Unused Code introduced by
This lambda might be unnecessary.
Loading history...
160
161
        assert np.all(np.isfinite(xs))
162
        assert np.all(np.isfinite(ys))
163
164
        instance = cls(xs, ys)
165
166
        instance._tf1 = tf1_instance.Clone()
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _tf1 was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
Coding Style introduced by
The attribute _tf1 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...
167
168
        return instance
169
170
    def integral(self, a, b):
0 ignored issues
show
Coding Style Naming introduced by
Argument name "a" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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
Argument name "b" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

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 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...
171
172
        return self._psf_interpolated.integral(a, b)
173
174
    @property
175
    def truncation_radius(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...
176
        return self._truncation_radius
177
178
    @property
179
    def total_integral(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...
180
        return self._total_integral
181
182
    @property
183
    def kernel_radius(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...
184
        return self._kernel_radius
185
186
187
# This is a class that, whatever you try to use it for, will raise an exception.
188
# This is to make sure that we never use an invalid PSF without knowing it
189
class InvalidPSF(object):
0 ignored issues
show
Coding Style introduced by
This class 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...
190
191
    # It can be useful to copy an invalid PSF. For instance HAL.get_simulated_dataset() makes a
192
    # copy of the HAL instance, including detector response, which can contain InvalidPSF (which
193
    # is fine as long as they are not used).
194
    def __deepcopy__(self, memo):
195
        return InvalidPSF()
196
197
    # This allow the Invalid PSF to be saved in the HDF file
198
    def to_pandas(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...
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
199
200
        items = (('xs', []), ('ys', []))
201
202
        return pd.DataFrame.from_dict(dict(items))
203
204
    def __getattribute__(self, item):
205
206
        # White list of available attributes
207
        if item in ["__deepcopy__", "to_pandas"]:
208
209
            return object.__getattribute__(self, item)
210
211
        raise InvalidPSFError("Trying to use an invalid PSF")
212