hawc_hal.psf_fast.psf_interpolator   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 8
eloc 46
dl 0
loc 146
rs 10
c 0
b 0
f 0

1 Function

Rating   Name   Duplication   Size   Complexity  
A _divide_in_blocks() 0 12 1

3 Methods

Rating   Name   Duplication   Size   Complexity  
A PSFInterpolator.__init__() 0 8 1
A PSFInterpolator._get_point_source_image_aitoff() 0 86 2
A PSFInterpolator.point_source_image() 0 26 4
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 collections
0 ignored issues
show
introduced by
standard import "import collections" should be placed before "import numpy as np"
Loading history...
3
from hawc_hal import flat_sky_projection
4
from hawc_hal.sphere_dist import sphere_dist
5
import reproject
0 ignored issues
show
introduced by
third party import "import reproject" should be placed before "from hawc_hal import flat_sky_projection"
Loading history...
6
7
8
def _divide_in_blocks(arr, nrows, ncols):
9
    """
10
    Return an array of shape (n, nrows, ncols) where
11
    n * nrows * ncols = arr.size
12
13
    If arr is a 2D array, the returned array should look like n subblocks with
14
    each subblock preserving the "physical" layout of arr.
15
    """
16
    h, w = arr.shape
0 ignored issues
show
Coding Style Naming introduced by
Variable name "h" 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
Variable name "w" 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...
Unused Code introduced by
The variable w seems to be unused.
Loading history...
17
    return (arr.reshape(h // nrows, nrows, -1, ncols)
18
            .swapaxes(1, 2)
19
            .reshape(-1, nrows, ncols))
20
21
22
class PSFInterpolator(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...
23
24
    def __init__(self, psf_wrapper, flat_sky_proj):
25
26
        self._psf = psf_wrapper
27
28
        self._flat_sky_p = flat_sky_proj
29
30
        # This will contain cached source images
31
        self._point_source_images = collections.OrderedDict()
32
33
    def _get_point_source_image_aitoff(self, ra, dec, psf_integration_method):
0 ignored issues
show
Coding Style Naming introduced by
Argument name "ra" 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...
34
35
        # Get the density for the required (RA, Dec) points by interpolating the density profile
36
37
        # First we obtain an image with a flat sky projection centered exactly on the point source
38
        ancillary_image_pixel_size = 0.05
39
        pixel_side = 2 * int(np.ceil(self._psf.truncation_radius / ancillary_image_pixel_size))
40
        ancillary_flat_sky_proj = flat_sky_projection.FlatSkyProjection(ra, dec, ancillary_image_pixel_size,
41
                                                                        pixel_side, pixel_side)
42
43
        # Now we compute the angular distance of all pixels in the ancillary image with respect to the center
44
        angular_distances = sphere_dist(ra, dec, ancillary_flat_sky_proj.ras, ancillary_flat_sky_proj.decs)
45
46
        # Compute the brightness (i.e., the differential PSF)
47
        ancillary_brightness = self._psf.brightness(angular_distances).reshape((pixel_side, pixel_side)) * \
48
                               self._flat_sky_p.project_plane_pixel_area
49
50
        # Now reproject this brightness on the new image
51
        if psf_integration_method == 'exact':
52
53
            reprojection_method = reproject.reproject_exact
54
            additional_keywords = {'parallel': False}
55
56
        else:
57
58
            reprojection_method = reproject.reproject_interp
59
            additional_keywords = {}
60
61
        brightness, _ = reprojection_method((ancillary_brightness, ancillary_flat_sky_proj.wcs),
62
                                            self._flat_sky_p.wcs, shape_out=(self._flat_sky_p.npix_height,
63
                                                                             self._flat_sky_p.npix_width),
64
                                            **additional_keywords)
65
66
        brightness[np.isnan(brightness)] = 0.0
67
68
        # Now "integrate", i.e., multiply by pixel area
69
        point_source_img_ait = brightness
70
71
        # # First let's compute the core of the PSF, i.e., the central area with a radius of 0.5 deg,
72
        # # using a small pixel size
73
        #
74
        # # We use the oversampled version of the flat sky projection to make sure we compute an integral
75
        # # with the right pixelization
76
        #
77
        # oversampled = self._flat_sky_p.oversampled
78
        # oversample_factor = self._flat_sky_p.oversample_factor
79
        #
80
        # # Find bounding box for a faster selection
81
        #
82
        # angular_distances_core, core_idx = oversampled.get_spherical_distances_from(ra, dec, cutout_radius=5.0)
83
        #
84
        # core_densities = np.zeros(oversampled.ras.shape)
85
        #
86
        # core_densities[core_idx] = self._psf.brightness(angular_distances_core) * oversampled.project_plane_pixel_area
87
        #
88
        # # Now downsample by summing every "oversample_factor" pixels
89
        #
90
        # blocks = _divide_in_blocks(core_densities.reshape((oversampled.npix_height, oversampled.npix_width)),
91
        #                                oversample_factor,
92
        #                                oversample_factor)
93
        #
94
        # densities = blocks.flatten().reshape([-1, oversample_factor**2]).sum(axis=1)  # type: np.ndarray
95
        #
96
        # assert densities.shape[0] == (self._flat_sky_p.npix_height * self._flat_sky_p.npix_width)
97
        #
98
        # # Now that we have a "densities" array with the right (not oversampled) resolution,
99
        # # let's update the elements outside the core, which are still zero
100
        # angular_distances_, within_outer_radius = self._flat_sky_p.get_spherical_distances_from(ra, dec,
101
        #                                                                                         cutout_radius=10.0)
102
        #
103
        # # Make a vector with the same shape of "densities" (effectively a padding of what we just computed)
104
        # angular_distances = np.zeros_like(densities)
105
        # angular_distances[within_outer_radius] = angular_distances_
106
        #
107
        # to_be_updated = (densities == 0)
108
        # idx = (within_outer_radius & to_be_updated)
109
        # densities[idx] = self._psf.brightness(angular_distances[idx]) * self._flat_sky_p.project_plane_pixel_area
110
        #
111
        # # NOTE: we check that the point source image is properly normalized in the convolved point source class.
112
        # # There we know which source we are talking about and for which bin, so we can print a more helpful
113
        # # help message
114
        #
115
        # # Reshape to required shape
116
        # point_source_img_ait = densities.reshape((self._flat_sky_p.npix_height, self._flat_sky_p.npix_width)).T
117
118
        return point_source_img_ait
119
120
    def point_source_image(self, ra_src, dec_src, psf_integration_method='exact'):
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...
121
122
        # Make a unique key for this request
123
        key = (ra_src, dec_src, psf_integration_method)
124
125
        # If we already computed this image, return it, otherwise compute it from scratch
126
        if key in self._point_source_images:
127
128
            point_source_img_ait = self._point_source_images[key]
129
130
        else:
131
132
            point_source_img_ait = self._get_point_source_image_aitoff(ra_src, dec_src, psf_integration_method)
133
134
            # Store for future use
135
136
            self._point_source_images[key] = point_source_img_ait
137
138
            # Limit the size of the cache. If we have exceeded 20 images, we drop the oldest 10
139
            if len(self._point_source_images) > 20:
140
141
                while len(self._point_source_images) > 10:
142
                    # FIFO removal (the oldest calls are removed first)
143
                    self._point_source_images.popitem(last=False)
144
145
        return point_source_img_ait
146